Create different color palettes with Gutenberg

Since Friday I am working on my first Gutenberg project and almost instantly had my first larger problem: the project has two different color palettes for text and background color. Gutenberg, by default, makes it possible to have one color palette. The colors can be modified relatively easy, but the project makes it necessary two have two different palettes. This post shows how to get this working.

Update from July 11, 2018: Both color attributes need to be set as attributes in the last code block. Otherwise, the selected colors are not recognized from the color pickers after reloading the browser.

The Goal

The goal was to recreate the color pickers that are available in the paragraph block:

A screenshot of the color palettes present in the sidebar when a paragraph block is selected in Gutenberg. Both palettes – for text and background color – have the same colors.

The colors are inside an area that can be toggled, and the selected color is shown next to the title of the area. When a color is selected, the paragraph element gets a class and an inline style.

The approach

Fast I came across the ColorPalette component in the Gutenberg GitHub repo, to that you can pass custom colors to overwrite the default color palette (or the one that was defined via add_theme_support()). That worked, but to make it look like the ones from the paragraph block, we need to use the PanelColor component. But we cannot pass colors to that component.

After searching for a solution, I ended up with recreating this component, that uses ColorPalette internally. With the code from the paragraph block I found a solution to apply the inline style, but setting the class name did not work.

Finally, I found out: to create the class name, Gutenberg uses the default color palette, not the one that is passed to ColorPalette. So the solution for that problem is to add all needed colors via add_theme_support() and define the color subset in the component.

The solution

With that, the solution is made from three parts:

You see: everything for the solution is available in the Gutenberg repo – one only need to find it 🙈😃

I assume that you have installed Gutenberg and included a JavaScript file in the editor and are already using a package.json. I am using modern JavaScript syntax, which is converted to ES5 with Babel.

Define the color palette

At first, we need to define our colors, which we want to use later:

add_action( 'after_setup_theme', 'setup_theme_supported_features' );

/**
 * Remove unused Gutenberg features.
 */
function setup_theme_supported_features() {
	add_theme_support( 'editor-color-palette', 
		[
			'name' => 'Puerto Rico',
			'slug' => 'puerto-rico',
			'color' => '#53c4ab',
		],
		[
			'name' => 'Tundora',
			'slug' => 'tundora',
			'color' => '#454545',
		],
		[
			'name' => 'Butterfly Bush',
			'slug' => 'butterfly-bush',
			'color' => '#5151a0',
		],
		[
			'name' => 'White',
			'slug' => 'white',
			'color' => '#ffffff',
		],
	);

	add_theme_support( 'disable-custom-colors' );
}

The JavaScript part

Besides Gutenberg components, we need the classnames library. To install it, we run the following command in our command line:

npm install classnames

Now let us begin with the external things, that we need to import into the JavaScript file:

import classnames from 'classnames';

const {
	registerBlockType,
} = wp.blocks;
const {
	InspectorControls,
	InnerBlocks,
	withColors,
	getColorClass
} = wp.editor;
const {
	PanelBody,
	withFallbackStyles,
	PanelColor,
	ColorPalette,
} = wp.components;

const {
	compose,
	Component,
} = wp.element;

const {getComputedStyle} = window;

const FallbackStyles = withFallbackStyles((node, ownProps) => {
	const {textColor, backgroundColor} = ownProps.attributes;
	const editableNode = node.querySelector('[contenteditable="true"]');
	//verify if editableNode is available, before using getComputedStyle.
	const computedStyles = editableNode ? getComputedStyle(editableNode) : null;
	return {
		fallbackBackgroundColor: backgroundColor || !computedStyles ? undefined : computedStyles.backgroundColor,
		fallbackTextColor: textColor || !computedStyles ? undefined : computedStyles.color,
	};
});

Like already written, the solution is composed of existing Gutenberg components – that said, those definitions and the withFallbackStyles() function in the paragraph block and the PanelColor component.

Like it is made in the paragraph block, we will define the edit part of our block as a subclass of Component:

class OneColumnBlock extends Component {
	constructor() {
		super(...arguments);
	}

	render() {
		const {
			attributes,
			setAttributes,
			mergeBlocks,
			onReplace,
			className,
			backgroundColor,
			textColor,
			setBackgroundColor,
			setTextColor,
			fallbackBackgroundColor,
			fallbackTextColor,
			fallbackFontSize,
		} = this.props;

		const textColors = [
			{
				name: 'Tundora',
				slug: 'tundora',
				color: '#454545'
			},
		];
		const backgroundColors = [
			{
				name: 'Puerto Rico',
				slug: 'puerto-rico',
				color: '#53c4ab'
			},
			{
				name: 'Butterfly Bush',
				slug: 'butterfly-bush',
				color: '#5151a0'
			},
			{
				name: 'White',
				slug: 'white',
				color: '#ffffff'
			}
		];

		return (
			<div
				className={classnames(className, {
					'has-background': backgroundColor.value,
					[backgroundColor.class]: backgroundColor.class,
					[textColor.class]: textColor.class,
				})}
				
				style={{
					backgroundColor: backgroundColor.value,
					color: textColor.value,
				}}
			>
				<InnerBlocks/>
				<InspectorControls>
					<PanelBody title={'Farbschema'}>
						<PanelColor {...{title: 'Textfarbe', colorName: textColor.name, colorValue: textColor.value, initialOpen: false }} >
							<ColorPalette
								colors={textColors}
								disableCustomColors={true}
								value={textColor.value}
								onChange={setTextColor}
							/>
						</PanelColor>

						<PanelColor {...{title: 'Hintergrundfarbe', colorName: backgroundColor.name, colorValue: backgroundColor.value, initialOpen: false }} >
							<ColorPalette
								colors={backgroundColors}
								disableCustomColors={true}
								value={backgroundColor.value}
								onChange={setBackgroundColor}
							/>
						</PanelColor>
					</PanelBody>
				</InspectorControls>
			</div>
		);
	}
}

Interesting is the render part. The constants at the beginning are from the paragraph block – textColors and backgroundColors are our subsets of the colors defined in add_theme_support( 'editor-color-palette' ).

In the return part, we open a div, in which the class names and the inline styles are outputted. This is also taken from the paragraph component. InnerBlocks is a component, that enables it to insert other blocks into a block, and after that inside InspectorControls and PanelBody we create our color pickers.

Normally, we would use just the PanelColor component, like seen in the paragraph block. For that, we would import the component from wp.editor. But I make it like done in this component, and import the PanelColor component from wp.components.

For PanelColor, we define the title, the color name, the color value and false for initialOpen, to define that the panel is closed by default. We disable the possibility to choose a custom colr and set the color picker’s value to textColor.value respectively backgroundColor.value. As the onChange value, we use the same as used for PanelColor in the paragraph block.

One last piece of code remains – also inspired by the paragraph component:

export default registerBlockType('slug/one-column', {
	title: 'Eine Spalte',
	icon: 'admin-post',
	category: 'layout',
	edit: compose([
		withColors('backgroundColor', {textColor: 'color'}),
		FallbackStyles,
	])(OneColumnBlock),
	save: props => {
		const {
			backgroundColor,
			textColor,
			customBackgroundColor,
			customTextColor,
		} = props.attributes;

		const textClass = getColorClass( 'color', textColor );
		const backgroundClass = getColorClass( 'background-color', backgroundColor );
		
		const className = classnames( {
			'has-background': backgroundColor || customBackgroundColor,
			[ textClass ]: textClass,
			[ backgroundClass ]: backgroundClass,
		} );
		return (
			<div className={className}>
				<InnerBlocks.Content/>
			</div>
		);
	},
});

Here we register our block slug/one-column and set a few meta data. edit is our OneColumnBlock class and in save we build the color classes and set them for a div container, that wraps the content of the InnerBlocks.

And that is the result (I have a few more colors in the project that used in the example):

Screenshot of our result. Two color picker controls with different color palettes.

The complete code can be found as a Gist on GitHub.

Related posts

3 comments on »Create different color palettes with Gutenberg«

  1. Mike England

    Thank you for sharing this! I've been trying to re-create this block in particular for the color palette panel and have had some very tough struggles getting it to work.

    Reply

Leave a Comment

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