Fonts are a defining characteristic of the design of any site. That includes WordPress themes, where it’s common for theme developers to integrate a service like Google Fonts into the WordPress Customizer settings for a “classic” PHP-based theme. That hasn’t quite been the case for WordPress block themes. While integrating Google Fonts into classic themes is well-documented, there’s nothing currently available for block themes in the WordPress Theme Handbook.
That’s what we’re going to look at in this article. Block themes can indeed use Google Fonts, but the process for registering them is way different than what you might have done before in classic themes.
What we already know
As I said, there’s little for us to go on as far as getting started. The Twenty Twenty-Two theme is the first block-based default WordPress theme, and it demonstrates how we can use downloaded font files as assets in the theme. But it’s pretty unwieldy because it involves a couple of steps: (1) register the files in the functions.php file and (2) define the bundled fonts in the theme.json file.
Since Twenty Twenty-Two was released, though, the process has gotten simpler. Bundled fonts can now be defined without registering them, as shown in the Twenty Twenty-Three theme. However, the process still requires us to manually download font files and bundle them into the themes. That’s a hindrance that sort of defeats the purpose of simple, drop-in, hosted fonts that are served on a speedy CDN.
What’s new
If you didn’t already know, the Gutenberg project is an experimental plugin where features being developed for the WordPress Block and Site Editor are available for early use and testing. In a recent Theme Shaper article, Gutenberg project lead architect Matias Ventura discusses how Google Fonts — or any other downloaded fonts, for that matter — can be added to block themes using the Create Block Theme plugin.
This short video at Learn WordPress provides a good overview of the Create Block Theme plugin and how it works. But the bottom line is that it does what it says on the tin: it creates block themes. But it does it by providing controls in the WordPress UI that allow you to create an entire theme, child theme, or a theme style variation without writing any code or ever having to touch template files.
I’ve given it a try! And since Create Block Theme is authored and maintained by the WordPress.org team, I’d say it’s the best direction we have for integrating Google Fonts into a theme. That said, it’s definitely worth noting that the plugin is in active development. That means things could change pretty quickly.
Before I get to how it all works, let’s first briefly refresh ourselves with the “traditional” process for adding Google Fonts to classic WordPress themes.
To refresh our memory, here is an example from the default Twenty Seventeen theme showing how Google fonts are enqueued in the functions.php file.
function twentyseventeen_fonts_url() { $ fonts_url = ''; /** * Translators: If there are characters in your language that are not * supported by Libre Franklin, translate this to 'off'. Do not translate * into your own language. */ $ libre_franklin = _x( 'on', 'libre_franklin font: on or off', 'twentyseventeen' ); if ( 'off' !== $ libre_franklin ) { $ font_families = array(); $ font_families[] = 'Libre Franklin:300,300i,400,400i,600,600i,800,800i'; $ query_args = array( 'family' => urlencode( implode( '|', $ font_families ) ), 'subset' => urlencode( 'latin,latin-ext' ), ); $ fonts_url = add_query_arg( $ query_args, 'https://fonts.googleapis.com/css' ); } return esc_url_raw( $ fonts_url ); }
Then Google Fonts is pre-connected to the theme like this:
The Create Block Theme plugin satisfies GDPR privacy requirements, as it leverages the Google Fonts API to serve solely as a proxy for the local vendor. The fonts are served to the user on the same website rather than on Google’s servers, protecting privacy. WP Tavern discusses the German court ruling and includes links to guides for self-hosting Google Fonts.
How to use Google Fonts with block themes
This brings us to today’s “modern” way of using Google Fonts with WordPress block themes. First, let’s set up a local test site. I use Flywheel’s Local app for local development. You can use that or whatever you prefer, then use the Theme Test Data plugin by the WordPress Themes Team to work with dummy content. And, of course, you’ll want the Create Block Theme plugin in there as well.
Have you installed and activated those plugins? If so, navigate to Appearance → Manage theme fonts from the WordPress admin menu.
The “Manage theme fonts” screen displays a list of any fonts already defined in the theme’s theme.json file. There are also two options at the top of the screen:
Add Google fonts. This option adds Google Fonts directly to the theme from the Google fonts API.
Add local fonts. This option adds downloaded font files to the theme.
I’m using a completely blank theme by WordPress called Emptytheme. You’re welcome to roll along with your own theme, but I wanted to call out that I’ve renamed Emptytheme to “EMPTY-BLANK” and modified it, so there are no predefined fonts and styles at all.
I thought I’d share a screenshot of my theme’s file structure and theme.json file to show that there are literally no styles or configurations going on.
File structure of Emptytheme (left) and theme.json file (right)
Let’s click the “Add Google Fonts” button. It takes us to a new page with options to choose any available font from the current GoogleFonts API.
For this demo, I selected Inter from the menu of options and selected the 300, Regular, and 900 weights from the preview screen:
Once I’ve saved my selections, the Inter font styles I selected are automatically downloaded and stored in the theme’s assets/fonts folder:
Notice, too, how those selections have been automatically written to the theme.json file in that screenshot. The Create Block Theme plugin even adds the path to the font files.
If we go to the Create Block Theme’s main screen and click the Manage theme fonts button again, we will see Inter’s 300, 400 (Regular), and 900 weight variants displayed in the preview panel.
A demo text preview box at the top even allows you to preview the selected fonts within the sentence, header, and paragraph with the font size selection slider. You can check out this new feature in action in this GitHub video.
The selected font(s) are also available in the Site Editor Global Styles (Appearance → Editor), specifically in the Design panel.
From here, navigate to Templates → Index and click the blue Edit button to edit the index.html template. We want to open the Global Styles settings, which are represented as a contrast icon located at the top-right of the screen. When we click the Text settings and open the Font menu in the Typography section… we see Inter!
Same thing, but with local fonts
We may as well look at adding local fonts to a theme since the Create Block Theme plugin provides that option. The benefit is that you can use any font file you want from whatever font service you prefer.
Without the plugin, we’d have to grab our font files, drop them somewhere in the theme folder, then resort to the traditional PHP route of enqueuing them in the functions.php file. But we can let WordPress carry that burden for us by uploading the font file on the Add local fonts screen using the Create Block Theme interface. Once a file is selected to upload, font face definitions boxes are filled automatically.
Even though we can use any .ttf, .woff, or .woff2 file, I simply downloaded Open Sans font files from Google Fonts for this exercise. I snatched two weight variations, regular and 800.
The same auto-magical file management and theme.json update we saw with the Google Fonts option happens once again when we upload the font files (which are done one at a time). Check out where the fonts landed in my theme folder and how they are added to theme.json:
Removing fonts
The plugin also allows us to remove font files from a block theme from the WordPress admin. Let’s delete one of the Open Sans variants we installed in the last section to see how that works.
Clicking the Remove links triggers a warning for you to confirm the deletion. We’ll click OK to continue.
Let’s open our theme folder and check the theme.json file. Sure enough, the Open Sans 800 file we deleted on the plugin screen removed the font file from the theme folder, and the reference to it is long gone in theme.json.
The Create Block Theme plugin significantly enhances the user experience when it comes to handling fonts in WordPress block themes. The plugin allows us to add or delete any fonts while respecting GDPR requirements.
We saw how selecting a Google Font or uploading a local font file automatically places the font in the theme folder and registers it in the theme.json file. We also saw how the font is an available option in the Global Styles settings in the Site Editor. And if we need to remove a font? The plugin totally takes care of that as well — without touching theme files or code.
Thanks for reading! If you have any comments or suggestions, share them in the comments. I’d love to know what you think of this possible direction for font management in WordPress.
Additional resources
I relied on a lot of research to write this article and thought I’d share the articles and resources I used to provide you with additional context.
A little while back, Ganesh Dahal penned a post here on CSS-Tricks responding to a tweet that asked about adding CSS box shadows on WordPress blocks and elements. There’s a lot of great stuff in there that leverages new features that shipped in WordPress 6.1 that provide controls for applying shadows to things directly in the Block Editor and Site Editor UI.
Ganesh touched briefly on button elements in that post. I want to pick that up and go deeper into approaches for styling buttons in WordPress block themes. Specifically, we’re going to crack open a fresh theme.json file and break down various approaches to styling buttons in the schema.
Why buttons, you ask? That’s a good question, so let’s start with that.
The different types of buttons
When we’re talking about buttons in the context of the WordPress Block Editor, we have to distinguish between two different types:
Child blocks inside of the Buttons block
Buttons that are nested inside other block (e.g. the Post Comments Form block)
If we add both of these blocks to a template, they have the same look by default.
As we can see, the HTML tag names are different. It’s the common classes — .wp-block-button and .wp-element-button — that ensure consistent styling between the two buttons.
If we were writing CSS, we would target these two classes. But as we know, WordPress block themes have a different way of managing styles, and that’s through the theme.json file. Ganesh also covered this in great detail, and you’d do well giving his article a read.
So, how do we define button styles in theme.json without writing actual CSS? Let’s do it together.
Creating the base styles
theme.json is a structured set of schema written in property:value pairs. The top level properties are called “sections”, and we’re going to work with the styles section. This is where all the styling instructions go.
We’ll focus specifically on the elements in the styles. This selector targets HTML elements that are shared between blocks. This is the basic shell we’re working with:
That button corresponds to HTML elements that are used to mark up button elements on the front end. These buttons contain HTML tags that could be either of our two button types: a standalone component (i.e. the Button block) or a component nested within another block (e.g. the Post Comment block).
Rather than having to style each individual block, we create shared styles. Let’s go ahead and change the default background and text color for both types of buttons in our theme. There’s a color object in there that, in turn, supports background and text properties where we set the values we want:
If crack open DevTools and have a look at the CSS that WordPress generates for the buttons, we see that the .wp-element-button class adds the styles we defined in theme.json:
Those are our default colors! Next, we want to give users visual feedback when they interact with the button.
Implementing interactive button styles
Since this is a site all about CSS, I’d bet many of you are already familiar with the interactive states of links and buttons. We can :hover the mouse cursor over them, tab them into :focus, click on them to make them :active. Heck, there’s even a :visited state to give users a visual indication that they’ve clicked this before.
Notice the “structured” nature of this. We’re basically following an outline:
Elements
Element
Object
Property
Value
We now have a complete definition of our button’s default and interactive styles. But what if we want to style certain buttons that are nested in other blocks?
Styling buttons nested in individual blocks
Let’s imagine that we want all buttons to have our base styles, with one exception. We want the submit button of the Post Comment Form block to be blue. How would we achieve that?
This block is more complex than the Button block because it has more moving parts: the form, inputs, instructive text, and the button. In order to target the button in this block, we have to follow the same sort of JSON structure we did for the button element, but applied to the Post Comment Form block, which is mapped to the core/post-comments-form element:
Notice that we’re no longer working in elements anymore. Instead, we’re working inside blocks which is reserved for configuring actual blocks. Buttons, by contrast, are considered a global element since they can be nested in blocks, even though they are available as a standalone block too.
The JSON structure supports elements within elements. So, if there’s a button element in the Post Comment Form block, we can target it in the core/post-comments-form block:
This selector means that not only are we targeting a specific block — we’re targeting a specific element that is contained in that block. Now we have a default set of button styles that are applied to all buttons in the theme, and a set of styles that apply to specific buttons that are contained in the Post Comment Form block.
The CSS generated by WordPress has a more precise selector as a result:
And what if we want to define different interactive styles for the Post Comment Form button? It’s the same deal as the way we did it for the default styles, only those are defined inside the core/post-comments-form block:
WordPress automagically generates and applies the right classes to output these button styles. But what if you use a “hybrid” WordPress theme that supports blocks and full-site editing, but also contains “classic” PHP templates? Or what if you made a custom block, or even have a legacy shortcode, that contains buttons? None of these are handled by the WordPress Style Engine!
No worries. In all of those cases, you would add the .wp-element-button class in the template, block, or shortcode markup. The styles generated by WordPress will then be applied in those instances.
And there may be some situations where you have no control over the markup. For example, some block plugin might be a little too opinionated and liberally apply its own styling. That’s where you can typically go to the “Advanced” option in the block’s settings panel and apply the class there:
Wrapping up
While writing “CSS” in theme.json might feel awkward at first, I’ve found that it becomes second nature. Like CSS, there are a limited number of properties that you can apply either broadly or very narrowly using the right selectors.
And let’s not forget the three main advantages of using theme.json:
The styles are applied to buttons in both the front-end view and the block editor.
Your CSS will be compatible with future WordPress updates.
The generated styles work with block themes and classic themes alike — there’s no need to duplicate anything in a separate stylesheet.
If you have used theme.json styles in your projects, please share your experiences and thoughts. I look forward to reading any comments and feedback!
We’ve accomplished a bunch of stuff in this series! We created a custom WordPress block that fetches data from an external API and renders it on the front end. Then we took that work and extended it so the data also renders directly in the WordPress block editor. After that, we created a settings UI for the block using components from the WordPress InspectorControls package.
There’s one last bit for us to cover and that’s saving the settings options. If we recall from the last article, we’re technically able to “save” our selections in the block settings UI, but those aren’t actually stored anywhere. If we make a few selections, save them, then return to the post, the settings are completely reset.
Let’s close the loop and save those settings so they persist the next time we edit a post that contains our custom block!
We’re working with an API that provides us with soccer football team ranking and we’re using it to fetch for displaying rankings based on country, league, and season. We can create new attributes for each of those like this:
Next, we need to set the attributes from LeagueSettings.js. Whenever a ComboboxControl is updated in our settings UI, we need to set the attributes using the setAttributes() method. This was more straightfoward when we were only working with one data endpoint. But now that we have multiple inputs, it’s a little more involved.
This is how I am going to organize it. I am going to create a new object in LeagueSettings.js that follows the structure of the settings attributes and their values.
In each of the handle______Change(), I am going to create a setLocalAttributes() that has an argument that clones and overwrites the previous localSettings object with the new country, league, and season values. This is done using the help of the spread operator.
// LeagueSettings.js function handleCountryChange(value) { // Initial code setLocalAttributes({ ...localSettings, country: value }); // Rest of the code } function handleLeagueChange(value) { // Initial code setLocalAttributes({ ...localSettings, league: value }); // Rest of the code } function handleSeasonChange(value) { // Initial code setLocalAttributes({ ...localSettings, season: value }); // Rest of the code }
We can define the setLocalAttributes() like this:
// LeagueSettings.js function setLocalAttributes(value) { let newSettings = Object.assign(localSettings, value); localSettings = { ...newSettings }; setAttributes({ settings: localSettings }); }
So, we’re using Object.assign() to merge the two objects. Then we can clone the newSettings object back to localSettings because we also need to account for each settings attribute when there a new selection is made and a change occurs.
Finally, we can use the setAttributes() as we do normally to set the final object. You can confirm if the above attributes are changing by updating the selections in the UI.
Another way to confirm is to do a console.log() in DevTools to find the attributes.
Look closer at that screenshot. The values are stored in attributes.settings. We are able to see it happen live because React re-renders every time we make a change in the settings, thanks to the useState() hook.
Displaying the values in the blocks settings UI
It isn’t very useful to store the setting values in the control options themselves since each one is dependent on the other setting value (e.g. rankings by league depends on which season is selected). But it is very useful in situations where the settings values are static and where settings are independent of each other.
Without making the current settings complicated, we can create another section inside the settings panel that shows the current attributes. You can choose your way to display the settings values but I am going to import a Tip component from the @wordpress/components package:
// LeagueSettings.js import { Tip } from "@wordpress/components";
While I’m here, I am going to do a conditional check for the values before displaying them inside the Tip component:
Here’s how that winds up working in the block editor:
API data is more powerful when live data can be shown without having to manually update them each and every time. We will look into that in the next installment of this series.
One of the main goals of the WordPress Site Editor (and, yes, that is now the “official” name) is to move basic block styling fromCSS to structured JSON. JSON files are machine-readable, which makes it consumable by the JavaScript-based Site Editor for configuring a theme’s global styles directly in WordPress.
It’s not all the way there yet! If we look at the Twenty Twenty-Two (TT2) default theme, there were two main unresolved issues: styling interactions (like :hover, :active, :focus), and the margins and padding of layout containers. You can see how those were temporarily fixed in the TT2 style.css file rather than making it into the theme.json file.
WordPress 6.1 fixed those issues and what I want to do is look specifically at the latter. Now that we have JSON-ified styles for the margins and padding of layout containers, that opens us up to more flexible and robust ways to define spacing in our theme layouts.
What kind of spacing are we talking about?
First off, we already have root-level padding which is a fancy way of describing padding on the <body> element. That’s nice because it ensures consistent spacing on an element that is shared on all pages and posts.
But there’s more to it because now we have a way for blocks to bypass that padding and align themselves full-width. That’s thanks to padding-aware alignments which is a new opt-in feature in theme.json. So, even if you have root-level padding, you can still allow, say, an image (or some other block) to break out and go full-width.
That gets us to another thing we get: constrained layouts. The idea here is that any blocks nested in the layout respect the layout’s content width — which is a global setting — and do not flow outside of it. We can override that behavior on a block-by-block basis with alignments, but we’ll get to that.
Let’s start with…
Root-level padding
Again, this isn’t new. We’ve had the ability to set padding on the <body> element in theme.json since the experimental Gutenberg plugin introduced it in version 11.7. We set it on the styles.spacing object, where we have margin and padding objects to define the top, right, bottom, and left spacing on the body:
Cool. But herein lies the issue of how in the world we can allow some blocks to break out of that spacing to fill the full screen, edge-to-edge. That’s why the spacing is there, right? It helps prevent that from happening!
But there are indeed plenty of cases where you might want to break out of that spacing on a one-off instance when working in the Block Editor. Say we plop an Image block on a page and we want it to go full-width while the rest of the content respects the root-level padding?
Enter…
Padding-aware alignments
While attempting to create the first default WordPress theme that defines all styles in the theme.json file, lead designer Kjell Reigstad illustrates the challenging aspects of breaking out of root-level padding in this GitHub issue.
Root-level padding prevents blocks from taking up the full viewport width (left). But with padding-aware alignments, some blocks can “opt-out” of that spacing and take up the full viewport width (right). (Image credit: Kjell Reigstad)
New features in WordPress 6.1 were created to address this issue. Let’s dig into those next.
useRootPaddingAwareAlignments
A new useRootPaddingAwareAlignments property was created to address the problem. It was actually first introduced in the Gutenberg plugin v13.8. The original pull request is a nice primer on how it works.
Right off the bat, notice that this is a feature we have to opt into. The property is set to false by default and we have to explicitly set it to true in order to enable it. Also notice that we have appearanceTools set to true as well. That opts us into UI controls in the Site Editor for styling borders, link colors, typography, and, yes, spacing which includes margin and padding.
When we do enable useRootPaddingAwareAlignments, we are provided with custom properties with root padding values that are set on the <body> element on the front end. Interestingly, it also applies the padding to the .editor-styles-wrapper class so the spacing is displayed when working in the back-end Block Editor. Pretty cool!
I was able to confirm those CSS custom properties in DevTools while digging around.
Enabling useRootPaddingAwareAlignments also applies left and right padding to any block that supports the “content” width and “wide” width values in the Global Styles image above. We can also define those values in theme.json:
If the Global Styles settings are different than what is defined in theme.json, then the Global Styles take precedence. You can learn all about managing block theme styles in my last article.
contentSize is the default width for blocks.
wideSize provides a “wide” layout option and establishes a wider column for blocks to stretch out.
So, that last code example will give us the following CSS:
/* The default content container */ .wp-container-[id] > * { max-width: 640px; margin-left: auto !important; margin-right: auto !important; } /* The wider content container */ .wp-container-[id] > .alignwide { max-width: 1000px; }
[id] indicates a unique number automatically generated by WordPress.
But guess what else we get? Full alignment as well!
See that? By enabling useRootPaddingAwareAlignments and defining contentSize and wideSize, we also get a full alignment CSS class for a total of three container configurations for controlling the width of blocks that are added to pages and posts.
This applies to the following layout-specific blocks: Columns, Group, Post Content, and Query Loop.
Block layout controls
Let’s say we add any of those aforementioned layout-specific blocks to a page. When we select the block, the block settings UI offers us new layout settings based on the settings.layout values we defined in theme.json (or the Global Styles UI).
We’re dealing with very specific blocks here — ones that can have other blocks nested inside. So, these Layout settings are really about controlling the width and alignment of those nested blocks. The “Inner blocks use content width” setting is enabled by default. If we toggle it off, then we have no max-width on the container and the blocks inside it go edge-to-edge.
If we leave the toggle on, then nested blocks will adhere to either the contentWidth or wideWidth values (more on that in a bit). Or we can use the numeric inputs to define custom contentWidth and wideWidth values in this one-off instance. That’s great flexibility!
Wide blocks
The settings we just looked are set on the parent block. Once we’ve nested a block inside and select it, we have additional options in that block to use the contentWidth, wideWidth, or go full-width.
This Image block is set to respect the contentWidth setting, labeled “None” in the contextual menu, but can also be set to wideWidth (labeled “Wide width”) or “Full width”.
Notice how WordPress multiplies the root-level padding CSS custom properties by -1 to create negative margins when selecting the “Full width” option.
The .alignfull class sets negative margins on a nested block to ensure it takes up the full viewport width without conflicting with the root-level padding settings.
Using a constrained layout
We just covered the new spacing and alignments we get with WordPress 6.1. Those are specific to blocks and any nested blocks within blocks. But WordPress 6.1 also introduces new layout features for even more flexibility and consistency in a theme’s templates.
Case in point: WordPress has completely restructured its Flex and Flow layout types and gave us a constrainedlayout type that makes it easier to align block layouts in themes using the content width settings in the Site Editor’s Global Styles UI.
Flex, Flow, and Constrained layouts
The difference between these three layout types is the styles that they output. Isabel Brison has an excellent write-up that nicely outlines the differences, but let’s paraphrase them here for reference:
Flow layout: Adds vertical spacing between nested blocks in the margin-block direction. Those nested blocks can also be aligned to the left, right, or center.
Constrained layout: Same exact deal as a Flow layout, but with width constraints on nested blocks that are based on the contentWidth and wideWidth settings (either in theme.json or Global Styles).
Flex layout: This was unchanged in WordPress 6.1. It uses CSS Flexbox to create a layout that flows horizontally (in a row) by default, but can flow vertically as well so blocks stack one on top of another. Spacing is applied using the CSS gap property.
This new slate of layout types creates semantic class names for each layout:
Updating your theme to support constrained layouts
If you’re already using a block theme of your own making, you’re going to want to update it to support constrained layouts. All it takes is swapping out a couple of things in theme.json:
These are recently released block themes that have enabled spacing settings with useRootPaddingAwareAlignments and have an updated theme.json file that defines a constrained layout:
The base layout styles are default features that ship in WordPress 6.1 Core. In other words, they’re enabled right out of the box. But we can disable them if we need to with this little snippet in functions.php:
Big warning here: disabling support for the default layout types also removes all of the base styling for those layouts. That means you’ll need to roll your own styles for spacing, alignments, and anything else needed to display content in different template and block contexts.
Wrapping up
As a great fan of full-width images, the new contained WordPress 6.1 layout and padding aware alignment features are two of my most favorites yet. Taken together with other tools including, better margin and padding control, fluid typography, and updated List and Quote blocks, among others, is solid proof that WordPress is moving towards a better content creation experience.
Now, we have to wait and look at how the imagination and creativity of ordinary designers and content creators use these incredible tools and take it to a new level.
I consulted and referenced a lot of sources while digging into all of this. Here’s a big ol’ list of things I found helpful and think you might enjoy as well.
So far, we’ve covered how to work with data from an external API in a custom WordPress block. We walked through the process of fetching that data for use on the front end of a WordPress site, and how to render it directly in the WordPress Block Editor when placing the block in content. This time, we’re going to bridge those two articles by hooking into the block editor’s control panel to create a settings UI for the block we made.
You know the control panel I’m referring to, right? It’s that panel on the right that contains post and block settings in the block editor.
See that red highlighted area? That’s the control panel. A Paragraph block is currently selected and the settings for it are displayed in the panel. We can change styles, color, typography… a number of things!
Well, that’s exactly what we’re doing this time around. We’re going to create the controls for the settings of the Football Rankings block we worked on in the last two articles. Last time, we made a button in our block that fetches the external data for the football rankings. We already knew the URL and endpoints we needed. But what if we want to fetch ranking for a different country? Or maybe a different league? How about data from a different season?
We need form controls to do that. We could make use of interactive React components — like React-Select — to browse through the various API options that are available to parse that data. But there’s no need for that since WordPress ships with a bunch of core components that we hook right into!
Before we hook into anything, it’s a good idea to map out what it is we need in the first place. I’ve mapped out the structure of the RapidAPI data we’re fetching so we know what’s available to us:
Seasons and countries are two top-level endpoints that map to a leagues endpoint. From there, we have the rest of the data we’re already using to populate the rankings table. So, what we want to do is create settings in the WordPress Block Editor that filter the data by Season, Country, and League, then pass that filtered data into the rankings table. That gives us the ability to drop the block in any WordPress page or post and display variations of the data in the block.
In order to get the standings, we need to first get the leagues. And in order to get the leagues, we first need to get the countries and/or the seasons. You can view the various endpoints in the RapidAPI dashboard.
There are different combinations of data that we can use to populate the rankings, and you might have a preference for which data you want. For the sake of this article, we are going to create the following options in the block settings panel:
Choose Country
Choose League
Choose Season
Then we’ll have a button to submit those selections and fetch the relevant data and pass them into the rankings table.
Load and store a list of countries
We can’t select which country we want data for if we don’t have a list of countries to choose from. So, our first task is to grab a list of countries from RapidAPI.
The ideal thing is to fetch the list of countries when the block is actually used in the page or post content. There’s no need to fetch anything if the block isn’t in use. The approach is very similar to what we did in the first article, the difference being that we are using a different API endpoint and different attributes to store the list of returned countries. There are other WordPress ways to fetch data, like api-fetch, but that‘s outside the scope of what we’re doing here.
We can either include the country list manually after copying it from the API data, or we could use a separate API or library to populate the countries. But the API we’re using already has a list of countries, so I would just use one of its endpoints. Let’s make sure the initial country list loads when the block is inserted into the page or post content in the block editor:
We have a state variable to store the list of countries. Next, we are going to import a component from the @wordpress/block-editor package called InspectorControls which is where all of the components we need to create our settings controls are located.
import { InspectorControls } from "@wordpress/block-editor";
The package’s GitHub repo does a good job explaining InspectorControls. In our example, we can use it to control the API data settings like Country, League, and Season. Here’s a preview so that you get an idea of the UI we’re making:
Here, I am making sure that we are using conditional rendering so that the function only loads the component after the list of countries is loaded. If you’re wondering about that LeagueSettings component, it is a custom component I created in a separate components subfolder in the block so we can have a cleaner and more organized Edit function instead of hundreds of lines of country data to deal with in a single file.
We can import it into the edit.js file like this:
import { LeagueSettings } from "./components/LeagueSettings";
Next, we’re passing the required props to the LeagueSettings component from the parent Edit component so that we can access the state variables and attributes from the LeagueSettings child component. We can also do that with other methods like the Context API to avoid prop drilling, but what we have right now is perfectly suitable for what we’re doing.
The other parts of the Edit function can also be converted into components. For example, the league standings code can be put inside a separate component — like maybe LeagueTable.js — and then imported just like we imported LeagueSettings into the Edit function.
Inside the LeagueSettings.js file
LeagueSettings is just like another React component from which we can destructure the props from the parent component. I am going to use three state variables and an additional leagueID state because we are going to extract the ID from the league object:
There are other panel tags and attributes — it’s just my personal preference to use these ones. None of the others are required… but look at all the components we have available to make a settings panel! I like the simplicity of the PanelBody for our use case. It expands and collapses to reveal the dropdown settings for the block and that’s it.
Speaking of which, we have a choice to make for those selections. We could use the SelectControl component or a ComboBoxControl, which the docs describe as “an enhanced version of a SelectControl, with the addition of being able to search for options using a search input.” That’s nice for us because the list of countries could get pretty long and users will be able to either do a search query or select from a list.
Here’s an example of how a ComboboxControl could work for our country list:
When a country is selected from the ComboboxControl, the country value changes and we filter the data accordingly:
function handleCountryChange(value) { // Set state of the country setCountry(value); // League code from RapidAPI const options = { method: "GET", headers: { "X-RapidAPI-Key": "Your RapidAPI key", "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com", }, }; fetch(`https://api-football-v1.p.rapidapi.com/v3/leagues?country=$ {value}`, options) .then((response) => response.json()) .then((response) => { return response.response; }) .then((leagueOptions) => { // Set state of the league variable setLeague(leagueOptions); // Convert it as we did for Country options setupLeagueSelect = leagueOptions.map((league) => { return { label: league.league.name, value: league.league.name, }; }); setFilteredLeagueOptions(setupLeagueSelect); }) .catch((err) => console.error(err)); }
Note that I am using another three state variables to handle changes when the country selection changes:
I will show the code that I used for the other settings but all it does is take normal cases into account while defining errors for special cases. For example, there will be errors in some countries and leagues because:
there are no standings for some leagues, and
some leagues have standings but they are not in a single table.
This isn’t a JavaScript or React tutorial, so I will let you handle the special cases for the API that you plan to use:
In the last article, we made a button in the block editor that fetches fresh data from the API. There’s no more need for it now that we have settings. Well, we do need it — just not where it currently is. Instead of having it directly in the block that’s rendered in the block editor, we’re going to move it to our PanelBody component to submit the settings selections.
So, back in LeagueSettings.js:
// When countriesList is loaded, show the country combo box { countriesList && ( <ComboboxControl label="Choose country" value={country} options={filteredCountryOptions} onChange={(value) => handleCountryChange(value)} onInputChange={(inputValue) => { setFilteredCountryOptions( setupCountrySelect.filter((option) => option.label .toLowerCase() .startsWith(inputValue.toLowerCase()) ) ); }} /> )} // When filteredLeagueOptions is set through handleCountryChange, show league combobox { filteredLeagueOptions && ( <ComboboxControl label="Choose league" value={league} options={filteredLeagueOptions} onChange={(value) => handleLeagueChange(value)} onInputChange={(inputValue) => { setFilteredLeagueOptions( setupLeagueSelect.filter((option) => option.label .toLowerCase() .startsWith(inputValue.toLowerCase()) ) ); }} /> )} // When filteredSeasonOptions is set through handleLeagueChange, show season combobox { filteredSeasonOptions && ( <> <ComboboxControl label="Choose season" value={season} options={filteredSeasonOptions} onChange={(value) => handleSeasonChange(value)} onInputChange={ (inputValue) => { setFilteredSeasonOptions( setupSeasonSelect.filter((option) => option.label .toLowerCase() .startsWith(inputValue.toLowerCase() ) ); } } /> // When season is set through handleSeasonChange, show the "Fetch data" button { season && ( <button className="fetch-data" onClick={() => getData()}>Fetch data</button> ) } </> </> )}
Here’s the result!
We’re in a very good place with our block. We can render it in the block editor and the front end of the site. We can fetch data from an external API based on a selection of settings we created that filters the data. It’s pretty darn functional!
But there’s another thing we have to tackle. Right now, when we save the page or post that contains the block, the settings we selected for the block reset. In other words, those selections are not saved anywhere. There’s a little more work to make those selections persistent. That’s where we plan to go in the next article, so stay tuned.
The way we write CSS for WordPress themes is in the midst of sweeping changes. I recently shared a technique for adding fluid type support in WordPress by way of theme.json, a new file that WordPress has been pushing hard to become a central source of truth for defining styles in WordPress themes that support full-site editing (FSE) features.
Wait, no style.css file? We still have that. In fact, style.css is still a required file in block themes, though its role is greatly reduced to meta information used for registering the theme. That said, the fact is that theme.json is still in active development, meaning we’re in a transitional period where you might find styles defined there, in styles.css or even at the block level.
So, what does styling actually look like in these WordPress FSE days? That’s what I want to cover in this article. There’s a lack of documentation for styling block themes in the WordPress Theme Developer Handbook, so everything we’re covering here is what I’ve gathered about where things currently are as well as discussions about the future of WordPress theming.
The evolution of WordPress styles
The new developmental features that are included in WordPress 6.1 get us closer to a system of styles that are completely defined in theme.json, but there is still be plenty of work to do before we can fully lean on it. One way we can get an idea of what’s coming in future releases is by using the Gutenberg plugin. This is where experimental features are often given a dry run.
Another way we can get a feel for where we are is by looking at the evolution of default WordPress themes. To date, there are three default themes that support full-site editing:
Twenty Twenty-One (TT1): This is the first classic version of a block-compatible default theme. There is also a block version (TT1 blocks) and has since been a go-to resource for block theming. However, all 5,900 lines of CSS in TT1 is in style.css. There is no theme.json file. TT1 Blocks is the first look we got at styling in the Block Editor era, and we can consider it a teaser more than a model.
Twenty Twenty-Two (TT2): This is the first true block-based default WordPress theme, and it’s where we first meet theme.json. The file contains only 373 lines of code. Its lead developers had made concerted efforts to make this a CSS-less theme; however, style.css still shipped with just under 150 lines of code since not all of the issues with theme.json were resolved in the experimental Gutenberg plugin ahead of release.
But don’t start trading the CSS in style.css for JSON property-value pairs in theme.json just yet. There are still CSS styling rules that need to be supported in theme.json before we think about doing that. The remaining significant issues are currently being discussed with an aim to fully move all the CSS style rules into theme.json and consolidate different sources of theme.json into a UI for for setting global styles directly in the WordPress Site Editor.
The Global Styles UI is integrated with the right panel in the Site Editor. (Credit: Learn WordPress)
That leaves us in a relatively tough spot. Not only is there no clear path for overriding theme styles, but it’s unclear where the source of those styles even come from — is it from different layers of theme.json files, style.css, the Gutenberg plugin, or somewhere else?
Why theme.json instead of style.css?
You might be wondering why WordPress is moving toward a JSON-based definition of styles instead of a traditional CSS file. Ben Dwyer from the Gutenberg development team eloquently articulates why the theme.json approach is a benefit for theme developers.
It’s worth reading Ben’s post, but the meat is in this quote:
Overriding CSS, whether layout, preset, or block styles, presents an obstacle to integration and interoperability: visual parity between the frontend and editor becomes more difficult to maintain, upgrades to block internals may conflict with overrides. Custom CSS is, furthermore, less portable across other block themes.
By encouraging theme authors to use theme.json API where possible, the hierarchy of “base > theme > user” defined styles can be resolved correctly.
One of the major benefits of moving CSS to JSON is that JSON is a machine-readable format, which means it can be exposed in the WordPress Site Editor UI by fetching an API, thus allowing users to modify default values and customize a site’s appearance without writing any CSS at all. It also provides a way to style blocks consistently, while providing a structure that creates layers of specificity such that the user settings take the highest priority over those defined in theme.json. That interplay between theme-level styles in theme.json and the user-defined styles in the Global Styles UI is what makes the JSON approach so appealing.
Developers maintain consistency in JSON, and users gain flexibility with code-less customizations. That’s a win-win.
We’ve already seen a lot of progress as far as what parts of a theme theme.json is capable of styling. Prior to WordPress 6.1, all we could really do was style headings and links. Now, with WordPress 6.1, we can add buttons, captions, citations, and headings to the mix.
And we do that by defining JSON elements. Think of elements as individual components that live in a WordPress block. Say we have a block that contains a heading, a paragraph, and a button. Those individual pieces are elements, and there’s an elements object in theme.json where we define their styles:
A better way to describe JSON elements is as low-level components for themes and blocks that do not need the complexity of blocks. They are representations of HTML primitives that are not defined in a block but can be used across blocks. How they are supported in WordPress (and the Gutenberg plugin) is described in the Block Editor Handbook and this Full Site Editing tutorial by Carolina Nymark.
For example, links are styled in the elements object but are not a block in their own right. But a link can be used in a block and it will inherit the styles defined on the elements.link object in theme.json. This doesn’t fully encapsulate the definition of an element, though, as some elements are also registered as blocks, such as the Heading and Button blocks — but those blocks can still be used within other blocks.
Here is a table of the elements that are currently available to style in theme.json, courtesy of Carolina:
Element
Selector
Where it’s supported
link
<a>
WordPress Core
h1 – h6
The HTML tag for each heading level: <h1>, <h2>, <h3>, <h4>, <h5> and <h6>
WordPress Core
heading
Styles all headings globally by individual HTML tag: <h1>, <h2>, <h3>, <h4>, <h5> and <h6>
As you can see, it’s still early days and plenty still needs to move from the Gutenberg plugin into WordPress Core. But you can see how quick it would be to do something like style all headings in a theme globally without hunting for selectors in CSS files or DevTools.
Further, you can also start to see how the structure of theme.json sort of forms layers of specificity, going from global elements (e.g. headings) to individual elements (e.g. h1), and block-level styles (e.g. h1 contained in a block).
Let’s keep talking about CSS specificity. I mentioned earlier that the JSON approach to styling establishes a hierarchy. And it’s true. Styles that are defined on JSON elements in theme.json are considered default theme styles. And anything that is set by the user in the Global Styles UI will override the defaults.
In other words: user styles carry more specificity than default theme styles. Let’s take a look at the Button block to get a feel for how this works.
I’m using Emptytheme, a blank WordPress theme with no CSS styling. I’m going to add a Button block on a new page.
The background color, text color, and rounded borders come from the block editor’s default settings.
OK, we know that WordPress Core ships with some light styling. Now, I’m going to switch to the default TT3 theme from WordPress 6.1 and activate it. If I refresh my page with the button, the button changes styles.
The background color, text color, and rounded corner styles have changed.
You can see exactly where those new styles are coming from in TT3’s theme.json file. This tells us that the JSON element styles take precedence over WordPress Core styles.
Now I am going to modify TT3 by overriding it with a theme.json file in a child theme, where the default background color of the Button block is set to red.
The default style for the Button block has been updated to red.
But notice the search button in that last screenshot. It should be red, too, right? That must mean it is styled at another level if the change I made is at the global level. If we want to change both buttons, we could do it at the user level using the Global Styles UI in the site editor.
We changed the background color of both buttons to blue and modified the text as well using the Global styles UI. Notice that the blue from there took precedence over the theme styles!
The Style Engine
That’s a very quick, but good, idea of how CSS specificity is managed in WordPress block themes. But it’s not the complete picture because it’s still unclear where those styles are generated. WordPress has its own default styles that come from somewhere, consumes the data in theme.json for more style rules, and overrides those with anything set in Global Styles.
Are those styles inline? Are they in a separate stylesheet? Maybe they’re injected on the page in a <script>?
That’s what the new Style Engine is hopefully going to solve. The Style Engine is a new API in WordPress 6.1 that is meant to bring consistency to how styles are generated and where styles are applied. In other words, it takes all of the possible sources of styling and is singularly responsible for properly generating block styles. I know, I know. Yet another abstraction on top of other abstractions just to author some styles. But having a centralized API for styles is probably the most elegant solution given that styles can come from a number of places.
We’re only getting a first look at the Style Engine. In fact, here’s what has been completed so far, according to the ticket:
Audit and consolidate where the code generates block support CSS in the back end so that they are delivered from the same place (as opposed to multiple places). This covers CSS rules such as margin, padding, typography, colors, and borders.
Remove repetitive layout-specific styles and generate semantic class names.
Reduce the number of inline style tags we print to the page for block, layout, and element support.
Basically, this is the foundation for establishing a single API that contains all the CSS style rules for a theme, wherever they come from. It cleans up the way WordPress would inject inline styles pre-6.1 and establishes a system for semantic class names.
We talked a bit about JSON elements in the theme.json file and how they are basically HTML primitives for defining default styles for things like headings, buttons, and links, among others. Now, let’s look at actually using a JSON element and how it behaves in various styling contexts.
JSON elements generally have two contexts: the global level and the block level. The global level styles are defined with less specificity than they are at the block level to ensure that block-specific styles take precedence for consistency wherever blocks are used.
Global styles for JSON elements
Let’s look at the new default TT3 theme and examine how its buttons are styled. The following is an abbreviated copy-paste of the TT3 theme.json file (here’s the full code) showing the global styles section, but you can find the original code here.
All buttons are styled at the global level (styles.elements.button).
Every button in the Twenty Twenty-Three theme shares the same background color, which is set to the --wp--preset--color--primary CSS variable, or #B4FD55.
We can confirm this in DevTools as well. Notice that a class called .wp-element-button is the selector. The same class is used to style the interactive states as well.
Again, this styling is all happening at the global level, coming from theme.json. Whenever we use a button, it is going to have the same background because they share the same selector and no other style rules are overriding it.
As an aside, WordPress 6.1 added support for styling interactive states for certain elements, like buttons and links, using pseudo-classes in theme.json — including :hover, :focus, and :active — or the Global Styles UI. Automattic Engineer Dave Smithdemonstratesthis feature in a YouTube video.
We could override the button’s background color either in theme.json (preferably in a child theme since we’re using a default WordPress theme) or in the Global Styles settings in the site editor (no child theme needed since it does not require a code change).
But then the buttons will change all at once. What if we want to override the background color when the button is part of a certain block? That’s where block-level styles come into play.
Block-level styles for elements
To understand how we can use and customize styles at the block level, let’s change the background color of the button that is contained in the Search block. Remember, there is a Button block, but what we’re doing is overriding the background color at the block level of the Search block. That way, we’re only applying the new color there as opposed to applying it globally to all buttons.
To do that, we define the styles on the styles.blocks object in theme.json. That’s right, if we define the global styles for all buttons on styles.elements, we can define the block-specific styles for button elements on styles.block, which follows a similar structure:
That sets the color for all headings to the preset base color in WordPress. Let’s change the color and font size of Heading 2 elements at the global level as well:
Now, all Heading 2 elements are set to be the primary preset color with a fluid font size. But maybe we want a fixed fontSize for the Heading 2 element when it is used in the Query Look block:
Now we have three levels of styles for Heading 2 elements: all headings, all Heading 2 elements, and Heading 2 elements that are used in the Query Loop block.
Existing theme examples
While we only looked at the styling examples for buttons and headings in this article, WordPress 6.1 supports styling additional elements. There’s a table outlining them in the “Defining styles with JSON elements” section.
You’re probably wondering which JSON elements support which CSS properties, not to mention how you would even declare those. While we wait for official documentation, the best resources we have are going to be the theme.json files for existing themes. I’m going to provide links to themes based on the elements they customize, and point out what properties are customized.
It’s easy to get lost while trying to get around the Site Editor unless you are working day and night inside the tool. The navigation is jumpy and confusing, especially when going from template browsing to template editing to modifying individual blocks.
Even as a keen early rider in the world of Gutenberg block editor and block-eye themes, I do have tons of my own frustrations. I’m optimistic, though, and anticipate that the site editor, once completed, will be a revolutionary tool for users and techno-savvy theme developers alike. This hopeful tweet already confirms that. In the meantime, it seems that we should be preparing for more changes, and perhaps even a bumpy ride.
References
I’m listing all of the resources I used while researching information for this article.
Fluid typography is a fancy way of “describing font properties, such as size or line height, that scale fluidly according to the size of the viewport”. It’s also known by other names, like responsive typography, flexible type, fluid type, viewport sized typography, fluid typography, and even responsive display text.
Here is an example of fluid typography that you can play live (courtesy of MDN documentation). CSS-Tricks has covered fluid typography extensively as well. But the point here is not to introduce you to fluid typography, but how to use it. More specifically, I want to show you how to implement fluid typography in WordPress 6.1 which recently introduced a fluid type feature directly in the WordPress Block Editor.
Open up your style.css file, slap in a style rule with fancy clamp()-ing on the font-size property, and good to go, right? Sure, that’ll give you fluid text, but to get Block Editor controls that make it possible to apply fluid type anywhere on your WordPress site? That requires a different approach in these block-ified days.
Fluid typography support in Gutenberg
Some WordPress theme developers have been using the clamp() function to define a fluid font-size, in their WordPress themes, even in newer “block” themes such as Twenty Twenty-Two, Twenty Twenty-Three, and others.
But the Gutenberg plugin — the one that contains experimental development for WordPress Block and Site Editor features — introduced support for fluid typography starting in version 13.8. That opened the door for implementing at a theme level so that fluid type can be applied to specific elements and blocks directly in the Block Editor. CSS-Tricks was even given a shout-out in the Pull Request that merged the feature.
That work became part of WordPress Core in WordPress 6.1. Rich Tabor, one of the earlier advocates of fluid typography in the Block Editor says:
[Fluid typography] is also a part of making WordPress more powerful, while not more complicated (which we all know is quite the challenge). […] Fluid typography just works. Actually, I think it works great.
This Make WordPress post highlights the approach taken to support the feature at the block level so that a fluid font size is applied to blocks dynamically by default. There are some benefits to this, of course:
It provides a way for theme authors to activate fluid typography without worrying about implementing it in code.
It applies fluid typography to specific typographical entities, such as elements or blocks in a maintainable and reusable way.
It allows flexibility in terms of font size units (e.g. px, rem, em, and %).
Now that this new feature is available in the WordPress Block Editor by default, theme authors can apply uniform fluid typography without writing additional code.
Blocks that support typography and spacing settings
Gutenberg 14.1 released on September 16, 2022, and introduced typographic settings on a bunch of blocks. That means the text settings for those blocks were set in CSS before and had to be changed in CSS as well. But those blocks now provide font and spacing controls in the Block Editor interface.
That work is currently slated to be added to WordPress 6.1, as detailed in this Make WordPress blog post. And with it is an expanded number of blocks that with typography support.
WordPress blocks that will support typography settings in the upcoming WordPress 6.1 release.
Declaring fluid type in a WordPress block theme
So, how do we put this new fluid typography to use in WordPress? The answer is in theme.json, a new-ish file specific to block themes that contains a bunch of theme configurations in key:value pairs.
Let’s look at a rule for a large font in theme.json where contentSize: 768px and we’re working with a widesize: 1600px layout. This is how we can specify a CSS font-size using the clamp() function:
As of WordPress 6.1, only rem, em and px units are supported.
That’s great and works, but with the new fluid type feature we would actually use a different approach. First, we opt into fluid typography on settings.typography, which has a new fluid property:
"settings": { "typography": { "fluid": true } }
Then we specify our settings.fontSizes like before, but with a new fluidSize property where we can set the min and max size values for our fluid type range.
That’s really it. We just added fluid type to a font size called “Large” with a range that starts at 2.25rem and scales up to 3rem. Now, we can apply the “Large” font to any block with font settings.
…which is applied to the element, say a Paragraph Block:
<p class="has-large-font-size">...</p>
Initially, I found it hard to understand and wrap around in my mind the concept of the CSS clamp() function without also learning about the min(), max(), and calc() functions. This calculator tool helped me quite a bit, especially for determining which values to use in my own theme projects.
For demonstration purposes, let’s use the calculator to define our font-size range so that the size is 36px at a 768px viewport width and 48px at a 1600px viewport width.
The calculator automatically generates the following CSS:
So, those minimum and maximum values correspond to the the fluidSize.min and fluidSize.max values in theme.json. The min value is applied at viewports that are 768px wide and below. Then the font-size scales up as the viewport width grows. If the viewport is wider than 1600px, the max is applied and the font-size is capped there.
Let’s start with Justin’s set of instructions. I used in a modified version of the default Twenty Twenty-Three theme that is currently under development.
First, let’s make sure we’re running the Gutenberg plugin (13.8 and up) or WordPress 6.1, then opt into fluid type on the settings.typography.fluid property in the theme.json file:
If everything is working correctly, we can now head into the WordPress Block Editor and apply the “Normal” font setting to our block:
Nice! And if we save and inspect that element on the front end, this is the markup:
Very good. Now let’s make sure the CSS is actually there:
Good, good. Let’s expose that CSS custom property to see if it’s really clampin’ things:
Looks like everything is working just as we want it! Let’s look at another example…
Example 2: Excluding a font setting from fluid type
This time, let’s follow the instructions from the merged Pull Request with a nod to this example by Carolina Nymark that shows how we can disable fluid type on a specific font setting.
I used the empty theme as advised in the instructions and opened up the theme.json file for testing. First, we opt into fluid type exactly as we did before:
Notice that we’ve applied the fluid type feature only on the “Normal”, “Medium”, and “Large” font settings. “Extra Large” is the odd one out where the fluid object is set to false.
What WordPress does from here — via the Gutenberg style engine — is apply the properties we set into CSS clamp() functions for each font size setting that has opted into fluid type and a single size value for the Extra Large setting:
Good start! Let’s confirm that the .has-x-large-font-size class is excluded from fluid type:
If we expose the --wp--preset--font-size--x-large variable, we’ll see it’s set to 2.25rem.
That’s exactly what we want!
Block themes that support fluid typography
Many WordPress themes already make use of the clamp() function for fluid type in both block and classic themes. A good example of fluid typography use is the recently released Twenty Twenty-Three default theme.
I’ve reviewed all the block themes from WordPress Block Theme directory, examining theme.json file of each theme and to see just how many block themes currently support fluid typography — not the new feature since it’s still in the Gutenberg plugin as of this writing — using the CSS clamp() function. Of the 146 themes I reviewed, the majority of them used a clamp() function to define spacing. A little more than half of them used clamp() to define font sizes. The Alara theme is the only one to use clamp() for defining the layout container sizes.
Understandably, only a few recently released themes contain the new fluid typography feature. But here are the ones I found that define it in theme.json:
And if you read my previous post here on CSS-Tricks, the TT2 Gopher Blocks theme I used for the demo has also been updated to support the fluid typography feature.
Selected reactions to the WordPress fluid typography features
Having fluid typography in WordPress at the settings level is super exciting! I thought I’d share some thoughts from folks in the WordPress developer community who have commented on it.
Matias Ventura, the lead architect of the Gutenberg project:
One of my favorite latest Gutenberg features is fluid type that — hopefully — "just works". It's available for testing in 13.8. pic.twitter.com/9SLUgEg4pF
As one of the bigger efforts towards making publishing beautifully rich pages in WordPress, fluid typography is a pretty big experience win for both the folks building with WordPress — and those consuming the content.
Contrast that idea with font sizes that respond to specific viewport sizes, such as those defined by media queries, but do nothing in between those sizes. theme.json already allows authors to insert their own fluid font size values. This won’t change, but this PR offers it to folks who don’t want to worry about the implementation details.
Brian Garner, designer and principal developer advocate at WPEngine:
Fluid Typography is a huge improvement to #WordPress. FSE themes can experimentally opt into this via the Gutenberg plugin, but we’ll see it officially supported in 6.1 this fall. Here’s how it looks/works: pic.twitter.com/Jc7gHdyz5R
A few developers think some features should be an opt-in. Jason Crist of Automattic says:
I love the power of fluid typography, however I also don’t believe that it should just be enabled by default. It’s usage (and the details of it) are important design decisions that should be made carefully.
You can also find a bunch more comments in the official testing instructions for the feature.
Wrapping up
The fluid typography feature in WordPress is still in active development at the time of this writing. So, right now, theme authors should proceed to use it, but with caution and expect some possible changes before it is officially released. Justin cautions theme authors using this feature and suggests to keep eye on the following two GitHub issues:
Let’s acknowledge that developing for WordPress is weird right now. Whether you’re new to WordPress or have worked with it for eons, the introduction of “Full-Site Editing” (FSE) features, including the Block Editor (WordPress 5.0) and the Site Editor (WordPress 5.9), have upended the traditional way we build WordPress themes and plugins.
Even though it’s been five years since we met the Block Editor for the first time, developing for it is difficult because documentation is either lacking or outdated. That’s more of a statement on how fast FSE features are moving, something Geoff lamented in a recent post.
Case in point: In 2018, an introductory series about getting into Gutenberg development was published right here on CSS-tricks. Times have changed since then, and, while that style of development does still work, it is not recommended anymore (besides, the create-guten-block project it’s based on is also no longer maintained).
In this article, I intend to help you get started with WordPress block development in a way that follows the current methodology. So, yes, things could very well change after this is published. But I’m going to try and focus on it in a way that hopefully captures the essence of block development, because even though the tools might evolve over time, the core ideas are likely to remain the same.
The Gutenberg Editor: (1) The block inserter, (2) the content area, and (3) the settings sidebar Credit: WordPress Block Editor Handbook
What are WordPress blocks, exactly?
Let’s start by airing out some confusion with what we mean by terms like blocks. All of the development that went into these features leading up to WordPress 5.0 was codenamed “Gutenberg” — you know, the inventor of the printing press.
Since then, “Gutenberg” has been used to describe everything related to blocks, including the Block Editor and Site Editor, so it’s gotten convoluted to the extent that some folks depise the name. To top it all off, there’s a Gutenberg plugin where experimental features are tested for possible inclusion. And if you think calling all of this “Full-Site Editing” would solve the issue, there are concerns with that as well.
So, when we refer to “blocks” in this article what we mean are components for creating content in the WordPress Block Editor. Blocks are inserted into a page or post and provide the structure for a particular type of content. WordPress ships with a handful of “core” blocks for common content types, like Paragraph, List, Image, Video, and Audio, to name a few.
Apart from these core blocks, we can create custom blocks too. That is what WordPress block development is about (there’s also filtering core blocks to modify their functionality, but you likely won’t be needing that just yet).
What blocks do
Before we dive into creating blocks, we must first get some sense of how blocks work internally. That will definitely save us a ton of frustration later on.
The way I like to think about a block is rather abstract: to me, a block is an entity, with some properties (called attributes), that represents some content. I know this sounds pretty vague, but stay with me. A block basically manifests itself in two ways: as a graphical interface in the block editor or as a chunk of data in the database.
When you open up the WordPress Block Editor and insert a block, say a Pullquote block, you get a nice interface. You can click into that interface and edit the quoted text. The Settings panel to the right side of the Block Editor UI provides options for adjusting the text and setting the block’s appearance.
When you are done creating your fancy pullquote and hit Publish, the entire post gets stored in the database in the wp_posts table. This isn’t anything new because of Gutenberg. That’s how things have always worked — WordPress stores post content in a designated table in the database. But what’s new is that a representation of the Pullquote block is part of the content that gets stored in post_content field of the wp_posts table.
What does this representation look like? Have a look:
<!-- wp:pullquote {"textAlign":"right"} --> <figure class="wp-block-pullquote has-text-align-right"> <blockquote> <p>It is not an exaggeration to say that peas can be described as nothing less than perfect spheres of joy.</p> <cite>The Encyclopedia of world peas</cite> </blockquote> </figure> <!-- /wp:pullquote -->
Looks like plain HTML, right?! This, in technical lingo, is the “serialized” block. Notice the JSON data in the HTML comment, "textAlign": "right". That’s an attribute — a property associated with the block.
Let’s say that you close the Block Editor, and then some time later, open it again. The content from the relevant post_content field is retrieved by the Block Editor. The editor then parses the retrieved content, and wherever it encounters this:
OK, that seems like a Pullquote block to me. Hmm.. it’s got an attribute too… I do have a JavaScript file that tells me how to construct the graphical interface for a Pullquote block in the editor from its attributes. I should do that now to render this block in all its glory.
As a block developer, your job is to:
Tell WordPress that you want to register a specific type of block, with so-and-so details.
Provide the JavaScript file to the Block Editor that will help it render the block in the editor while also “serializing” it to save it in the database.
Provide any additional resources the block needs for its proper functionality, e.g. styles and fonts.
One thing to note is that all of this conversion from serialized data to graphical interface — and vice versa — takes place only in the Block Editor. On the front end, the content is displayed exactly the way it is stored. Therefore, in a sense, blocks are a fancy way of putting data in the database.
Hopefully, this gives you some clarity as to how a block works.
Blocks are just plugins
Blocks are just plugins. Well, technically, you can put blocks in themes and you can put multiple blocks in a plugin. But, more often than not, if you want to make a block, you’re going to be making a plugin. So, if you’ve ever created a WordPress plugin, then you’re already part-way there to having a handle on making a WordPress block.
But let’s assume for a moment that you’ve never set up a WordPress plugin, let alone a block. Where do you even start?
Setting up a block
We have covered what blocks are. Let’s start setting things up to make one.
Make sure you have Node installed
This will give you access to npm and npx commands, where npm installs your block’s dependencies and helps compile stuff, while npx runs commands on packages without installing them. If you’re on macOS, you probably already have Node and can can use nvm to update versions. If you’re on Windows, you’ll need to download and install Node.
Create a project folder
Now, you might run into other tutorials that jump straight into the command line and instruct you to install a package called @wordpress/create-block. This package is great because it spits out a fully formed project folder with all the dependencies and tools you need to start developing.
I personally go this route when setting up my own blocks, but humor me for a moment because I want to cut through the opinionated stuff it introduces and focus just on the required bits for the sake of understanding the baseline development environment.
These are the files I’d like to call out specifically:
readme.txt: This is sort of like the front face of the plugin directory, typically used to describe the plugin and provide additional details on usage and installation. If you submit your block to the WordPress Plugin Directory, this file helps populate the plugin page. If you plan on creating a GitHub repo for your block plugin, then you might also consider a README.md file with the same information so it displays nicely there.
package.json: This defines the Node packages that are required for development. We’ll crack it open when we get to installation. In classic WordPress plugin development, you might be accustomed to working with Composer and a composer.json file instead. This is the equivalent of that.
plugin.php: This is the main plugin file and, yes, it’s classic PHP! We’ll put our plugin header and metadata in here and use it to register the plugin.
In addition to these files, there’s also the src directory, which is supposed to contain the source code of our block.
Having these files and the src directory is all you need to get started. Out of that group, notice that we technically only need one file (plugin.php) to make the plugin. The rest either provide information or are used to manage the development environment.
The aforementioned @wordpress/create-block package scaffolds these files (and more) for us. You can think of it as an automation tool instead of a necessity. Regardless, it does make the job easier, so you can take the liberty of scaffolding a block with it by running:
npx @wordpress/create-block
Install block dependencies
Assuming you have the three files mentioned in the previous section ready, it’s time to install the dependencies. First, we need to specify the dependencies we will need. We do that by editing the package.json. While using the @wordpress/create-block utility, the following is generated for us (comments added; JSON does not support comments, so remove the comments if you’re copying the code):
{ // Defines the name of the project "name": "block-example", // Sets the project version number using semantic versioning "version": "0.1.0", // A brief description of the project "description": "Example block scaffolded with Create Block tool.", // You could replace this with yourself "author": "The WordPress Contributors", // Standard licensing information "license": "GPL-2.0-or-later", // Defines the main JavaScript file "main": "build/index.js", // Everything we need for building and compiling the plugin during development "scripts": { "build": "wp-scripts build", "format": "wp-scripts format", "lint:css": "wp-scripts lint-style", "lint:js": "wp-scripts lint-js", "packages-update": "wp-scripts packages-update", "plugin-zip": "wp-scripts plugin-zip", "start": "wp-scripts start" }, // Defines which version of the scripts packages are used (24.1.0 at time of writing) // https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/ "devDependencies": { "@wordpress/scripts": "^24.1.0" } }
The @wordpress/scripts package is the main dependency here. As you can see, it’s a devDependency meaning that it aids in development. How so? It exposes the wp-scripts binary that we can use to compile our code, from the src directory to the build directory, among other things.
There are a number of other packages that WordPress maintains for various purposes. For example, the @wordpress/components package provides several pre-fab UI components for the WordPress Block Editor that can be used for creating consistent user experiences for your block that aligns with WordPress design standards.
You don’t actually need to install these packages, even if you want to use them. This is because these @wordpress dependencies aren’t bundled with your block code. Instead, any import statements referencing code from utility packages — like @wordpress/components — are used to construct an “assets” file, during compilation. Moreover, these import statements are converted to statements mapping the imports to properties of a global object. For example, import { __ } from "@wordpress/i18n" is converted to a minified version of const __ = window.wp.i18n.__. (window.wp.i18n being an object that is guaranteed to be available in the global scope, once the corresponding i18n package file is enqueued).
During block registration in the plugin file, the “assets” file is implicitly used to tell WordPress the package dependencies for the block. These dependencies are automatically enqueued. All of this is taken care of behind the scenes, granted you are using the scripts package. That being said, you can still choose to locally install dependencies for code completion and parameter info in your package.json file:
Now that package.json is set up, we should be able to install all those dependencies by navigating to the project folder in the command line and running npm install.
Add the plugin header
If you’re coming from classic WordPress plugin development, then you probably know that all plugins have a block of information in the main plugin file that helps WordPress recognize the plugin and display information about it on the Plugins screen of the WordPress admin.
Here’s what @wordpress/create-block generated for me in for a plugin creatively called “Hello World”:
<?php /** * Plugin Name: Block Example * Description: Example block scaffolded with Create Block tool. * Requires at least: 5.9 * Requires PHP: 7.0 * Version: 0.1.0 * Author: The WordPress Contributors * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Text Domain: css-tricks * * @package create-block */
That’s in the main plugin file, which you can call whatever you’d like. You might call it something generic like index.php or plugin.php. The create-block package automatically calls it whatever you provide as the project name when installing it. Since I called this example “Block Example”, the package gave us a block-example.php file with all this stuff.
You’re going to want to change some of the details, like making yourself the author and whatnot. And not all of that is necessary. If I was rolling this from “scratch”, then it might look something closer to this:
<?php /** * Plugin Name: Block Example * Plugin URI: https://css-tricks.com * Description: An example plugin for learning WordPress block development. * Version: 1.0.0 * Author: Arjun Singh * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Text Domain: css-tricks */
We’ve already looked at the required files for our block. But if you’re using @wordpress/create-block, you will see a bunch of other files in the project folder.
build/: This folder received the compiled assets when processing the files for production use.
node_modules: This holds all the development dependencies we installed when running npm install.
src/: This folder holds the plugin’s source code that gets compiled and sent to the build directory. We’ll look at each of the files in here in just a bit.
.editorconfig: This contains configurations to adapt your code editor for code consistency.
.gitignore: This is a standard repo file that identifies local files that should be excluded from version control tracking. Your node_modules should definitely be included in here.
package-lock.json: This is an auto-generated file containing for tracking updates to the required packages we installed with npm install.
Block metadata
I want to dig into the src directory with you but will focus first on just one file in it: block.json. If you’ve used create-block , it’s already there for you; if not, go ahead and create it. WordPress is leaning in hard to make this the standard, canonical way to register a block by providing metadata that provides WordPress context to both recognize the block and render it in the Block Editor.
Here’s what @wordpress/create-block generated for me:
There’s actually a bunch of different information we can include here, but all that’s actually required is name and title. A super minimal version might look like this:
$ schema defines the schema formatting used to validate the content in the file. It sounds like a required thing, but it’s totally optional as it allows supporting code editors to validate the syntax and provide other additional affordances, like tooltip hints and auto-completion.
apiVersion refers to which version of the Block API the plugin uses. Today, Version 2 is the latest.
name is a required unique string that helps identify the plugin. Notice that I’ve prefixed this with css-tricks/ which I’m using as a namespace to help avoid conflicts with other plugins that might have the same name. You might choose to use something like your initials instead (e.g. as/block-example).
version is something WordPress suggests using as a cache-busting mechanism when new versions are released.
title is the other required field, and it sets the name that’s used wherever the plugin is displayed.
category groups the block with other blocks and displays them together in the Block Editor. Current existing categories include text, media, design, widgets, theme, and embed, and you can even create custom categories.
icon lets you choose something from the Dashicons library to visually represent your block in the Block Editor. I’m using the format-quote icon](https://developer.wordpress.org/resource/dashicons/#format-quote) since we’re making our own pullquote sort of thing in this example. It’s nice we can leverage existing icons rather than having to create our own, though that’s certainly possible.
editorScript is where the main JavaScript file, index.js, lives.
Register the block
One last thing before we hit actual code, and that’s to register the plugin. We just set up all that metadata and we need a way for WordPress to consume it. That way, WordPress knows where to find all the plugin assets so they can be enqueued for use in the Block Editor.
Registering the block is a two-fold process. We need to register it both in PHP and in JavaScript. For the PHP side, open up the main plugin file (block-example.php in this case) and add the following right after the plugin header:
This is what the create-block utility generated for me, so that’s why the function is named the way it is. We can use a different name. The key, again, is avoiding conflicts with other plugins, so it’s a good idea to use your namespace here to make it as unique as possible:
Why are we pointing to the build directory if the block.json with all the block metadata is in src? That’s because our code still needs to be compiled. The scripts package processes the code from files in the src directory and places the compiled files used in production in the build directory, while also copying theblock.jsonfile in the process.
Alright, let’s move over to the JavaScript side of registering the block. Open up src/index.js and make sure it looks like this:
import { registerBlockType } from "@wordpress/blocks"; import metadata from "./block.json"; import Edit from "./edit.js"; import Save from "./save.js"; const { name } = metadata; registerBlockType(name, { edit: Edit, save: Save, });
We’re getting into React and JSX land! This tells WordPress to:
Import the registerBlockType module from the @wordpress/blocks package.
Import metadata from block.json.
Import the Edit and Save components from their corresponding files. We’ll be putting code into those files later.
Register the the block, and use the Edit and Save components for rendering the block and saving its content to the database.
What’s up with the edit and save functions? One of the nuances of WordPress block development is differentiating the “back end” from the “front end” and these functions are used to render the block’s content in those contexts, where edit handles back-end rendering and save writes the content from the Block Editor to the database for rendering the content on the front end of the site.
A quick test
We can do some quick work to see our block working in the Block Editor and rendered on the front end. Let’s open index.js again and use the edit and save functions to return some basic content that illustrates how they work:
import { registerBlockType } from "@wordpress/blocks"; import metadata from "./block.json"; const { name } = metadata; registerBlockType(name, { edit: () => { return ( "Hello from the Block Editor" ); }, save: () => { return ( "Hello from the front end" ); } });
This is basically a stripped-down version of the same code we had before, only we’re pointing directly to the metadata in block.json to fetch the block name, and left out the Edit and Save components since we’re running the functions directly from here.
We can compile this by running npm run build in the command line. After that, we have access to a block called “Block Example” in the Block Editor:
If we drop the block into the content area, we get the message we return from the edit function:
If we save and publish the post, we should get the message we return from the save function when viewing it on the front end:
Creating a block
Looks like everything is hooked up! We can revert back to what we had in index.js before the test now that we’ve confirmed things are working:
import { registerBlockType } from "@wordpress/blocks"; import metadata from "./block.json"; import Edit from "./edit.js"; import Save from "./save.js"; const { name } = metadata; registerBlockType(name, { edit: Edit, save: Save, });
Notice that the edit and save functions are tied to two existing files in the src directory that @wordpress/create-block generated for us and includes all the additional imports we need in each file. More importantly, though, those files establish the Edit and Save components that contain the block’s markup.
Back end markup (src/edit.js)
import { useBlockProps } from "@wordpress/block-editor"; import { __ } from "@wordpress/i18n"; export default function Edit() { return ( <p {...useBlockProps()}> {__("Hello from the Block Editor", "block-example")} </p> ); }
See what we did there? We’re importing props from the @wordpress/block-editor package which allows us to generate classes we can use later for styling. We’re also importing the __ internationalization function, for dealing with translations.
Front-end markup (src/save.js)
This creates a Save component and we’re going to use pretty much the same thing as src/edit.js with slightly different text:
import { useBlockProps } from "@wordpress/block-editor"; import { __ } from "@wordpress/i18n"; export default function Save() { return ( <p {...useBlockProps.save()}> {__("Hello from the front end", "block-example")} </p> ); }
Again, we get a nice class we can use in our CSS:
Styling blocks
We just covered how to use block props to create classes. You’re reading this article on a site all about CSS, so I feel like I’d be missing something if we didn’t specifically address how to write block styles.
Differentiating front and back-end styles
If you take a look at the block.json in the src directory you’ll find two fields related to styles:
editorStyle provides the path to the styles applied to the back end.
style is the path for shared styles that are applied to both the front and back end.
Recall that the @wordpress/scripts package copies the block.json file when it processes the code in the /src directory and places compiled assets in the /build directory. It is the build/block.json file that is used to register the block. That means any path that we provide in src/block.json should be written relative to build/block.json.
Using Sass
We could drop a couple of CSS files in the build directory, reference the paths in src/block.json, run the build, and call it a day. But that doesn’t leverage the full might of the @wordpress/scripts compilation process, which is capable of compiling Sass into CSS. Instead, we place our style files in the src directory and import them in JavaScript.
While doing that, we need to be mindful of how @wordpress/scripts processes styles:
A file named style.css or style.scss or style.sass, imported into the JavaScript code, is compiled to style-index.css.
All other style files are compiled and bundled into index.css.
The @wordpress/scripts package uses webpack for bundling and @wordpress/scripts uses the PostCSS plugin for working for processing styles. PostCSS can be extended with additional plugins. The scripts package uses the ones for Sass, SCSS, and Autoprefixer, all of which are available for use without installing additional packages.
In fact, when you spin up your initial block with @wordpress/create-block, you get a nice head start with SCSS files you can use to hit the ground running:
editor.scss contains all the styles that are applied to the back-end editor.
style.scss contains all the styles shared by both the front and back end.
Let’s now see this approach in action by writing a little Sass that we’ll compile into the CSS for our block. Even though the examples aren’t going to be very Sass-y, I’m still writing them to the SCSS files to demonstrate the compilation process.
Front and back-end styles
OK, let’s start with styles that are applied to both the front and back end. First, we need to create src/style.scss (it’s already there if you’re using @wordpress/create-block) and make sure we import it, which we can do in index.js:
import "./style.scss";
Open up src/style.scss and drop a few basic styles in there using the class that was generated for us from the block props:
That’s it for now! When we run the build, this gets compiled into build/style.css and is referenced by both the Block Editor and the front end.
Back-end styles
You might need to write styles that are specific to the Block Editor. For that, create src/editor.scss (again, @wordpress/create-block does this for you) and drop some styles in there:
Then import it in edit.js, which is the file that contains our Edit component (we can import it anywhere we want, but since these styles are for the editor, it’s more logical to import the component here):
import "./editor.scss";
Now when we run npm run build, the styles are applied to the block in both contexts:
Referencing styles in block.json
We imported the styling files in the edit.js and index.js, but recall that the compilation step generates two CSS files for us in the build directory: index.css and style-index.css respectively. We need to reference these generated files in the block metadata.
Let’s add a couple of statements to the block.json metadata:
Run npm run build once again, install and activate the plugin on your WordPress site, and you’re ready to use it!
You can use npm run start to run your build in watch mode, automatically compiling your code every time you make a change in your code and save.
We’re scratching the surface
Actual blocks make use of the Block Editor’s Settings sidebar and other features WordPress provides to create rich user experiences. Moreover, the fact that there’s essentially two versions of our block — edit and save — you also need to give thought to how you organize your code to avoid code duplication.
But hopefully this helps de-mystify the general process for creating WordPress blocks. This is truly a new era in WordPress development. It’s tough to learn new ways of doing things, but I’m looking forward to seeing how it evolves. Tools like @wordpress/create-block help, but even then it’s nice to know exactly what it’s doing and why.
Are the things we covered here going to change? Most likely! But at least you have a baseline to work from as we keep watching WordPress blocks mature, including best practices for making them.
References
Again, my goal here is to map out an efficient path for getting into block development in this season where things are evolving quickly and WordPress documentation is having a little hard time catching up. Here are some resources I used to pull this together:
If we browse the WordPress theme directory, a majority of themes showcase cover images. It is a feature in popular demand. The cover page trend is true even in the block theme directory screenshots as well.
Let’s consider the following example from Twenty Twenty (a classic theme) which includes a cover template that can be used to display both in single post and page, where the post’s featured image displays at the top that stretches across the browser screen, with post title and other desired meta data below. Cover templates allow creating content that stands out from the traditional constraints of displaying content.
Screenshot showing a single post with Twenty Twenty cover template.
Creating cover templates currently requires writing PHP code as captured here in the Twenty Twenty default theme’s cover template. If we look at the template-parts/content-cover.php file, it contains the code for displaying content when the cover-template is used.
Thus, it is not possible to create a customized cover page if you do not possess a deep knowledge of PHP. For many ordinary WordPress users, the only option is to use plugin like Custom Post Type UI as described in this short video.
Cover sections in block themes
Since WordPress 5.8, theme authors could create custom templates (like single post, author, category, and others) with a top hero section using block editor cover block and bundled into their themes with minimal or no code.
Before diving into how top large cover sections are created in block themes templates, let’s briefly look at the two block themes Twenty Twenty-Two and Wabi by Rich Tabor (full review here).
Screenshot showing cover page thumbnails of Twenty Twenty-Two (left) and Wabi (right) themes.
Many others chose to create a top cover section by using cover block, which allowed users to change the background color and add a static image from Media Library or upload from media devices – without writing any code. With this approach, images from the post featured image block had to be added manually to each single post if you wanted to have the post featured image as the background image in single posts.
Cover Blocks with dynamic post featured image
WordPress 6.0 made available another cool featured image cover blocks feature, which allows use of the featured image of any post or page as the background image in the cover block.
In the following short video, Automattic engineers discuss adding featured images to cover blocks with an example from Archeo theme:
The image block including post featured image block can be further customized using duotone color in theme.json as discussed in this short Connecting The Dots YouTube video (Automattic’s Anne McCarthy).
Use case examples (Wei,Bright Mode)
If we browse the thumbnail images in the block theme directory, we see a majority of them include large cover header sections. If we dig into their template files, they make use of cover blocks with static image background.
Some recently developed themes are using cover blocks with the dynamic post featured image background (e.g., Archeo, Wei, Frost, Bright Mode, etc.). A brief overview of the new feature is available in this short GitHub video.
Screenshot showing cover page thumbnails of Wei (left) and Bright-mode (right) themes.
Combining dynamic accent colors features of Wabi theme with cover and post featured image blocks, Rich Tabor further expands his creativity in his new Wei theme (full review available here) to display dynamic cover images from a single post.
In his Wei announcement post, Rich Tabor writes: “Behind-the-scenes, the single.html template is using a Cover block that leverages the post’s featured image. Then the duotone is applied by the color scheme assigned to the post. This way, just about any image will look fine”.
If you would like to dig deeper into the Wei theme’s header cover block and learn how to create your own, here is a short video from Fränk Klein (WP Development Courses) who explains step-by-step how it was created.
Similar to the Wei theme, Brian Gardner also makes use of cover block with post featured image block in his recent Bright Mode theme to display standout contents with vibrant colors.
Brian told WPTavern: “he loves most about the theme is the way the Cover Block is used on single pages. It pulls the featured image into the Cover block and also offers custom block styles for shadows and full-height options. […] I feel as though this really presents what’s possible with modern WordPress.”
In response, Jamie Marsland of Pootlepress created this YouTube video where he reproduces a nearly identical homepage in nearly 20 minutes.
Commenting on Marsland video, Sarah Gooding of WP Travern writes: “He is what one might describe as a power user with the block editor. He can quickly shuffle rows, columns, and groups around, adjusting padding and margins as necessary, and assign each section the corresponding color for the design. At this point, this is not something most average WordPress users could do.”
Though the block editor has come a long way, there are still growing pain points to most theme developers and ordinary users to create and design complex layouts with it.
Adding enhancement to TT2 Gopher blocks
In this section, I will walk you through how I added enhancements to the TT2 Gopher Blocks theme that I referenced in my previous article. Inspired by cover blocks from themes that I described earlier, I wanted to add three cover templates (author, category, and single-cover) to the theme.
While browsing websites, we notice two types of cover headers. The mostly observed header is cover section blended with the site header (site title and top navigation) into the cover block (e.g., Twenty Twenty, Twenty Twenty-Two, Wei, Wabi, Frost, Bright Mode, etc.). We also find header cover section which is not blended with site header and positioned just underneath, such as this BBC Future website. For TT2 Gopher blocks theme, I opted for the latter.
Creating cover header patterns
First, let’s create cover header patterns for author, single, and others (categories, tags) templates using cover blocks. Then we will convert them into patterns (as described here previously) and call the respective header cover patterns into the templates.
If you are familiar to working with the block editor, design your header section using cover blocks in the site editor and then convert the cover header code into patterns. However, if you are not familiar with FSE editor, then the easiest way is to copy patterns from the patterns directory in a post, make necessary modification and convert it into a pattern.
In my previous CSS-Tricks article, I discussed in detail on creating and using block patterns. Here is a brief overview of the workflow that I am using to create the single post cover header pattern:
Single post cover header pattern
Step 1: Using FSE interface, let’s create a new blank file and start building block structure as shown on the left panel.
Screenshot of the WordPress UI with the Full Site Editor. A block is being assembled with post date, categories, and post title.
Alternatively, this could be done in a post or page first, and then copy and paste the markup into a pattern file, later.
Step 2: Next, to covert the above markup into a pattern, first we should copy its code markup and paste into a new /patterns/header-single-cover.php in our code editor. We should also add required pattern file header markup (e.g., title, slug, categories, inserter, etc.).
Here is the entire code of the /patterns/header-single-cover.php file:
Step 3: For this demo, I have used this image from photos directory as a filler background image, and applied the Midnight duotone color. To use post featured image dynamically, we should add "useFeaturedImage":true in the cover block by replacing the above filler image link just before the "dimRatio":50 such that the line 10 should look like the following:
Alternatively, the filler image could also be changed by clicking Replace and selecting Use featured image option:
Screenshot of the WordPress UI with ‘Replace’ and ‘Use featured image’ selected.
Now, the header cover patterns should be visible in the patterns inserter panel for use anywhere in the templates, posts, and pages.
Archive cover headers
Inspired by this WP Tavern post and a step-by-step walkthrough to create an author template header, I wanted to create a similar cover header and add to TT2 Gopher theme, too.
First, let’s create the archive cover header pattern for author.html the template as well, following the above workflow. In this case, I am creating this in a new blank page, by adding blocks (as shown below in list view):
Screenshot of the WordPress UI for an Author page using a single post header cover.
In the background for the cover, I used the same image used in the single post header cover.
Because we would like to display a short author biography on the author block, a biographical statement should also be added to the user profile page, or else a blank space will be displayed in the front-end.
The following is the markup code of the header-author-cover, that we will use pattern, in the next step:
To covert the markup into a header-author-cover pattern, we should add the required pattern file header markup as described earlier. By editing the header-author-cover.php pattern, we can create similar header covers for tags, taxonomy, and other custom templates.
WordPress 6.0 and the recent Gutenberg 13.7 extended template creating features into the block editor, thus making it possible for many WordPress users, without deep knowledge of coding, to create their customized templates.
Block editor allows creating various types of templates, including cover templates. Let’s briefly overview how combining cover block and post featured image block with new template UI makes easy to create various types of cover custom templates even with no or low coding skills.
Screenshot of the WordPress UI displaying available templates provided by TT2 Gopher Blocks – Single, Page, Index, Home, 404, Blank, and Archive.
Creating templates has been made much easier with Gutenberg 13.7. How to create block templates with codes and in site editor is described in the Theme handbook and in my previous article.
Author template with cover block
Top (header section) markup of the author.html template is shown below (line 6):
As an early Gutenberg user, I couldn’t be more excited with the new theming tools like create block theme plugin and others which allow theme authors to achieve the following directly from block editor UI without writing any code:
(iv) modify and save style variation of the current theme
Additionally, the recent iterations of the Gutenberg plugin allow enabling fluid typography and layout alignments and other stylistic controls using only theme.json file without JavaScript and a line of CSS rules.
Thank you for reading and share your comments and thoughts below!
Block patterns, also frequently referred to as sections, were introduced in WordPress 5.5 to allow users to build and share predefined block layouts in the pattern directory. The directory is the home of a wide range of curated patterns designed by the WordPress community. These patterns are available in simple copy and paste format, require no coding knowledge and thus are a big time saver for users.
Despite many articles on patterns, there is a lack of comprehensive and up-to-date articles on pattern creation covering the latest enhanced features. This article aims to fill the gap with a focus on the recent enhanced features like creating patterns without registration and offer an up-to-date step-by-step guide to create and use them in block themes for novices and experienced authors.
Since the launch of WordPress 5.9 and the Twenty Twenty-Two (TT2) default theme, the use of block patterns in block themes has proliferated. I have been a big fan of block patterns and have created and used them in my block themes.
The article assumes that readers have basic knowledge of WordPress full site editing (FSE) interface and block themes. The Block Editor Handbook and Full Site Editing website provide the most up-to-date tutorial guides to learn all FSE features, including block themes and patterns discussed in this article.
Section 1: Evolving approaches to creating block patterns
The initial approach to creating block patterns required the use of block pattern API either as a custom plugin or directly registered in the functions.php file to bundle with a block theme. The newly launched WordPress 6.0 introduced several new and enhanced features working with patterns and themes, including pattern registration via a /patterns folder and a page creation pattern modal.
For background, let’s first briefly overview how the pattern registration workflow evolved from using the register pattern API to directly loading without registration.
Use case example 1: Twenty Twenty-One
The default Twenty Twenty-One theme (TT1) and TT1 Blocks theme (a sibling of TT1) showcase how block patterns can be registered in the theme’s functions.php. In the TT1 Blocks experimental-theme, this single block-pattern.php file containing eight block patterns is added to the functions.php as an include as shown here.
A custom block pattern needs to be registered using the register_block_pattern function, which receives two arguments — title (name of the patterns) and properties (an array describing properties of the pattern).
Here is an example of registering a simple “Hello World” paragraph pattern from this Theme Shaper article:
title(required): A human-readable title for the pattern.
content(required): Block HTML Markup for the pattern.
description(optional): A visually hidden text used to describe the pattern in the inserter. A description is optional but it is strongly encouraged when the title does not fully describe what the pattern does. The description will help users discover the pattern while searching.
categories(optional): An array of registered pattern categories used to group block patterns. Block patterns can be shown on multiple categories. A category must be registered separately in order to be used here.
keywords(optional): An array of aliases or keywords that help users discover the pattern while searching.
viewportWidth(optional): An integer specifying the intended width of the pattern to allow for a scaled preview of the pattern in the inserter.
blockTypes(optional): An array of block types that the pattern is intended to be used with. Each value needs to be the declared block’s name.
inserter(optional): By default, all patterns will appear in the inserter. To hide a pattern so that it can only be inserted programmatically, set the inserter to false.
The following is an example of a quote pattern plugin code snippets taken from the WordPress blog.
/* Plugin Name: Quote Pattern Example Plugin */ register_block_pattern( 'my-plugin/my-quote-pattern', array( 'title' => __( 'Quote with Avatar', 'my-plugin' ), 'categories' => array( 'text' ), 'description' => _x( 'A big quote with an avatar".', 'Block pattern description', 'my-plugin' ), 'content' => '<!-- wp:group --><div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --><!-- wp:image {"align":"center","id":553,"width":150,"height":150,"sizeSlug":"large","linkDestination":"none","className":"is-style-rounded"} --><div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large is-resized"><img src="https://blockpatterndesigns.mystagingwebsite.com/wp-content/uploads/2021/02/StockSnap_HQR8BJFZID-1.jpg" alt="" class="wp-image-553" width="150" height="150"/></figure></div><!-- /wp:image --><!-- wp:quote {"align":"center","className":"is-style-large"} --><blockquote class="wp-block-quote has-text-align-center is-style-large"><p>"Contributing makes me feel like I'm being useful to the planet."</p><cite>— Anna Wong, <em>Volunteer</em></cite></blockquote><!-- /wp:quote --><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --></div></div><!-- /wp:group -->', ) );
Using patterns in a template file
Once patterns are created, they can be used in a theme template file with the following block markup:
An example from this GitHub repository shows the use of “footer-with-background” pattern slug with “tt2gopher” prefix in TT2 Gopher blocks theme.
Early adopters of block themes and Gutenberg plugin took advantage of patterns in classic themes as well. The default Twenty Twenty and my favorite Eksell themes (a demo site here) are good examples that showcase how pattern features can be added to classic themes.
Use case example 2: Twenty Twenty-Two
If a theme includes only a few patterns, the development and maintenance are fairly manageable. However, if a block theme includes many patterns, like in TT2 theme, then the pattern.php file becomes very large and hard to read. The default TT2 theme, which bundles more than 60 patterns, showcases a refactored pattern registration workflow structure that is easier to read and maintain.
Taking examples from the TT2 theme, let’s briefly discuss how this simplified workflow works.
2.1: Registering Patterns Categories
For demonstration purposes, I created a TT2 child theme and set it up on my local test site with some dummy content. Following TT2, I registered footer-with-background and added to the following pattern categories array list in its block-patterns.php file.
In this code example, each pattern listed in the $ block_patterns = array() is called by foreach() function which requires a patterns directory file with the listed pattern name in the array which we will add in the next step.
2.2: Adding a pattern file to the /inc/patterns folder
Next, we should have all the listed patterns files in the $ block_patterns = array(). Here is an example of one of the pattern files, footer-with-background.php:
Let’s reference the pattern in the footer.html template part:
<!-- wp:template-part {"slug":"footer"} /-->
This is similar to adding heading or footer parts in a template file.
The patterns can similarly be added to the parts/footer.html template by modifying it to refer to slug of the theme’s pattern file (footer-with-background):
Section 2: Creating and loading patterns without registration
Please note that this feature requires the installation of WordPress 6.0 or Gutenberg plugin 13.0 or above in your site.
This new WordPress 6.0 feature allows pattern registration via standard files and folders – /patterns, bringing similar conventions like /parts, /templates, and /styles.
The process, as also described in this WP Tavern article, involves the following three steps:
creating a patterns folder at the theme’s root
adding plugin style pattern header
pattern source content
A typical pattern header markup, taken from the article is shown below:
In the following steps, let’s explore how a footer-with-background.php pattern registered with PHP and used in a footer.html template is refactored.
2.1: Create a /patterns folder at the root of the theme
The first step is to create a /patterns folder at TT2 Gopher theme’s root and move the footer-with-background.php pattern file to /patterns folder and refactor.
2.2: Add pattern data to the file header
Next, create the following pattern header registration fields.
A pattern file has a top title field written as PHP comments. Followed by the block-content written in HTML format.
2.3: Add Pattern Content to the file
For the content section, let’s copy the code snippets within single quotes (e.g., '...') from the content section of the footer-with-background and replace the <!-- some-block-content /-->:
The entire code snippet of the patterns/footer-with-background.php file can be viewed here on the GitHub.
Please note that the /inc/patterns and block-patterns.php are extras, not required in WordPress 6.0, and included only for demo purposes.
2.4: Referencing the patterns slug in the template
Adding the above refactored footer-with-background.php pattern to footer.html template is exactly the same as described in the previous section (Section 1, 2.2).
Now, if we view the site’s footer part in a block editor or front-end of our site in a browser, the footer section is displayed.
Pattern categories and types Registration (optional)
To categorize block patterns, the pattern categories and types should be registered in theme’s functions.php file.
After the registration, the patterns are displayed in the pattern inserter together with the core default patterns. To add theme specific category labels in the patterns inserter, we should modify the previous snippets by adding theme namespace:
The footer-with-background pattern is visible in the patterns inserted with its preview (if selected):
This process greatly simplifies creating and displaying block patterns in block themes. It is available in WordPress 6.0 without using the Gutenberg plugin.
Examples of themes without patterns registration
Early adopters have already started using this feature in their block themes. A few examples of the themes, that are available from the theme directory, that load patterns without registration are listed below:
Section 3: Creating and using patterns with low-code
The official patterns directory contains community-contributed creative designs, which can be copied and customized as desired to create content. Using patterns with a block editor has never been so easier!
Any patterns from the ever-growing directory can also be added to block themes just by simple “copy and paste” or include in the theme.json file by referring to their directory pattern slug. Next, I will go through briefly how easily this can be accomplished with very limited coding.
Adding and customizing patterns from patterns directory
3.1: Copy pattern from directory into a page
Here, I am using this footer section pattern by FirstWebGeek from the patterns directory. Copied the pattern by selecting the “Copy Pattern” button and directly pasted it in a new page.
3.2: Make desired customizations
I made only a few changes to the color of the fonts and button background. Then copied the entire code from the code editor over to a clipboard.
If you are not familiar with using the code editor, go to options (with three dots, top right), click the Code editor button, and copy the entire code from here.
3.3: Create a new file in /patterns folder
First, let’s create a new /patterns/footer-pattern-test.php file and add the required pattern header section. Then paste the entire code (step 3, above). The pattern is categorized in the footer area (lines: 5), we can view the newly added in the pattern inserter.
<?php /** * Title: Footer pattern from patterns library * Slug: tt2gopher/footer-pattern-test * Categories: tt2gopher-footer * Viewport Width: 1280 * Block Types: core/template-part/footer * Inserter: yes */ ?> <!-- wp:group {"align":"full","style":{"spacing":{"padding":{"top":"100px","bottom":"70px","right":"30px","left":"30px"}}},"backgroundColor":"black","layout":{"contentSize":"1280px"}} --> <div class="wp-block-group alignfull has-black-background-color has-background" style="padding-top:100px;padding-right:30px;padding-bottom:70px;padding-left:30px"><!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"style":{"typography":{"fontStyle":"normal","fontWeight":"700","textTransform":"uppercase"}},"textColor":"cyan-bluish-gray"} --> <h2 class="has-cyan-bluish-gray-color has-text-color" style="font-style:normal;font-weight:700;text-transform:uppercase">lorem</h2> <!-- /wp:heading --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px">One of the main benefits of using Lorem Ipsum is that it can be easily generated, and it takes the pressure off designers to create meaningful text. Instead, they can focus on crafting the best website data.</p> <!-- /wp:paragraph --> <!-- wp:social-links {"iconColor":"vivid-cyan-blue","iconColorValue":"#0693e3","openInNewTab":true,"className":"is-style-logos-only","style":{"spacing":{"blockGap":{"top":"15px","left":"15px"}}}} --> <ul class="wp-block-social-links has-icon-color is-style-logos-only"><!-- wp:social-link {"url":"#","service":"facebook"} /--> <!-- wp:social-link {"url":"#","service":"twitter"} /--> <!-- wp:social-link {"url":"#","service":"instagram"} /--> <!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul> <!-- /wp:social-links --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"level":4,"style":{"typography":{"textTransform":"capitalize","fontStyle":"normal","fontWeight":"700","fontSize":"30px"}},"textColor":"cyan-bluish-gray"} --> <h4 class="has-cyan-bluish-gray-color has-text-color" style="font-size:30px;font-style:normal;font-weight:700;text-transform:capitalize">Contact Us</h4> <!-- /wp:heading --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px","lineHeight":"1.2"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px;line-height:1.2">123 BD Lorem, Ipsum<br><br>+123-456-7890</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px","lineHeight":"1"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px;line-height:1">sample@gmail.com</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px","lineHeight":"1"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px;line-height:1">Opening Hours: 10:00 - 18:00</p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"level":4,"style":{"typography":{"fontSize":"30px","fontStyle":"normal","fontWeight":"700","textTransform":"capitalize"}},"textColor":"cyan-bluish-gray"} --> <h4 class="has-cyan-bluish-gray-color has-text-color" style="font-size:30px;font-style:normal;font-weight:700;text-transform:capitalize">Newsletter</h4> <!-- /wp:heading --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px">Lorem ipsum dolor sit amet, consectetur ut labore et dolore magna aliqua ipsum dolor sit</p> <!-- /wp:paragraph --> <!-- wp:search {"label":"","placeholder":"Enter Your Email...","buttonText":"Subscribe","buttonPosition":"button-inside","style":{"border":{"width":"1px"}},"borderColor":"tertiary","backgroundColor":"background-header","textColor":"background"} /--></div> <!-- /wp:column --></div> <!-- /wp:columns --></div> <!-- /wp:group -->
3.4: View the new pattern in the inserter
To view the newly added Footer pattern from patterns library pattern, go to any post or page and select the inserter icon (blue plus symbol, top left), and then select “TT2 Gopher – Footer” categories. The newly added pattern is shown on the left panel, together with other footer patterns and its preview on the right (if selected):
Registering patterns directly in theme.json file
In WordPress 6.0, it is possible to register any desired patterns from the pattern directory with theme.json file with the following syntax. The 6.0 dev note states, “the patterns field is an array of [pattern slugs] from the Pattern Directory. Pattern slugs can be extracted by the [URL] in single pattern view at the Pattern Directory.”
This short WordPress 6.0 features video demonstrates how patterns are registered in the /patterns folder (at 3:53) and registering the desired patterns from the pattern director in a theme.json file (at 3:13).
Then, the registered pattern is available in the patterns inserter search box, which is then available for use just like theme-bundled patterns library.
In this example, the pattern slug footer-section-design-with-3-column-description-social-media-contact-and-newsletter from the earlier example is registered via theme.json.
Page creation pattern model
As part of “building with patterns” initiatives, WordPress 6.0 offers a pattern modal option to theme authors to add page layout patterns into block theme, allowing site users to select page layout patterns (e.g., an about page, a contact page, a team page, etc.) while creating a page. The following is an example taken from the dev note:
register_block_pattern( 'my-plugin/about-page', array( 'title' => __( 'About page', 'my-plugin' ), 'blockTypes' => array( 'core/post-content' ), 'content' => '<!-- wp:paragraph {"backgroundColor":"black","textColor":"white"} --> <p class="has-white-color has-black-background-color has-text-color has-background">Write you about page here, feel free to use any block</p> <!-- /wp:paragraph -->', ) );
The page creation pattern modal can also be disabled completely by removing the post-content block type of all the patterns. An example sample code is available here.
You can follow and participate in GitHub’s discussion from the links listed under the resource section below.
Using patterns directory to build page
Patterns from the directory can also be used to create the desired post or page layout, similar to page builders. The GutenbergHub team has created an experimental online page builder app using patterns directly from the directory (introductory video). Then the codes from the app can be copied and pasted in a site, which greatly simplifies the building complex page layout process without coding.
In this short video, Jamie Marsland demonstrates (at 1:30) how the app can be used to create an entire page layout similar to page builder using desired page sections of the directory.
Wrapping up
Patterns allow users to recreate their commonly used content layout (e.g., hero page, call out, etc.) in any page and lower the barriers to presenting content in styles, which were previously not possible without coding skills. Just like the plugins and themes directories, the new patterns directory offers users options to use a wide range of patterns of their choices from the pattern directory, and write and display content in style.
Indeed, block patterns will change everything and surely this is a game changer feature in the WordPress theme landscape. When the full potential of building with patterns effort becomes available, this is going to change the way we design block themes and create beautiful content even with low-code knowledge. For many creative designers, the patterns directory may also provide an appropriate avenue to showcase their creativity.