Eight years ago, the WordPress Core team introduced the Theme Customizer; a tool for site administrators which allows aspects of the site to be adjusted without the use of any code.

(Note; there is a mix of spelling for the word “colour” in this article. My writing is British English and the code is American English. Make sure that you use “color” and not “colour”, as appropriate. The same applies to “customise” and “Customizer”.)

If the WordPress Theme is coded so that it supports features of the Customizer, admins can modify features like the site-wide logo, header colour and header image.

For a current project, in which I’m implementing the Gutenberg Editor for content creation, I needed to provide a way for the administrator to be able to control the “primary” and “secondary” colours of the theme and override the standard colours I’ve defined in the code. By using the logic of a primary and secondary colour – much in the same way that CSS frameworks like Zurb Foundation work – I can ensure that coloured elements on the page are harmonious; buttons matching links, or borders matching the header colour.

This is pretty simple to achieve using the add_setting function in the $wp_customize object; we add a control to the Customizer and save the selected colour as a theme modification.

Once the colour is selected, we can add a piece of inline CSS to the page head, which will override any rules in the CSS files linked by the Theme. (Assuming that your CSS rules aren’t excessively specific. Read more about that here.)

function mytheme_customize_css()
{
	$primary_color = get_theme_mod('primary_color', '#0000ff');
	?>
		 <style>
			a, h1 { color: <?php echo $primary_color; ?>; }
			button, .page-header { background-color: <?php echo $primary_color;?>; }
			.b-call-to-action { border-color: <?php echo $primary_color;?>; }
		 </style>
	<?php
}
add_action( 'wp_head', 'mytheme_customize_css');

Using CSS variables

This approach is fine, but what happens if there are dozens of elements which need to be styled to match the selected colour? You’ll have to add every single rule from every single element to this customised CSS snippet. What a pain. Thankfully, the white knight of CSS variables (also known as CSS Custom Properties) came to the rescue in 2016.

Browser support rose rapidly until I began using CSS variables in production code – with a fallback solution for Internet Explorer 11 – in 2018. Check out the version of the code which I use now. How much easier is that?!

function mytheme_customize_css()
{
	?>
	<style>
		:root { --color-primary: <?php echo get_theme_mod('primary_color', '#0000ff'); ?>; }
	</style>
	<?php
}
add_action( 'wp_head', 'mytheme_customize_css');

This approach depends upon you building your Theme CSS using CSS Variables from the start; instead of defining your CSS with a {color: #0000ff;}, you use a {color: var(--color-primary;} instead. By defining the value of --color-primary on the HTML element (:root), this value will apply throughout the document and will be inherited by the a tag. All in the browser, without any compilation necessary through (e.g.) SCSS.

So, your CSS file will look like this:

:root {
	--color-primary: #0000ff;
}

a, h1 {
	color: var(--color-primary);
}

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

.b-call-to-action {
	border: 1rem solid var(--color-primary);
}

…and (when you choose red from the colour picker in the Customizer) the inline CSS will look like this:

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

Loading custom colours in the Gutenberg Editor

That’s all you need to allow the selection of colours using the WordPress Customizer. But what about Gutenberg? There are two steps necessary here, assuming that you’ve coded your Theme so that the CSS is loaded both in the frontend of the website and in the Editor.

The first is to ensure that the CSS rules from the Customizer are loaded in the Editor. Instead of applying them to the :root element – which will potentially modify styles throughout WordPress admin – you need to add them to the main wrapper of the Gutenberg Editor: .editor-styles-wrapper. You also need to enqueue the styles using the admin_head hook, not wp_head.

By adding the definition of --color-primary to the .editor-styles-wrapper element, the definition will only apply to that element and its children.

function mytheme_customize_editor_css()
{
	?>
	<style>
		.editor-styles-wrapper { --color-primary: <?php echo get_theme_mod('primary_colour', '#0000ff'); ?>; }
	</style>
	<?php
}
add_action( 'admin_head', 'mytheme_customize_editor_css');

Adding the custom colours to the Gutenberg colour picker

This is the most difficult bit! The Gutenberg Editor provides users with a colour picker, so that they can choose the foreground and background colour for each element. Theme and Plugin developers can customise these colours using add_theme_support('editor-color-palette', $colours), where $colours is an array.

function gutenbergColors(){

	$colors = apply_filters('mytheme/gutenberg-colors', [
		[
			'name' => 'Primary',
			'slug' => 'primary',
			'color' => '#0000ff'
		],
		[
			'name' => 'Highlight',
			'slug' => 'highlight',
			'color' => '#ffff00'
		]
	]);

	if (!empty($colors)) {
		add_theme_support('editor-color-palette', $colors);
	}
}

add_action('after_setup_theme', 'gutenbergColors');

If the colours are customised in this way, the Theme or Plugin developer will need to ensure that the appropriate CSS class names – which are applied when using the colour picker in the Gutenberg Editor – are also customised.

The colours in the Editor are applied directly using an inline style attribute containing an RGB value of the selected colour, but the HTML for the website isn’t: it’s saved with just a class attribute.

Update 21st April: Gutenberg does apply the class name to the element in the editor, but also adds the inline style attribute. The rgb value appears to be dynamically calculated from the CSS applied through the CSS class name.

Where the use of static colours would mean definitions like .has-primary-color {color: #0000ff} in our CSS, we need to make sure that the definitions use the CSS variables: .has-primary-color {color: var(--color-primary);}.

You’ll see that I’ve added a custom filter for the $colours array in PHP, so that we can “hook into” it and modify the colours, if a custom colour is selected in the Customizer. Here’s how that works.

function maybeCustomizeColors($colours)
{
	if (!empty($colour_primary = get_theme_mod('colour_primary', ''))) {
		foreach ($colours as &$colour) {
			if ($colour['slug'] == 'primary') {
				$colour['color'] = $colour_primary;
				break;
			}
		}
	}

	return $colours;
}

add_filter('sht/gutenberg-colors', 'maybeCustomizeColors');

And that’s it! You now have working code which will allow website administrators to modify the primary colour using the WordPress Customizer, and which allows authors to pick and use those colours in the Gutenberg Editor.

An unsolvable problem for colours modified after the content has been created

The public website will always correctly reflect the colours chosen in the Customizer, as the CSS variable will be modified automatically whenever the primary colour is modified. This means that the administrator can choose a different primary colour at any time and the website will correctly reflect these changes. This is why it’s crucial to assign CSS variables to elements, not the static (or generated) hex or RGB colours. The same applies to page elements or custom blocks which have been pre-assigned with the primary colour CSS class name.

There is, unfortunately, a currently unsolvable problem. If the primary colour has been manually applied to (e.g.) a paragraph in the Gutenberg Editor, the element will be coloured using an inline RGB style attribute. This value will not be automatically updated in the Editor if a new primary colour is either defined in the Theme CSS, or using the Customizer. The website will be correct, but the element in the Editor will still use the previous selected colour. (A Github issue is open for this inconsistency.)

Got any questions? Leave a comment or get in touch via Twitter.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.