Using CSS Custom Properties for better UX in Frontend and Gutenberg

If you are a WordPress theme developer, chances are high that you’re working with the Customizer to provide certain design options to your themes’ users – whether it is for colors, fonts, spacing or else. Providing customization opportunities is crucial for a theme’s adoption as you will never be able to please every user with your theme’s out-of-the-box design. As widely known, most WordPress users look for feature-rich solutions when assembling their site. They may find your theme’s base design very appealing, but if they cannot modify it to e.g. use their brand colors, they’ll quickly move on to something else. Long story short: Allowing users to customize your theme is crucial for adoption.

Now there’s an issue with that though: Most of these design options affect the site’s CSS, and CSS is a largely static resource. Therefore the typical approach to integrate these Customizer controls with the theme’s actual appearance is to copy the pieces of CSS that would be affected by the respective options from the stylesheet into a PHP file, and from there generate that stylesheet on the fly. This results in three major problems:

  1. You are most likely sending a whole bunch of duplicate CSS down the line, negatively affecting performance of the site.
  2. Because you are duplicating CSS, the approach is quite error-prone. For example when modifying CSS later, you need to remember to also make the exact same change in the Customizer CSS, if it applies there too.
  3. This also affects the WYSIWYG experience in the editor, as you also need to manually maintain the CSS for the editor (Gutenberg / classic) in the same way.

There is a solution to all of these problems though, and its name is “CSS Custom Properties”. In this post we will look at how to use this CSS feature to improve UX for both website visitors and content creators, all at once.

How bad is it, really?

The only one of these problems easily quantifiable is the first one, the negative impact on performance for a website using a theme with duplicate CSS generated to cover Customizer options (to which from now on I’ll simply refer to as “Customizer CSS”). Here’s two examples:

  • Twenty Nineteen sends an extra 5136 bytes (>5KB) of CSS if its color option is changed from the default. And all of that is only to be able to modify a single (!) primary color.
  • The upcoming Twenty Twenty does a bit of a better job, since it relies more on specific CSS classes for its color options, reducing the amount of CSS that needs to be duplicated. It also prints the extra CSS in an “almost minified” way, which is nice. Still, it sends an extra 4542 bytes (>4.4KB) of CSS down every user’s line when all color options are changed from their defaults.

Given that Twenty Twenty allows for a bit more of color customization (three options instead of one) but sends less extra CSS, it is already an improvement over Twenty Nineteen. Still, it is quite a bit of extra CSS for little benefit. Any theme that takes this approach further will most likely end up with way more extra CSS, as every Customizer design option will add to this. And you probably at least know or even have created themes that allow for more customization than core themes.

This also applies to maintaining the duplicate CSS: The more design options you provide, the more extra CSS you add, the harder it becomes to not miss something when modifying CSS later. Expanding the scope to Gutenberg, where usually some extra CSS rules are needed to cover for the editor UI quirks, it gets even more complex – and as soon as you miss something there, the WYSIWYG preview of your post content is no longer WYSIWYG.

What are CSS Custom Properties, and how can they fix this?

CSS Custom Properties (also called CSS Variables) are essentially variables in CSS. This may not sound new to you if you’ve been using preprocessors like Sass or LESS, where variables have existed for many years. However, the key difference is that CSS Custom Properties can be used in actual CSS, not just its preprocessors. That means you can reference a variable in multiple CSS rules, and you just need to change the value of the variable to make sure all rules relying on the variable are updated accordingly. Here’s how easy they are to use:

You simply declare a variable with its value. You can do that within any selector, so that the variable cascades down from that selector. Or, if you want to affect the entire document, you simply declare the variable in the pseudo-selector :root, like so:

:root {
    --primary-color: #123456;
}

Then, you just access the variable from wherever you need it. For example:

.button-primary {
    background-color: var(--primary-color);
}

.card-primary {
    border-color: var(--primary-color);
}

Maybe you already see where this is going. The great thing about CSS Custom Properties for our problems outlined earlier is that they can be dynamically modified easily. For example, let’s imagine the above CSS is loaded in a single stylesheet. If we now want to override the primary color without touching that file, we can simply inject a mini stylesheet into the page (following the original stylesheet) with the following:

:root {
    --primary-color: #654321; /* This is a much nicer color, right? */
}

And that’s it, all the rules from the original stylesheet that rely on the primary color now use our new primary color. Note that you can even set CSS Custom Properties with JavaScript, which could come in handy during Customizer preview for example.

So how do I put that into practice in a WordPress theme?

With the aforementioned approach applied to a WordPress theme, you would use CSS Custom Properties for every value that you want to provide a Customizer option for. You can then reduce the extra CSS rendered via PHP for the customizations to only setting the respective variable values, e.g. within the :root selector. Note that you can (and probably should) also think about generally relying on Custom Properties more in your CSS methodology, since it can also help with development, in the same way that variables do e.g. in Sass – with the key difference that for Custom Properties you don’t need a preprocessor.

Modifying an existing theme to rely on Custom Properties for the Customizer options is fairly trivial. You just need to find the respective values in your stylesheet (e.g. style.css), and replace each of them with a variable.

For example, to cover Twenty Nineteen’s frontend:

  • In style.css:
    • Replace #0073aa with e.g. var(--primary-color)
    • Replace #005177 with e.g. var(--primary-hover-color)
    • Replace #bfdcea with e.g. var(--primary-selection-color)
    • Add the default values for the Custom Properties, e.g. on top of the file: :root{--primary-color:#0073aa;--primary-hover-color:#005177;--primary-selection-color:#bfdcea;}
  • In inc/color-patterns.php:
    • Empty the value of the $theme_css variable.
    • Instead just fill that variable with the values for the three Custom Properties, similar to what you did in style.css before, but with the values from the Customizer options.

Or for Twenty Twenty’s frontend:

  • In style.css:
    • Replace #000 with e.g. var(--text-color)
    • Replace #cd2653 with e.g. var(--accent-color)
    • Replace #6d6d6d with e.g. var(--secondary-color)
    • Replace #dcd7ca with e.g. var(--border-color)
    • Replace #f5efe0 with e.g. var(--background-color)
    • Replace #fff with e.g. var(--header-footer-background-color)
    • Add the default values for the Custom Properties, e.g. on top of the file: :root{--text-color:#000;--accent-color:#cd2653;--secondary-color:#6d6d6d;--border-color:#dcd7ca;--background-color:#f5efe0;--header-footer-background-color:#fff;}
  • In inc/custom-css.php:
    • Empty the output generated in the if ( 'front-end' === $type ) { clause.
    • Instead just output the values for the Custom Properties, similar to what you did in style.css before, but with the values from the Customizer options.

The extra output will be literally just the definition of Custom Property values in :root, as seen in the examples above.

  • For Twenty Nineteen, the new output size for Customizer CSS is 95 bytes, which means a decrease of ~98.8% compared to the previous size.
  • For Twenty Twenty, the new output size for Customizer CSS is 162 bytes, which means a decrease of ~96.5% compared to the previous size. The decrease here is minimally less because, as mentioned earlier, Twenty Twenty already handles its extra Customizer CSS a bit better than Twenty Nineteen.

Okay, strictly, you also add the definitions to the original style.css file, so you can count the above bytes twice. That doesn’t matter too much though since it would still result in Customizer CSS reductions of ~96.4% for Twenty Nineteen and ~92.9% for Twenty Twenty respectively. As you can see, this means you require every user of a site relying on your theme to download a whole bunch less “redundant” CSS, positively impacting performance and thus also overall user experience, especially in circumstances where the network connection is not that great.

Also consider how many opportunities this opens up for more customization: With CSS Custom Properties, the possibilities are endless. You could for example just as easily replace hard-coded font-family definitions with Custom Properties in the stylesheet and then provide a Customizer control to pick a font to use. The current value could then be set in the same way as the colors addressed above. As mentioned in the beginning of this article, without Custom Properties maintenance becomes more complex for every Customizer design option your theme provides. With Custom Properties however, there’s hardly any increase in complexity, so it becomes easier for you as a theme developer to provide the customization flexibility that users want – with almost no impact on CSS size.

How does this work in the Customizer and, more importantly, Gutenberg?

So far we’ve only addressed how you can leverage CSS Custom Properties to improve user experience in the frontend – which arguably is the most important thing since most users of your theme will be exposed to that experience (probably without knowing that it’s your theme). But of course you also want the administrators using your theme to be happy – as they’ll eventually be the ones who decide to use it. In this last section of this post, let’s talk about how we can take the learnings from before and apply them also to the backend, i.e. the editing experience.

CSS Custom Properties in the Customizer

Because the best things come last, we’re gonna start with the Customizer integration first. Granted, the Customizer is not exactly the center of attention in WordPress land right now, but it still is the way for a theme to allow modifications in behavior and design today. As a theme developer you’re probably aware that by default the Customizer preview works by reloading the preview iframe holding the frontend on every setting change. This is how to make it work with the least developer intervention possible. Of course the experience is far from optimal that way, so let’s look at how we can get to a situation that feels faster. It shouldn’t require a full page refresh to see what your color change looks like.

You may have heard of a Customizer feature called “selective refresh”. Selective refresh allows a theme developer to define a certain area in the page (more technically, a certain element in the DOM) that should be refreshed everytime a certain setting is updated. While this still performs an HTTP request to refresh the content, the experience feels much faster and more integrated to the administrator because only the area affected reloads, not the entire frame. The way you typically see selective refresh being used is that the theme developer specifies a visible element in the DOM, e.g. a certain div or a section. However, we can also use the same feature to update CSS – because, keep in mind, a style tag containing CSS is just as much a DOM element as a div or section is. More precisely, we can use selective refresh to select our style tag containing the Custom Property definitions from the Customizer that are included in the frontend, and simply reload its inner content (i.e. the style rules) whenever a Customizer setting affecting it (e.g. a color) is changed.

Here’s what that code could look like. You could add something like the following to your Customizer interface setup function, which should be hooked into the customize_register action and receives the WP_Customize_Manager instance via e.g. a $wp_customize parameter:

if ( isset( $wp_customize->selective_refresh ) ) {
	$wp_customize->selective_refresh->add_partial(
		'css_custom_properties',
		array(
			'settings'        => $customizer_style_setting_ids,
			'selector'        => $customizer_style_element_selector,
			'render_callback' => ‘function_that_prints_customizer_css’,
		)
	);
}

You should pass an array of Customizer setting IDs, a string containing a selector uniquely identifying the Customizer style element (inline styles added via wp_add_inline_style() receive an ID attribute, so you can simply use that), and a callback function that prints the related Customizer extra CSS. Make sure that callback function only prints the CSS itself, not the surrounding style tags.

Here is a more concrete example for Twenty Twenty:

if ( isset( $wp_customize->selective_refresh ) ) {
	$wp_customize->selective_refresh->add_partial(
		'css_custom_properties_colors',
		array(
			'settings'        => array(
				'background_color',
				'header_footer_background_color',
				'accent_hue',
			),
			'selector'        => '#twentytwenty-style-inline-css',
			'render_callback' => function() {
				echo twentytwenty_get_customizer_css();
			},
		)
	);
}

Note that there is another way to update the preview with even more immediate feedback, which relies on custom JavaScript that listens to settings changes and acts accordingly. Twenty Twenty actually includes that, so you technically don’t even need to put the above in place. However, look at all the code necessary to accomplish that – 87 lines, not exactly straightforward compared to the above snippet, right? Furthermore it includes a lot of manual changes of style attributes and some duplicate logic that is also present on the PHP side of things. Don’t get me wrong, using JavaScript for an immediate preview is actually an awesome thing because it provides the fastest user experience for the person using the interface, but it can also get clunky easily if you make mistakes, and you need to know the JavaScript API of the Customizer fairly well. Takeaway from here: If you’re familiar enough with JavaScript and the Customizer API, I recommend adding JavaScript to update the styles (without making an HTTP request to the server). Otherwise, using selective refresh is perfectly fine and gets you about let’s say 90% there (plus it’s a lot easier to maintain).

CSS Custom Properties in Gutenberg

Using Custom Properties in Gutenberg puts the final touches in place. In order to achieve a WYSIWYG experience, you need to make sure that e.g. the text and background colors and fonts used in the Gutenberg editor match those used in the frontend. Most themes that do that today add the extra Customizer CSS either via wp_add_inline_style() to the theme’s block editor stylesheet, or they filter the block_editor_settings associative array, adding another entry to the styles list. Regardless of which way you choose, adding duplicate CSS here to override the default colors with Customizer colors introduce the same issues as in the frontend. So let’s fix them in the same way, with Custom Properties.

Since we’ve already learned how we can migrate an existing stylesheet to use Custom Properties to be flexible enough for the theme’s Customizer settings, doing that for the editor is going to be fairly trivial – as it’s essentially the same.

First of all, we need to modify the block editor stylesheet of the theme in the same way that we modified the frontend stylesheet earlier, by replacing all hard-coded colors that can be updated with the Customizer with Custom Properties.

  • For Twenty Twenty, this means making the same replacements made before in the assets/css/editor-style-block.css file, and then adding the Custom Property definitions (via :root) on top. Since Twenty Twenty provides a separate stylesheet for the classic editor (assets/css/editor-style-classic.css), you should probably make the replacements there as well.
  • For Twenty Nineteen, you need to make the same replacements made before in its style-editor.css, and then add the Custom Property definitions (via :root) on top. This file is used for both the block editor and the classic editor.

Then, simplify the Customizer CSS output generated by the theme’s respective PHP function to only generate the Custom Property definitions (i.e. exactly the same definitions as before.

  • In Twenty Twenty’s twentytwenty_get_customizer_css(), you can simply take over the definitions from before and return them regardless of which value the $type parameter passed to the function has.
  • Same in Twenty Nineteen’s twentynineteen_custom_colors_css(), just return the definitions you already provided earlier in every case. You no longer need to have one set of CSS for the frontend and another for the editor.

As you can see, in both of the above cases you’ve now significantly reduced complexity in the PHP-generated Customizer CSS even further. No longer is it necessary to have different CSS output generated for different contexts, since these differences are already accounted for in the actual stylesheets. The fact that you only print Custom Property definitions and reuse them in frontend, Customizer, Gutenberg, and yes, even the Classic Editor, ensures they always stay in sync, without you having to maintain each individual version. Think about it this way, this is true WYSIWYG by definition!

One more tiny recommendation: Keep in mind that, as users can freely modify colors, they might also make slightly more “extreme” changes, such as switching to a dark color scheme, where e.g. the background color is black and the font color is white. Since Gutenberg’s UI is intertwined with the styles for the preview, you need to make sure that its UI still works properly – if you just set the body color property to white, you won’t be able to see the text in some of Gutenberg’s UI any longer. Fortunately, there is a neat simple way to fix it (which I actually discovered myself just now): Just add theme support for ‘dark-editor-style’ if your color configuration results in a dark scheme, and Gutenberg will take care of the rest. 😍

Here is a quick demo showing how using the CSS Custom Properties approach results in consistent styles across the site’s frontend, the Customizer, and Gutenberg.

I hope this post inspired you to play around with CSS Custom Properties if you haven’t already. As you can see, its usage in WordPress themes with customizability can drastically improve user experience both in the frontend and the backend. And even for you as a developer, it improves the development experience, since you no longer have to maintain duplicate style rules. As you can see, there’s hardly any downside of using Custom Properties.

Browser support for the powerful feature is largely present, and has been for a while. Only Internet Explorer still cannot benefit from it. The good thing is that you can still start using Custom Properties already today. There’s a very simple way of providing a fallback for browsers that don’t support it: For every CSS rule containing a Custom Property, simply add the same rule with a hard-coded value right before. Browsers that cannot interpret Custom Properties yet will then fall back to the hard-coded value. Tools like PostCSS can help you with that. If you want to also provide the correct Customizer style to Internet Explorer users, for a while you can continue to maintain the current version of your theme’s extra Customizer CSS. For example, you could create a new function for the Custom Property definition output and only keep the old one for IE. A possibility would be to only load that stylesheet from the frontend if the current browser used is IE.
I very much recommend you to familiarize yourself more with the power of CSS Custom Properties, for example with this excellent introduction by Smashing Magazine. I’m looking forward to seeing this feature being leveraged by WordPress themes more – it’s another important milestone for improving user experience across the web, particularly relevant for CMSs with customizability, such as WordPress.

Leave a Reply

Your email address will not be published. Required fields are marked *