Styling Drupal blocks with CSS custom properties
CSS Custom Properties, more commonly known as CSS variables, have reshaped the way front-end developers approach theming in content management systems. Drupal's block system, which lets administrators place pieces of content in designated regions, benefits enormously from this approach because the same markup can look completely different based on the surrounding context.
In Australia, where teams in Sydney, Melbourne, and Brisbane often inherit complex multilingual sites from agencies across the Pacific, having a single source of truth for block styles reduces maintenance overhead. This article walks through practical techniques for wiring CSS custom properties into your Drupal theme, configuring them per block, and applying accessibility-conscious defaults that meet local expectations.
Why CSS variables work well for Drupal blocks
A Drupal block is essentially a renderable unit wrapped in a container with classes generated by the system and the surrounding template. Because the markup is predictable, you can target each block using its plugin ID, its region, or its visibility conditions. CSS Custom Properties slot perfectly into this picture, allowing a single set of declarations to cascade into hundreds of visual combinations without writing dozens of separate selectors.
Traditional approaches using preprocess functions in PHP or SCSS variables often require recompiling the theme whenever a colour or spacing token changes. With custom properties, a site builder can override values through the Drupal UI or via a stylesheet loaded conditionally per view mode, which is far more agile.
Picking the right approach for the job
| Approach | Where logic lives | Recompile needed | Runtime flexibility | Best fit |
|---|---|---|---|---|
| SCSS variables | build step | yes | none | large static designs |
preprocess hooks in .theme |
PHP layer | usually no | moderate | layout markup |
| CSS custom properties | browser + stylesheet | no | high | blocks with states |
| Layout Builder styles | config + CSS | no | high | per-section theming |
Custom properties tend to shine when blocks have hover, focus, dark mode, or admin-driven variants, since the browser reads them at runtime rather than at compile time. Smaller teams in Adelaide and Perth, where in-house developer counts are often lean, benefit from this agility because style changes ship without a release cycle.
A blended strategy is common in practice: keep structural rules in SCSS, runtime variants in custom properties, and rare overrides in preprocess. Decide the split based on who needs to change each value and how often.
Declaring variables in your theme stylesheet
Open your theme's primary stylesheet (commonly css/style.css or the file generated from your source .scss) and add a :root declaration. This is where the default tokens live, and any block within the document tree can read them.
:root {
--block-bg: #ffffff;
--block-fg: #1a1a1a;
--block-border: #d8d8d8;
--block-radius: 6px;
--block-padding: 1.25rem;
--block-accent: #0066b3;
}
The values above are intentionally conservative, working well in a Sydney newsroom's editorial colour scheme or a Brisbane council's civic palette. Once declared, any block class such as .block--type-custom-block or .block-system-branding-block can consume them directly. Using a clear --block-* namespace prevents collisions with variables declared by contributed modules.
Australian teams often need to balance brand colours with WCAG contrast ratios, especially under the strong outdoor light conditions common in Perth and Adelaide. Storing colours as variables lets you create a quick high-contrast override without touching PHP or template files, which matters when meeting procurement requirements from local government clients.
Scoping variables to specific blocks
Drupal adds predictable classes to each block, including the plugin ID and the region. You can scope a variable to a specific block by writing a more specific selector. The following example targets the system branding block and adjusts its padding and accent.
.block-system-branding-block {
--block-padding: 1.75rem;
--block-accent: #c8102e;
--block-bg: #f7f7f7;
}
Scoped overrides like this are powerful because the rest of the cascade still falls back to the defaults declared on :root. You can do the same for views blocks, menu blocks, or custom block content. If you ship several subscription tiers for a client based in Melbourne's creative sector, you might use the same technique to differentiate each landing page block without duplicating markup.
For agencies that maintain many sites, building a small utility library that names variables consistently across projects pays off quickly. A shared --block-* namespace makes it easier to onboard a new developer who has worked on a sibling site in Canberra or Hobart, since the mental model transfers between projects.
Injecting variables through preprocess hooks
Sometimes you need a variable that depends on data only available at render time, such as the node type being displayed. In those cases, add a preprocess hook in your theme's .theme file and emit the variable as an inline style on the block wrapper. Drupal 9 and 10 both support this through the template_preprocess_block hook.
function mytheme_preprocess_block(&$variables) {
if ($variables['plugin_id'] === 'views_block:articles_recent_block_1') {
$variables['attributes']['style'] = '--block-accent: #0066b3; --block-radius: 12px;';
}
}
The example above adds a custom property override directly on the block's outer wrapper. Because the property is scoped to that element, it does not leak into other blocks in the same region. Australian publishers who run election-night live blogs in Melbourne and Sydney often use this technique to colour-code state-by-state updates without writing multiple templates.
You can also pull values from a configuration form so administrators select an accent colour per block instance. Storing the chosen value as a custom property keeps presentation logic in CSS while configuration stays in the Drupal admin layer, which is the cleanest separation of concerns.
Editor-controlled styling from the block UI
Drupal 10's Single Directory Components feature and contributed modules such as Block Styles or Layout Builder Styles give editors a way to add a class or a data attribute to a block from the layout screen. Combine that with a CSS rule and you have a no-code path for tweaking visual output.
A typical pattern is to expose a few block-level style options such as bordered, flat, and accent, and map each to a CSS class. The class then triggers a different set of custom properties.
.block--style-accent {
--block-bg: var(--color-brand-soft);
--block-accent: var(--color-brand-strong);
--block-padding: 1.5rem;
}
.block--style-flat {
--block-border: transparent;
--block-bg: transparent;
--block-padding: 0;
}
Site editors in Brisbane local councils and Hobart tourism boards, who often manage content without developer support, can choose between these variants without ever touching a stylesheet. The custom properties propagate to nested elements that read them, so a button inside an "accent" block inherits the right colour automatically. For Australian audiences accustomed to rapidly changing public health messaging, this flexibility means updates ship in minutes rather than days.
Performance, accessibility and Australian compliance
Custom properties are inexpensive at runtime, but a stylesheet full of hundreds of overrides can bloat the cascade. Keep your variable namespace tight and reuse values rather than restating them. When you generate per-view-mode overrides through preprocess, consider caching them via Drupal's render cache so they are not recomputed on every request.
Accessibility deserves special attention in Australia. The Disability Discrimination Act 1992 and the accompanying Australian Human Rights Commission guidance expect public-facing websites to meet Web Content Accessibility Guidelines at level AA. Variables make this easier because you can declare a high-contrast variant at the document level and switch to it via prefers-color-scheme or a manual toggle.
:root {
--block-bg: #ffffff;
--block-fg: #1a1a1a;
}
[data-theme="dark"] {
--block-bg: #111111;
--block-fg: #f4f4f4;
}
@media (prefers-color-scheme: dark) {
:root {
--block-bg: #111111;
--block-fg: #f4f4f4;
}
}
The snippet above gives users in Adelaide and Darwin, where screen glare and indoor lighting vary dramatically, a more readable experience. Always test with the contrast tools recommended by the Australian Government Digital Transformation Agency, and remember that custom properties support color-mix() and calc(), so you can derive hover and focus variants from a single base colour.
If you operate a site that collects personal information through forms placed inside blocks, the Australian Privacy Principles under the Privacy Act 1988 require clear notice about data handling. A styled consent block with strong visual hierarchy makes that notice more discoverable, and custom properties help you keep the styling consistent across consent, error, and confirmation states throughout the user journey.