Welcome back! It’s time for Part 2 in this tutorial series that will teach you how to extend blocks with custom controls from within a theme.
Did you miss Part 1? Check out Beyond block styles, part 1: using the WordPress scripts package with themes first. Then come back to this post and follow along.
Part 2 of this three-part series will walk you through designing a custom Separator block style that displays an emoji icon. In Part 3, we’ll build an emoji icon picker for this style like this:
Table of Contents
Start your engine
In Part 1, you learned how to integrate the WordPress scripts package with your theme. Remember that you’ll need to enable watch mode when you write custom CSS/SCSS and JavaScript. That way, you’ll know for sure your compiled code will end up in the /public
folder.
Launch your favorite command-line tool and navigate to your theme folder. Then run this command:
npm run start
Back to basics: creating block styles
Before you get into the deep water of rolling your own editor controls, let’s step back into the shallows a minute—and build the icon styles with the Block Styles API.
And, yes. I can hear you from right through your screen: if we’re building a custom control, why would we register the block styles first?
The primary reason is that it is a natural part of the development process. When creating new features for blocks, I almost always begin by building them as custom block styles.
Often, that’s as far as you’ll go on a specific project. When you need to go beyond block styles, you won’t necessarily realize that until it’s obvious—or, at least, that’s how it works for me. As always, your mileage may vary.
Other reasons for beginning with the Block Styles API include:
- There’s a bright line where the Block Styles API reaches its limits, and the case for building custom controls becomes very clear. You and I are going to cross that line together.
- When was the last time you registered block styles with JavaScript? Has it been a while? Have you ever done it? (Plenty of very good WordPress theme developers haven’t.)
- You will walk away from our session with some base code that you’ll build on in Part 3. But even before then, you’ll have it to work with and understand how to use it in your own projects.
Registering block styles with JavaScript
Adding custom icons
One of the things I like to do in development is to compartmentalize the various pieces of the feature I’m building. This makes it easier to find what I’m looking for and change it in the future. Tidy code is easier to maintain over the long haul.
First, create a const.js
file under resources/js
. This folder should now look like this:
resources/
js/
const.js
editor.js
You will use const.js
to store constants that other files can import. Right now, it will hold an array of icon definitions named ICONS
.
So go ahead and add this code to the file:
export const ICONS = [
{ value: 'floral-heart', icon: '❦' },
{ value: 'blossom', icon: '🌼' },
{ value: 'sun', icon: '☀️' },
{ value: 'feather', icon: '🪶' },
{ value: 'fire', icon: '🔥' },
{ value: 'leaves', icon: '🍃' },
{ value: 'coffee', icon: '☕' },
{ value: 'beer', icon: '🍻' },
{ value: 'lotus', icon: '🪷' },
{ value: 'melting-face', icon: '🫠' },
{ value: 'guitar', icon: '🎸' },
{ value: 'pencil', icon: '✏️' },
{ value: 'rocket', icon: '🚀' },
{ value: 'clover', icon: '☘️' },
{ value: 'star', icon: '⭐' },
{ value: 'sunflower', icon: '🌻' },
{ value: 'beach-umbrella', icon: '⛱️' }
];
The command at the top, export const ICONS
, does exactly what you would think: it exports the array of objects you see between the square brackets.
Ultimately, each object will generate a CSS class that looks like this: .is-style-icon-{value}
.
Technically, you can use HTML symbols or even emoji in CSS class names. I like to do that sometimes, for fun, but in a tutorial like this, I would probably just start a debate about whether we should do some things just because we can.
Registering block styles
To use your custom icons, import them into resources/js/editor.js
. You’ll also need to import a few WordPress functions:
// Internal dependencies.
import { ICONS } from "./const";
// WordPress dependencies.
import { registerBlockStyle } from '@wordpress/blocks';
import domReady from '@wordpress/dom-ready';
import { __, sprintf } from '@wordpress/i18n';
Registering block styles is pretty straightforward: just plug your values into registerBlockStyle()
. Head over to the Block Editor Handbook to learn more about the Block Styles API.
Below, notice that the code waits until the DOM is ready, then loops through each of the icons and registers them as a block style:
// Register a block style for each icon.
domReady( () => {
ICONS.forEach( ( icon ) =>
registerBlockStyle( 'core/separator', {
name: `icon-${ icon.value }`,
label: sprintf( __( 'Icon: %s', 'theme-slug' ), icon.icon )
} )
);
} );
Save your file. webpack will compile it.
Now create a new post or page, and add a Separator block. Here’s what it should look like:
You haven’t added any custom CSS yet, so none of the styles actually work. For now, just make sure you can see the block styles where they belong, in the Styles panel in the sidebar.
And … it may be dawning on you that the block styles system is hitting a limit. You now have 20 custom styles in the sidebar over there—17 of yours, plus three defaults. The UI was simply not built for this.
Clearly, it’s time to build a custom control.
Designing icon separators
A peek behind the curtain: it wasn’t until after I’d built out all the code for this series that I realized that I selected an example with the most complicated CSS I could use.
I could have used something with a few lines of style rules, but noooo—my CSS had to get into a cage match with WordPress. But then I also realized: you’re a WordPress theme author yourself, and you know how this game is played. When the dust settled, my CSS was victorious. 🥊 🏆
For the icon block styles to render the design I had in mind, the CSS had to do these things:
- It must center the icon. CSS flexbox and text alignment make this dead simple.
- The background line needs to inherit the background color or gradient the user has chosen. To make it happen, the CSS has to override WordPress’ use of the
background
shorthand property1. - It must hide a part of that line behind the icon, so the line looks as if the icon has split in two. You’ll use the
base
color preset in Twenty Twenty-Three as the icon background.
Open your theme’s resources/scss/screen.scss
file and add the base block style code:
hr.wp-block-separator[class*=is-style-icon] {
display: flex;
justify-content: center;
border: none;
text-align: center;
overflow: visible;
width: 100% !important;
// Use `!important` to overrule core's use of the `background` shorthand.
background-color: transparent !important;
background-repeat: repeat-x !important;
background-position: 0% 50% !important;
background-size: 100% 2px !important;
// Don't use `!important` for the background image, which allows user-
// selected background gradients to work.
background-image: linear-gradient( 90deg, currentColor, currentColor );
// Styles the icon.
&::before {
// floral-heart default icon.
content: var( --separator-icon, "\2766" );
padding: 0 0.5em;
font-size: var( --wp--preset--font-size--large );
line-height: 1;
background-color: var( --wp--preset--color--base );
}
// If parent has background color, inherit down to the icon background.
:where( .has-background[class*=-background-color] ) &,
:where( .has-background[class*=-background-color] ) &::before {
background-color: inherit !important;
}
}
Now assign each custom block style class to its custom icon. Did you notice, above, that there’s a CSS custom property called --separator-icon
? You can use that to override the icon.
In the same screen.scss
file, add a style rule for each icon, assigning the symbol or emoji to the --separator-icon
variable:
.is-style-icon-blossom { --separator-icon: "🌼"; }
.is-style-icon-sun { --separator-icon: "☀️"; }
.is-style-icon-feather { --separator-icon: "🪶"; }
.is-style-icon-fire { --separator-icon: "🔥"; }
.is-style-icon-leaves { --separator-icon: "🍃"; }
.is-style-icon-coffee { --separator-icon: "☕"; }
.is-style-icon-beer { --separator-icon: "🍻"; }
.is-style-icon-lotus { --separator-icon: "🪷"; }
.is-style-icon-melting-face { --separator-icon: "🫠"; }
.is-style-icon-guitar { --separator-icon: "🎸"; }
.is-style-icon-pencil { --separator-icon: "✏️"; }
.is-style-icon-rocket { --separator-icon: "🚀"; }
.is-style-icon-clover { --separator-icon: "☘️"; }
.is-style-icon-star { --separator-icon: "⭐"; }
.is-style-icon-sunflower { --separator-icon: "🌻"; }
.is-style-icon-beach-umbrella { --separator-icon: "⛱️"; }
Your icons should now work in the editor:
Take another look. You may not have caught it the first time, but this screenshot doesn’t yet match the one at the top of this post—the one I specified as our goal.
What’s missing? The icons there have the gradient-background lines (okay, faux lines) behind them. But the block styles don’t.
The good news: the CSS can handle user-selected background colors and preset gradients:
There’s a bug in WordPress for the moment where user-defined gradients don’t work with the Separator block (one of our awesome reviewers actually discovered this while reviewing this tutorial). Preset gradients? No problem. Custom gradients? Broken.
You could write CSS for each icon’s default background. But that will add weight to the page load, slowing it down for no good reason. Don’t do that, please, especially on the front end.
Plus, the Separator block already has a background attribute. You just can’t get to it from the Block Styles API.
That background issue is one more limitation of the block styles system—you can only control your block style with CSS.
By contrast, when you build custom controls, you have full access to any attribute of the Separator block—not just the block style’s CSS class. And that’s just what you’ll do in Part 3 of this series. You’ll define a background gradient for each icon, then set it automatically based on user input.
Gearing up for the last leg of this journey
We’ve covered a lot of ground in the first two parts of this tutorial series. You’ve learned how to integrate the WordPress scripts package in your theme and have now seen it in action. You’ve built out some custom icon styles for the Separator block and seen some of the limitations of the Block Styles API.
In Part 3, we’ll bring it all together into a single icon picker control.
Before the final tutorial in this series comes out, I want you to do one thing: delete all the code in your resources/js/editor.js
file. Yes, all of it. You won’t need it anymore. Keep what you added to resources/js/const.js
and resources/scss/screen.scss
. You’ll build on top of those files next time.
Until then, play around with the code you have so far. And have fun!
Props to @bph and @marybaum for feedback and review.
Footnotes
- For background gradients, WordPress uses the
background
shorthand instead ofbackground-image
, wiping away styles on otherbackground-*
properties. There is an open ticket for this issue. To work around this and still let the background gradient come through, define each property individually and use!important
to ensure they work. ↩︎
Leave a Reply