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 , 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 file? We still have that. In fact, 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 is still in active development, meaning we’re in a transitional period where you might find styles defined there, in 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 new developmental features that are included in WordPress 6.1 get us closer to a system of styles that are completely defined in , 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:
But don’t start trading the CSS in for JSON property-value pairs in just yet. There are still CSS styling rules that need to be supported in 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 and consolidate different sources of into a UI for for setting global styles directly in the WordPress Site Editor.
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 files, , the Gutenberg plugin, or somewhere else?
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:
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 . That interplay between theme-level styles in 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.
There are other benefits, for sure, and Mike McAlister from WP Engine lists several in this Twitter thread. You can find even more benefits in this in-depth discussion over at the Make WordPress Core blog. And once you’ve given that a read, compare the benefits of the JSON approach with the available ways to define and override styles in classic themes.
We’ve already seen a lot of progress as far as what parts of a theme 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 object in 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 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 object in . 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 , courtesy of Carolina:
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 sort of forms layers of specificity, going from global elements (e.g. ) to individual elements (e.g. ), and block-level styles (e.g. contained in a block).
Additional information on JSON elements is available in this Make WordPress proposal and this open ticket in the Gutenberg plugin’s GitHub repo.
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 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.
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.
You can see exactly where those new styles are coming from in TT3’s 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 file in a child theme, where the default background color of the Button block is set 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!
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 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