WordPress.org

WordPress Developer Blog

How to add custom color options to blocks

Picture this: you’re happily writing code for that block that you’ve been dreaming about building for a while. Almost all the elements and settings are in their place. You’re near the finish line and ready to put this thing out into the world.

But there’s one thing missing: you need an extra color setting.

You’ve already implemented the standard text, background, and link colors that WordPress offers block developers out of the box. But your block is special. It needs at least one more color so that your users can enjoy the ultimate customizability that you envisioned when you set out on this journey.

But there’s a catch: WordPress doesn’t have a simple method of just configuring a color attribute that will magically work for your block (it’d be cool if it did). You’ve suddenly got to code all of that.

This is a tutorial that gives you a solution on adding custom color options to your blocks.

Some things to know up front

This is an advanced tutorial, so I’m making some assumptions about your knowledge level. This is necessary to keep this article down to a somewhat reasonable word count.

In particular, I’m making two major assumptions:

  • You already know how to build custom blocks from scratch and work with the @wordpress/scripts package.
  • You’re comfortable enough to take what you learn from this guide and extend it for custom use cases.

If you’re not quite there yet and need further help, hop over to the Block Editor Handbook and get started with some of its available guides. When you feel like you’re ready for this tutorial, it will still be here waiting for you.

You can view the example block I built to write this tutorial in its GitHub repository. I will not explain each detail of this—remember, I’m assuming some prior knowledge. But the code is available for you to use however you want.

I’m putting this in a big ol’ alert box to make sure you read it, so please don’t skip this part.

The code in this tutorial uses experimental features in WordPress. This absolutely means they can—and probably will—break at some point. If you don’t like living life on the bleeding edge, this is probably not the tutorial for you. But if you’re anything like me and enjoy fixing broken things, just remember that you are ultimately responsible for updating your plugins, themes, or sites if you decide to use this in production and it stops working down the road.

Building a list block with colored markers

In the following sections, you will learn how to add add a custom marker color option for a block that outputs a basic HTML list like this:

Undoubtedly, you’ll want to apply this to your own block that you’re building, so feel free to pick out the bits you need as you read along. But it’s also a good exercise to follow along with the steps and build this simpler block.

Setting up your block plugin

In this tutorial, the examples will be shown within a static block, but you can also apply them to a dynamic block. It follows the standard file and folder naming conventions that you will get from the @wordpress/create-block or @wordpress/scripts packages.

The file structure of your plugin should be:

  • build/
    • WordPress will output the compiled files to this folder.
  • src/
    • block.json
    • edit.js
    • index.js
    • save.js
    • style.scss
  • index.php

To get started, add your Plugin File Header and register the block using the register_block_type() function on the init hook:

<?php
/**
 * Plugin Name:       List Block With Marker Colors
 * Plugin URI:        https://developer.wordpress.org/news/
 * Description:       An example block that shows a list with a custom marker color option.
 * Version:           1.0.0
 * Requires at least: 6.3
 * Requires PHP:      7.4
 * Author:            WordPress Developer Blog
 * Author URI:        https://developer.wordpress.org/news/
 * Text Domain:       devblog
 */

add_action( 'init', 'devblog_list_marker_colors_setup' );

function devblog_list_marker_colors_setup() {
	register_block_type( trailingslashit( __DIR__ ) . 'build' );
}

Adding color attributes to block.json

When adding custom color options, you should always be mindful of what’s already available to you out of the box. WordPress has attributes for handling:

  • Text color
  • Link text color
  • Background color and gradient
  • Border color (handled through border supports rather than color)

It only makes sense to add new color attributes if you cannot make use of the defaults. Keep in mind that you can use the Selectors API to apply existing color attributes to specific selectors for your block.

For our fictional list block, let’s add a single color option that lets users change the list marker (bullet) color. 

To do this, you will need two new attributes:

  • markerColor: (string) For storing the slug when the user selects a preset color.
  • customMarkerColor: (string) For saving the CSS version of the color for either the color preset or when a user chooses a custom color.

Technically, you can use a single attribute for this and add some custom logic behind the scenes for handling both presets and custom colors. But using two separate attributes makes for a simpler codebase. You’ll probably find that there are many different ways of wrangling this once you start customizing this for your own blocks.

In your block.json file, add the markerColor and customMarkerColor properties to the attributes object:

{
	"apiVersion": 3,
	"version": "1.0.0",
	"name": "devblog/list-with-marker-colors",
	"title": "List With Marker Colors",
	"category": "widgets",
	"description": "Add a custom description here.",
	"attributes" : {
		"markerColor": {
			"type": "string"
		},
		"customMarkerColor": {
			"type": "string"
		}
	},
	"supports": {
		"color": {
			"link": true,
			"gradients": true
		}
	},
	"textdomain": "devblog",
	"editorScript": "file:./index.js",
	"style": "file:./style-index.css",
	"example": {}
}

Building the block edit function

With all the initial setup and block.json code in place, it’s time to get into the heart of this tutorial and add that custom control for your block type. That’s what you came here for, right?

In the following subsections, you will work directly within your block’s edit.js file.

Importing dependencies

Before writing any custom code, you’ll import several dependencies from the @wordpress/block-editor and @wordpress/i18n packages. You should already be familiar with most of these, but the color-specific components that might be new to you are:

  • withColors
  • __experimentalColorGradientSettingsDropdown
  • __experimentalUseMultipleOriginColorsAndGradients

We’ll dive into how those work in the upcoming sections. For now, you just need to import them at the top of your edit.js file:

import {
	InspectorControls,
	useBlockProps,
	withColors,
	__experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown,
	__experimentalUseMultipleOriginColorsAndGradients as useMultipleOriginColorsAndGradients
} from '@wordpress/block-editor';

import { __ } from '@wordpress/i18n';

The code uses aliases for the experimental component names, removing the __experimental prefix. This is standard practice so that you only need to change the import code itself when the component is no longer experimental.

Coding the block edit function

After you’ve added your imports, you need a block edit function to handle the functionality in the editor. To do this, add this code to your block’s edit.js file:

const Edit = ( {
	attributes: {
		customMarkerColor
	},
	markerColor,
	setMarkerColor,
	setAttributes,
	style,
	clientId
} ) => {
	// Add your custom code here.
};

export default withColors( {
	markerColor: 'marker-color'
} )( Edit );

You’ll likely notice that markerColor is not nested under the attributes property. It is a separate property altogether, and there is an accompanying setMarkerColor. This is because the edit function is wrapped with the withColors higher-order component. This gives you specific properties to access for more efficiently working with colors.

Instead of a string, markerColor is now an object with several properties that you can access. This is an example of what that object looks like when a preset color is selected:

{
	class: "has-vivid-red-marker-color",
	color: "#cf2e2e",
	name: "Vivid red",
	slug: "vivid-red"
}

But it looks like this when a custom color is selected:

{
	class: undefined,
	color: "#66b3dc"
}

You can use this data to do all sorts of neat things, such as adding the class to the block wrapper or manipulating the slug to do something custom.

setMarkerColor is a function for setting a new marker color for the list. It is built from the markerColor name passed into withColors by prefixing it with set.

Now let’s start building the guts of the Edit() function. The remainder of the code snippets in this section should be placed within the function itself.

The first thing you need to grab is a copy of the WordPress color and gradient settings. This uses the useMultipleOriginColorsAndGradients() (experimental) React hook. It gives you an object with most of the settings you’ll need for your color control, including arrays of the WordPress, theme, and user-defined colors and gradients.

Add this code within your block’s Edit() function:

const colorGradientSettings = useMultipleOriginColorsAndGradients();

Next, you will assign the useBlockProps() hook to the blockProps constant. But you’ll also use this opportunity to add your logic for outputting the marker color.

To do this, you’ll manipulate the style property for the block. Because this particular scenario will target list markers, the code below uses a CSS custom property named --devblog-list-marker and assigns it the marker color.

Add this code within your block’s Edit() function:

const blockProps = useBlockProps( {
	style: {
		...style,
		'--devblog-list-marker': markerColor.slug
			    ? `var( --wp--preset--color--${ markerColor.slug } )`
			    : customMarkerColor,
	}
} );

Now it’s time to add the custom color control. For this, you’ll need the ColorGradientSettingsDropdown (experimental) component. Really, all you’re mostly doing at this point is plugging in a handful of values.

Add this code to your block’s Edit() function:

const markerColorDropdown = (
	<ColorGradientSettingsDropdown
		settings={ [ {
			label: __( 'Marker', 'devblog' ),
			colorValue: markerColor.color || customMarkerColor,
			onColorChange: ( value ) => {
				setMarkerColor( value );

				setAttributes( {
					customMarkerColor: value
				} );
			}
		} ] }
		panelId={ clientId }
		hasColorsOrGradients={ false }
		disableCustomColors={ false }
		__experimentalIsRenderedInSidebar
		{ ...colorGradientSettings }
	/>
);

I want to draw your attention to two of the component’s properties:

  • colorValue: For the value here, make sure to check for markerColor.color or customMarkerColor.
  • onColorChange: You need to call both setMarkerColor() (for setting the markerColor attribute) and setAttributes (for setting the customMarkerColor attribute).

The last item is to return the block controls and markup. For this block type, I left this as a simple unordered list for the purposes of demonstration. You will obviously want to apply this technique to more complex blocks.

Now add this final bit of code inside of your block’s Edit() function:

return (
	<>
		<InspectorControls group="color">
			{ markerColorDropdown }
		</InspectorControls>
		<ul { ...blockProps }>
			<li>{ __( 'List Item 1', 'devblog' ) }</li>
			<li>{ __( 'List Item 2', 'devblog' ) }</li>
			<li>{ __( 'List Item 3', 'devblog' ) }</li>
			<li>{ __( 'List Item 4', 'devblog' ) }</li>
			<li>{ __( 'List Item 5', 'devblog' ) }</li>
		</ul>
	</>
);

One thing to note about this code is the group attribute for <InspectorControls>. By giving it a value of color, you’re telling WordPress to stick your marker color picker alongside all of the other color settings in the block’s sidebar. To learn more about how this works, check out Using block inspector sidebar groups.

Building the block save function

Since this is a static block, we also need to save the block markup in the backend. If you’re building a dynamic block, you will add your handling on the PHP side.

In the following subsections, you will be working in your block’s save.js file.

Importing dependencies

As usual, you need to import some dependencies for your save function. And again, you’ll need a couple of things from the @wordpress/block-editor and @wordpress/i18n packages.

Add this code to the top of your block’s save.js file:

import { useBlockProps } from '@wordpress/block-editor';
import { __ }            from '@wordpress/i18n';

Exporting the save function

The save function is much simpler than the edit function from the previous section. For this, you basically just need to export your final markup for the block so that it’s stored in the database.

The major difference here is that you’ll need both the markerColor and customMarkerColor attributes. As defined in block.json, each will be a string value.

Add this code in your save.js file:

export default function Save( {
	attributes: {
		markerColor,
		customMarkerColor
	},
	style
} ) {

	const blockProps = useBlockProps.save( {
		style: {
			...style,
			'--devblog-list-marker': markerColor !== undefined
			            ? `var( --wp--preset--color--${ markerColor } )`
				    : customMarkerColor,
		}
	} );

	return (
		<ul { ...blockProps }>
			<li>{ __( 'List Item 1', 'devblog' ) }</li>
			<li>{ __( 'List Item 2', 'devblog' ) }</li>
			<li>{ __( 'List Item 3', 'devblog' ) }</li>
			<li>{ __( 'List Item 4', 'devblog' ) }</li>
			<li>{ __( 'List Item 5', 'devblog' ) }</li>
		</ul>
	);
}

Adding custom styles

Since the block uses the --devblog-list-marker CSS custom property for handling the list marker color, it needs a way to tell the browser to display that color. For this, you need to target any ::marker pseudo-elements for the block in your CSS.

Add this to your block’s style.scss file:

.wp-block-devblog-list-with-marker-colors ::marker {
	color: var( --devblog-list-marker, inherit );
}

Of course, for your own block types, you should target the specific element that you’re using the color for. ::marker is specific to the list used in the block.

Registering your block

Now you just need to register your block via JavaScript for it to work. To do this, open your block’s index.js file.

Once again, you need to import a few things. You’ll need the registerBlockType() function from the @wordpress/blocks package and your block’s custom files.

Add this code at the top of your block’s index.js file:

// Import stylesheets.
import './style.scss';

// Import dependencies.
import { registerBlockType } from '@wordpress/blocks';
import blockEdit             from './edit';
import blockSave             from './save';
import blockData             from './block.json';

Now you just need to pass the data you imported into the registerBlockType() function:

registerBlockType( blockData, {
	edit: blockEdit,
	save: blockSave
} );

Once you’ve saved everything, you should see your new block under the Widgets category in inserter:

Add your own color options

There is a lot that I left unsaid in this tutorial. I’ve experimented with various methods for wrangling color options in the last year or so. In that time, some things have changed in WordPress. I’m also continually learning new techniques—a colleague even showed me an entirely new approach in the middle of writing the first version of this tutorial. And given that this tutorial is using components that are experimental, I think it’s OK to leave a few things out.

The goal for this tutorial was to provide you with a base to run your own experiments, break a few things along the way, and bring your experience back into the discussion. Feel free to share in the comments.

So go forth and have some fun with custom colors!

Props to @webcommsat, @juanmaguitar, and @ndiego for feedback and review.

Categories: , ,

5 responses to “How to add custom color options to blocks”

  1. colind Avatar

    Thanks for yet another helpful post. Question: would there be a way to add the marker color att to the core/list block rather than a custom block?

    Would there be a way to show the attribute control based on the block variation chosen? (default/colored marker) I have a case where the design calls for a heading variation with a stylized border and it would be great to use the core/heading block and then allow editors to opt for the border style variation which exposes the border color attribute control.

    1. Justin Tadlock Avatar

      I’m certain you can extend any block to add custom controls. Here’s a tutorial that I wrote (it was primarily for themes but should translate to plugins just fine): Beyond block styles, part 3: building custom design tools. Essentially, you’d want to combine the method there of filtering the List block and apply the custom control in the filter. You’d also need to filter registerBlockType to add custom attributes. At least that’s one approach.

      I’m not sure about showing controls based on variation. I feel like it should be possible, but I don’t know offhand. I’ll ask around to see if anyone knows.

    2. Nick Diego Avatar

      Concerning only showing the control for specific block styles, this is definitely possible. In fact, the WordPress Social Links block does this. You can see the source code here.

      You basically need to check if the className attribute contains the correct style class, and then conditionally render the color control.

  2. Adam Lenz Avatar

    The “reset all” function is not working on tool panel items this way. How can we tie into the resetAll prop on the parent Tool Panel? This is great btw.

  3. Robert O'Rourke Avatar
    Robert O’Rourke

    Off the back of this tutorial I wrote a basic plugin for adding to the colours UI via a single function call, it extends the `style.elements` attribute used to enable link colours. I’m excited to see where core goes in terms making the style engine a little more pluggable, and perhaps in terms of providing the UI in future.

    https://github.com/humanmade/block-supports-extended

Leave a Reply

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