Tag: Themes

Managing Fonts in WordPress Block Themes

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.

How it used to be done

This ThemeShaper article from 2014 provides an excellent example of how we used to do this in classic PHP themes, as is this newer Cloudways article by Ibad Ur Rehman.

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:

function twentyseventeen_resource_hints( $ urls, $ relation_type ) {   if ( wp_style_is( 'twentyseventeen-fonts', 'queue' ) && 'preconnect' === $ relation_type ) {     $ urls[] = array(       'href' => 'https://fonts.gstatic.com',       'crossorigin',     );   }   return $ urls; } add_filter( 'wp_resource_hints', 'twentyseventeen_resource_hints', 10, 2 );

What’s wrong with the traditional way

Great, right? There’s a hitch, however. In January 2022, a German regional court imposed a fine on a website owner for violating Europe’s GDPR requirements. The issue? Enqueuing Google Fonts on the site exposed a visitor’s IP address, jeopardizing user privacy. CSS-Tricks covered this a while back.

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 AppearanceManage theme fonts from the WordPress admin menu.

Manage Theme Fonts screen with type samples for Space Mono.
Source: WordPress Theme Directory

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.

Themes screen showing Empty Theme as the active selection with no screenshot preview.

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.

VS Code file explorer on the left and an open theme.json file on the right.
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 Google Fonts API.

Add Google Fonts to your theme screen with the select font menu open showing a list of available fonts.

For this demo, I selected Inter from the menu of options and selected the 300, Regular, and 900 weights from the preview screen:

Add Google Fonts to your theme screen with Inter selected and type samples below it of the various weight variations.

Once I’ve saved my selections, the Inter font styles I selected are automatically downloaded and stored in the theme’s assets/fonts folder:

VS Code file explorer on the left showing Inter font files; theme.json on the right showing Inter references.

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.

View the entire theme.json code
{   "version": 2,   "settings": {     "appearanceTools": true,     "layout": {       "contentSize": "840px",       "wideSize": "1100px"     },     "typography": {       "fontFamilies": [         {           "fontFamily": "Inter",           "slug": "inter",           "fontFace": [             {               "fontFamily": "Inter",               "fontStyle": "normal",               "fontWeight": "300",               "src": [                 "file:./assets/fonts/inter_300.ttf"               ]             },             {               "fontFamily": "Inter",               "fontStyle": "normal",               "fontWeight": "900",               "src": [                 "file:./assets/fonts/inter_900.ttf"               ]             },             {               "fontFamily": "Inter",               "fontStyle": "normal",               "fontWeight": "400",               "src": [                 "file:./assets/fonts/inter_regular.ttf"               ]             }           ]         }       ]     }   } }

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.

Manage Theme Fonts screen with a button to Add Google Font highlighted in red.

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 (AppearanceEditor), specifically in the Design panel.

Wordpress Site Editor screen with navigation panel open and highlighting the Edit button.

From here, navigate to TemplatesIndex 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!

Open template file in the Site Editor with an arrow pointing out the Global Styles settings button.

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.

Add local fonts to your theme screen with options to upload a font file and set its name, style, and weight.

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:

VS Code showing the font files and the theme.json file references to the font.

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.

The interface for removing a font from the theme.

Clicking the Remove links triggers a warning for you to confirm the deletion. We’ll click OK to continue.

Modal confirming the font deletion.

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.

Updated theme.json file showing the font references have been removed.

There’s ongoing work happening

There’s talk going on adding this “Font Manager” feature to WordPress Core rather than needing a separate plugin.

An initial iteration of the feature is available in the repo, and it uses the exact same approach we used in this article. It should be GDPR-compliant, too. The feature is scheduled to land with WordPress 6.3 release later this year.

Wrapping up

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.

WordPress font management

GitHub issues

European GDPR requirements

Managing Fonts in WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , ,

Styling Buttons in WordPress Block Themes

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:

  1. Child blocks inside of the Buttons block
  2. 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.

A black button above a comment form that also contains a black button.

But the markup is very different:

<div class="wp-block-button">   <a class="wp-block-button__link wp-element-button">Button 1</a> </div> <p class="form-submit wp-block-button">   <input name="submit" type="submit" id="submit" class="wp-block-button__link wp-element-button" value="Post Comment">  </p>

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:

// theme.json {   "version": 2,   "styles": {     "elements": {       // etc.     }   } }

So what we need to do is define a button element.

={   "version": 2,   "styles": {     "elements": {       "button": {         // etc.       }     }   } }

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:

{   "version": 2,   "styles": {     "elements": {       "button": {         "color": {           "background": "#17a2b8",           "text": "#ffffff"         }       }     }   } }

This changes the color of both button types:

A light blue button above a comment form that also contains a light blue button.

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:

.wp-element-button {   background-color: #17a2b8;   color: #ffffff; }

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.

Those are CSS pseudo-classes and we use them to target a link’s or button’s interactions.

In CSS, we might style a :hover state like this:

a:hover {   /* Styles */ }

In theme.json, we’re going to extend our existing button declaration with these pseudo-classes.

{   "version": 2,   "styles": {     "elements": {       "button": {         "color": {           "background": "#17a2b8",           "text": "#ffffff"         }         ":hover": {           "color": {             "background": "#138496"           }         },         ":focus": {           "color": {             "background": "#138496"           }         },         ":active": {           "color": {             "background": "#138496"           }         }       }     }   } }

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:

{   "version": 2,   "styles": {     "elements" {       "button": {         // Default button styles       }     }     "blocks": {       "core/post-comments-form": {         // etc.       }     }   } }

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:

{   "version": 2,   "styles": {     "elements" {       "button": {         // Default button styles       }     }     "blocks": {       "core/post-comments-form": {         "elements": {           "button": {             "color": {               "background": "#007bff"             }           }         }       }     }   } }

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.

A light blue button above a comment form that contans a bright blue button.

The CSS generated by WordPress has a more precise selector as a result:

.wp-block-post-comments-form .wp-element-button, .wp-block-post-comments-form .wp-block-button__link {   background-color: #007bff; }

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:

{   "version": 2,   "styles": {     "elements" {       "button": {         // Default button styles       }     }     "blocks": {       "core/post-comments-form": {         "elements": {           "button": {             "color": {               "background": "#007bff"             }             ":hover": {               "color": {                 "background": "#138496"               }             },             // etc.           }         }       }     }   } }

What about buttons that are not in blocks?

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:

A WordPress block settings panel with the Advanced settings expanded highlighting the CSS classes section in red.

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:

  1. The styles are applied to buttons in both the front-end view and the block editor.
  2. Your CSS will be compatible with future WordPress updates.
  3. 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!

Styling Buttons in WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , ,

Using The New Constrained Layout In WordPress Block Themes

One of the main goals of the WordPress Site Editor (and, yes, that is now the “official” name) is to move basic block styling from CSS 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:

{   "version": 2,   "styles": {     "spacing": {       "margin": {         "top": "60px",         "right": "30px",         "bottom": "60px",         "left": "30px"       },       "padding": {         "top": "30px",         "right": "30px",         "bottom": "30px",         "left": "30px"       }     }   } }

This is a global setting. So, if we were to crack open DevTools and inspect the <body> element, we would see these CSS styles:

body {   margin-top: 60px;   margin-right: 30px;   margin-bottom: 60px;   margin-left: 30px;   padding-top: 30px;   padding-right: 30px;   padding-bottom: 30px;   padding-left: 30px; }

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?


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.


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.

{   "version": 2,   "settings": {     "appearanceTools": true,     "useRootPaddingAwareAlignments": true,     // etc.   },

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.

Setting appearanceTools set to true automatically opts blocks into margin and padding without having to set either settings.spacing.padding or setting.spacing.margin to true.

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:

{   "version": 2,   "settings": {     "layout": {       "contentSize": "640px",       "wideSize": "1000px"     }   } }

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!

.wp-container-[id] .alignfull {   max-width: none; }

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 constrained layout 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:

Semantic layout class Layout type Supported blocks
.is-layout-flow Flow layout Columns, Group, Post Content, and Query Loop.
.is-layout-constrained Constrained layout Columns, Group, Post Content, and Query Loop.
.is-layout-flex Flex layout Columns, Buttons, Social Icons

Justin Tadlock has an extensive write-up on the different layout types and semantic classes, including use cases and examples.

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:

{   "version": 2,   "settings": {     "layout": {       "type": "constrained", // replaces `"inherit": true`       "type": "default", // replaces `"inherit": false`     }   } }

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:

Theme Root-level padding Constrained layout features
TT3 Source code Source codeTemplates
ProWP Source code Source codeTemplates
Triangulate Source code Source codeTemplates
Oaknut Source code Source codeTemplates
Loudness Source code Source codeTemplates
Pixl Source code Source codeTemplates
Block Canvas Source code Source code, Templates
Rainfall Source code Source codeTemplates

Disabling layout styles

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:

// Remove layout styles. add_theme_support( 'disable-layout-styles' );

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.

Because of the site editor development iterations in progress, we should always anticipate a difficult path ahead. However, as an optimist, I am eager to see what will happen in the upcoming version of WordPress 6.2. Some of the thing, that I am keeping a close eye on are things like features being considered for inclusion, support for sticky positioning, new layout class names for inner block wrappers, updated footer alignment options, and adding constrained and flow layout options to Cover blocks.

This GitHub issues #44720 lists the layout related discussions slated for WordPress 6.2.

Additional resources

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.


WordPress posts

GitHub pull requests and issues

Using The New Constrained Layout In WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , , ,

Adding Fluid Typography Support to WordPress Block Themes

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.

Illustrated list of WordPress blocks that received font and spacing controls in the Gutenberg plugin. There are 31 total blocks.

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.

Illustrated list of 60 WordPress blocks gaining typography and font size support in WordPress 6.1.
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:

"settings": {   "appearanceTools": true,   "layout": {     "contentSize": "768px",     "wideSize": "1600px"   },   "typography": {     "fontSizes": [        {         "name": "Large",         "size": "clamp(2.25rem, 6vw, 3rem)",         "slug": "large"       }     ]   } }

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.

"settings": {   "appearanceTools": true,   "layout": {     "contentSize": "768px",     "wideSize": "1600px"   },   "typography": {     "fontSizes": [        {         "size": "2.25rem",         "fluidSize": {           "min": "2.25rem",           "max": "3rem"         },         "slug": "large",         "name": "Large"       }     ]   } }

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.

How does this works under the hood? Rich Tabor offers a nice explanation, as does this Pull Request in GitHub. In short, WordPress converts the theme.json properties into the following CSS rule:

.has-large-font-size {   font-size: clamp(36px, calc(2.25rem + ((1vw - 7.68px) * 1.4423)), 48px); }

…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.

Entering values into the online calculator for fluid typography.

The calculator automatically generates the following CSS:

/* 36px @ 768px increasing to 48px @ 1600px */ font-size: clamp(36px, calc(2.25rem + ((1vw - 7.68px) * 1.4423)), 48px);

The calculator provide options to select input units as px, rem, and em. If we select rem unit, the calculator generates the following clamp() value:

/* 2.25rem @ 48rem increasing to 3rem @ 100rem */ font-size: clamp(2.25rem, calc(2.25rem + ((1vw - 0.48rem) * 1.4423)), 3rem);

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.


There are detailed testing instructions in the merged Pull Request that introduced the feature. There are even more testing instructions from Justin Tadlock’s pre-prelease post on Make WordPress.

Example 1: Setting a new fluid font setting

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:

{   "version": 2,   "settings": {     "appearanceTools": true,     "layout": {       "contentSize": "768px",       "wideSize": "1600px"     },     "typography": {       "fluid": true     }   } }

Now, let’s drop the settings.typography.fontSizes examples in there:

{   "version": 2,   "settings": {     "appearanceTools": true,     "layout": {       "contentSize": "768px",       "wideSize": "1600px"     },     "typography": {       "fluid": true       "fontSizes": [         {           "name": "Normal",           "size": "1.125rem",           "fluid": {             "min": "1rem",             "max": "1.5rem"           },           "slug": "normal"         }       ]     }   } }

If everything is working correctly, we can now head into the WordPress Block Editor and apply the “Normal” font setting to our block:

The WordPress Block Editor interface showing a paragraph block and the fluid typography settings for it.

Nice! And if we save and inspect that element on the front end, this is the markup:

Inspecting the WordPress Paragraph block in DevTools.

Very good. Now let’s make sure the CSS is actually there:

DevTools showing the font-size custom property for the WordPress Paragraph block's fluid typography.

Good, good. Let’s expose that CSS custom property to see if it’s really clampin’ things:

Revealing the custom property value in DevTools, showing a CSS clamp function.

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:

{   "version": 2,   "settings": {     "appearanceTools": true,     "layout": {       "contentSize": "768px",       "wideSize": "1000px"     },     "typography": {       "fluid": true     }   } }

This time, we’re working with smaller wideSize value of 1000px instead of 1600px. This should allow us to see fluid type working in an exact range.

OK, on to defining some custom font sizes under settings.typography.fontSizes:

{   "version": 2,   "settings": {     "typography": {       "fluid": true,       "fontSizes": [         {           "size": ".875rem",           "fluid": {             "min": "0.875rem",             "max": "1rem"         },           "slug": "small",           "name": "Small"         },         {           "size": "1rem",           "fluid": {             "min": "1rem",             "max": "1.5rem"           },           "slug": "normal",           "name": "Normal"         },         {           "size": "1.5rem",           "fluid": {             "min": "1.5rem",             "max": "2rem"           },           "slug": "large",           "name": "Large"         },         {           "size": "2.25rem",           "fluid": false,           "slug": "x-large",           "name": "Extra large"         }       ]     }   } }

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.

the WordPress Block Editor interface with four Paragraph blocks, each at a different font size setting.

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:

--wp--preset--font-size--small: clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 0.24), 1rem); --wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.48rem) * 0.962), 1.5rem); --wp--preset--font-size--large: clamp(1.5rem, 1.5rem + ((1vw - 0.48rem) * 0.962), 2rem); --wp--preset--font-size--x-large: 2.25rem;

Let’s check the markup on the front end:

Inspecting the WordPress Paragraph blocks in DevTools.

Good start! Let’s confirm that the .has-x-large-font-size class is excluded from fluid type:

Showing the font-size custom property for the Extra Large font setting in DevTools.

If we expose the --wp--preset--font-size--x-large variable, we’ll see it’s set to 2.25rem.

Revealing the Extra Large font size custom property value, showing 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:

Rich Tabor:

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.

Automattic developer Ramon Dodd commented in the Pull Request:

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.

Nick Croft, author of GenesisWP:

Brian Garner, designer and principal developer advocate at WPEngine:

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:

There is also still lots of ongoing work on typography and other design-related WordPress tools. If you’re interested, watch this typography tracking GitHub ticket and design tools related GitHub issues.


I used the following articles when researching fluid type and how WordPress is implementing it as a feature.

Tutorials and opinions


Adding Fluid Typography Support to WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , , , ,

Creating Style Variations in WordPress Block Themes

Global styles, a feature of the block themes, is one of my favorite parts of creating block themes. The concept of global style variations in WordPress were introduced in Gutenberg 12.5 which would allow theme authors to create alternate variations of a block theme with different combinations of colors, fonts, typography, spacing, etc. Different theme.json files stored under /styles folder “lets users quickly and easily switch between different looks in the same theme.”

The global styles panel UI is in active development iteration. More details on the development of this feature can be found and tracked here at this GitHub ticket (#35619).

In this article, I will walk through creating a proof-of-concept global style variation using alternate /styles/theme.json files and create child themes with different color modes by swapping color palettes only.

Table of contents


This article is intended for those who have basic understanding of WordPress block themes and some familiarity of using Full Site Editor (FSE) interface. If you’re new to block themes and the FSE, you can get started here on CSS-Tricks with this deep introduction to WordPress block themes and site editor documentation. This Full Site Editing website is one of the most up-to-date tutorial guides to learn all FSE features including block themes and styles variations discussed in this article.

Global style variations

For some background, let’s briefly overview global style variation. Twenty Twenty-Two (TT2) theme lead and Automattic design director Kjell Reigstad introduced global styles variations with this tweet and GitHub ticket #292 as child themes. In the ticket, Kjell notes that they were initially intended as alternate color patterns and fonts combinations, but they can be used for building simple child themes.

This example from Kjell demonstrates how different style combinations could be selected from options available in the sidebar.

Since then, the Automattic theme team has been experimenting with the concept to create variable child themes (variable color and fonts only), including the following:

  • geologist with blue, cream, slate, yellow variations
  • quadrat with black, green, red, white, and yellow versions

Global style switcher

The Gutenberg 12.5 release has introduced a global styles switcher which would allow users quickly and easily switch between different looks in the same theme via different theme.json files stored under a /styles folder.

The concept of allowing switching global style variation via theme.json has been discussed on GitHub for a while now. Gutenberg lead engineer Matias Ventura gave renewed importance to it by adding it to the WordPress 6.0 roadmap recently.

Embrace style alternates driven by json variations. This was teased in various videos around the new default theme and should be fully unveiled and presented in 6.0. One of the parallel goals is to create a few distinct variations of TT2 made just with styles. (35619)

Matias Ventura, “Preliminary Roadmap to 6.0”

The latest development iteration of theme style variation switcher is available with Gutenberg 13.0 and included in WordPress 6.0. In this Exploring WordPress 6.0 video, Automattic product liaison Anne McCarthy provides an overview of its major features, including style variations and Webfonts API (starting 5:18) discussed in this article.

Theme style variation versus child theme

In my previous article, I briefly covered building block child themes. Global style variations have blurred the line between alternate-theme.json and child themes. For example, the only difference between a recently released alante-dark child theme and its parent theme is an alternate.json file in the child theme that overrides the global theme styles like this:

Screenshot of the Visual Studio Code UI displaying the contents of alante-dark.
The alante-dark theme.

Likewise, the two recent Alara child themes in the WordPress directoryFramboise and Richmond — differ only in their single theme.json file.

Section 1: Creating theme style variations

At the root of your child theme folder, create a /styles folder, which holds style variations as JSON files. For this demo example, I created three variations of Twenty Twenty-Two’s theme.json color palettes — blue.json, maroon.json, and pink.json — by swapping the foreground and background colors:

Screenshot of the Visual Studio Code UI displaying the child theme file structure of "blue.json", "maroon.json", and "pink.json" in the styles directory.
The child theme file structure of “blue.json”, “maroon.json”, and “pink.json” in the styles directory.

Here is the final result after clicking the styles icon from the admin dashboard (located at Appearance → Editor):

Animated GIF showing the theme variations in WordPress.
Walking through the WordPress admin interface to select the “blue”, “maroon”, and “pink” styles.

Click the Other Styles button (recently revised to Browser styles), which displays “blue”, “maroon”, and “pink” color style icons in addition to its original styles.

To change and choose a style, select your preferred variation and click the Save button (top-right), which is displayed on the front end of your browser.

Adding labels to alternate style variations and file name with hover animation effect are available in Gutenberg 13.0.

Step 1: Setup and installation

First, install and set up a WordPress site with some dummy content. For this demo, I made a fresh WordPress install, activated Twenty Twenty-Two theme, and added Gutenberg test data.

The theme style variations and WebFonts API discussed in this article require installation and activation of the Gutenberg 13.0 plugin or WordPress 6.0.

Step 2: Create a TT2 child theme

In this demo child theme example, let’s slightly vary the body color from the header and footer color, with all site content centered:

The lower part of the site design are not visible because it is not scrolled into view. Site navigation is present in the header. A large banner image with a bird is visible. A date and title for the latest blog entry is also visible.
Screenshot of the default appearance of the demo theme in a browser window.

Step 3: Create JSON files

Create /styles in your child theme’s root folder with blue, maroon, and pink.json files:

__ style.css __ theme.json __ functions.php __ index.php __ templates __ ... __ parts __ ... __ styles __ blue.json __ maroon.json __ pink.json

Step 4: Create alternate theme JSON files

Next up, create your alternate-theme.json files with desired color pallets under /styles folder. For this demo example, I created three color palettes (blue, maroon, and pink). Here is the code for maroon.json:

{   "version": 2,   "title": "Maroon",   "settings": {     "color": {       "palette": [         { "slug": "foreground", "color": "#7C290F", "name": "Foreground" },         { "slug": "background", "color": "#ffffff", "name": "Background" },         { "slug": "foreground-dark", "color": "#000000", "name": "Foreground Dark" },         { "slug": "background-body", "color": "#ffd8be", "name": "Background Body" },         { "slug": "primary", "color": "#000000", "name": "Primary" },         { "slug": "secondary", "color": "#ffe2c7", "name": "Secondary" },         { "slug": "tertiary", "color": "#55ACEE", "name": "Tertiary" }       ]   },   "typography": {} }, "styles": {   "color":       {         "background": "var(--wp--preset--color--background-body)",         "text": "var(--wp--preset--color--foreground-dark)"       },   "elements": {       "link": {         "color": { "text": "var(--wp--preset--color--primary)" }       }     }   } }

The other two alternate blue.json and pink.json files swap values of foreground and background-body, foreground-dark and primary color properties with their respective blue and pink hex color values.

Section 2: An example of a use case

As I noted in my previous article, I have been working on block themes and using them for my own personal project site. Inspired by the theme style variations and Webfonts API features in Gutenberg plugin, I started tweaking my work-in-progress block theme with an alternate dark color mode and by configuring the Webfonts API.

In this section, I will walk you through how I created TT2 Gopher Blocks, a demo sibling of my work-in-progress block theme created for this article. The theme includes maroon, dark, and light color modes created using theme style variations and Webfonts API that became available with the Gutenberg 12.8 release.

Showing the homepage we are creating with style variations in WordPress.
Screenshot displaying a sample site using the TT2 Gopher theme with maroon default color.

Some highlights of the TT2 Gopher theme include centered, single-column content display, distinct header and footer, more user-friendly archive and search pages.

A copy of TT2 Gopher Blocks is available at the GitHub repository, which you can fork and customize.

Creating dark mode on WordPress

First, some background on dark mode. Dark mode is a personal preference and developers offer it or other mode toggle switches like on this site, which is not a small job for most regular developers. Creating dark mode is well-covered here at CSS-Tricks, including this complete guide to dark mode and dark mode typography.

In a WordPress site, we can add a dark mode toggle using the WP Dark Mode plugin. Erin Myers of WP Engine and WPBeginner describe how to use the WP Dark Mode plugin, while Brenda Barron lists other dark mode plugin options in this WPExplorer post.

Creating a dark mode in WordPress block themes without a plugin involves several steps. Over a year ago, Ari Stathopoulos created a dark support for the TT1 Blocks theme at the GitHub. Looking at the example here, it involves some JavaScript knowledge to create assets (e.g., toggler, customize, editor-mode-support), dark color CSS variables, and expanded functions.php files.

In this short video, Automattic’s Anne McCarthy demonstrates how simple it is to create a dark mode of TT2 block theme with global style variation by adding kllejr’s gist of JSON snippets in the TT2 /styles folder.

Creating the demo TT2 Gopher blocks theme

The TT2 Gopher is a very simple and modified version of the default Twenty Twenty-Two theme. It includes three theme style variations — maroon, dark, and white.

Describing each customization step is beyond the scope of this article, but you can learn more from my deep introduction to WordPress block themes as well as the Block Editor Handbook over at WordPress.org.

A brief overview of the TT2 Gopher theme color and font combinations include:

  • Light mode
    • The header is white and the footer has a smoky body background color.
    • Open Sans is the primary font.
  • Dark mode
    • The header and footer are black with lighter dark colors for the body backgrounds.
    • Source Serif Pro is the primary font.
  • Maroon mode
    • The header and footer are both a dark maroon color, with a lighter yellowish body background.
    • Work Sans is the primary font.

Let me briefly walk you through how I created theme style variations.

Adding and configuring webfonts

The Gutenberg 12.8 plugin introduced a new Webfonts API that allows the authors to load local (bundled) fonts “in a performance-friendly, privacy-friendly, and future-proof manner.” This feature can be implemented in a block theme the PHP way or the theme.json way.

Currently this feature works only with fonts bundled with block themes and does not support Google-hosted fonts because of privacy concerns. More details on the current status of Webfonts API development are covered in this make WordPress core article and this WP Tavern article.

Step 1: Download and add fonts in block theme

The TT2 theme adds Source Serif Pro font files to the theme’s assets/fonts folder. Two additional fonts — Work Sans and Public Sans — are also provided in he GitHub repository.

Step 2: Registering webfonts

In the TT2 theme, local Source Serif Pro webfonts are registered with PHP in its functions.php file:

function twentytwentytwo_get_font_face_styles() {   return "   @font-face{     font-family: 'Source Serif Pro';     font-weight: 200 900;     font-style: normal;     font-stretch: normal;     font-display: swap;     src: url('" . get_theme_file_uri( 'assets/fonts/SourceSerif4Variable-Roman.ttf.woff2' ) . "') format('woff2');   }   @font-face{     font-family: 'Source Serif Pro';     font-weight: 200 900;     font-style: italic;     font-stretch: normal;     font-display: swap;     src: url('" . get_theme_file_uri( 'assets/fonts/SourceSerif4Variable-Italic.ttf.woff2' ) . "') format('woff2');   }   "; }

Gutenberg 12.8 introduced the ability to register local web fonts with theme.json file. The following theme.json snippets from the demo TT2 Gopher theme show how local Work Sans web fonts are registered in the maroon theme style variation:

"typography": {   "fontFamilies": [     {       "fontFamily": "'Work Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', sans-serif",       "slug": "work-sans",       "name": "Work Sans",       "fontFace": [         { "fontFamily": "Work Sans", "fontDisplay": "block", "fontWeight": "400", "fontStyle": "normal", "fontStretch": "normal", "src": [ "file:./assets/fonts/work-sans/WorkSans-VariableFont_wght.ttf" ] },         { "fontFamily": "Work Sans", "fontDisplay": "block", "fontWeight": "700", "fontStyle": "normal", "fontStretch": "normal", "src": [ "file:./assets/fonts/work-sans/WorkSans-VariableFont_wght.ttf" ] },         { "fontFamily": "Work Sans", "fontDisplay": "block", "fontWeight": "400", "fontStyle": "italic", "fontStretch": "normal", "src": [ "file:./assets/fonts/work-sans/WorkSans-Italic-VariableFont_wght.ttf" ] },         { "fontFamily": "Work Sans", "fontDisplay": "block", "fontWeight": "700", "fontStyle": "italic", "fontStretch": "normal", "src": [ "file:./assets/fonts/work-sans/WorkSans-Italic-VariableFont_wght.ttf" ] }       ]     }   ] }

Additional information on how to register and use local webfonts in block themes is described in this tutorial and this WP Tavern article.

Creating theme style variations

Following the steps described in the previous section, I created two alternate versions of the theme.json file — white.json and black.json — with different color and fonts combinations inside the child theme’s /styles folder.

This feature requires version 2 of theme.json. Since Gutenberg 12.5, title can also be added at theme.json to display style label in the site editor or file name (without extension) will be displayed by default.

Here is an example of white.json:

{   "version": 2,   "title": "White",   "settings": {     "color": {       "palette": [         { "slug": "foreground", "color": "#000000", "name": "Foreground" },         { "slug": "background", "color": "#f2f2f2", "name": "Background" },         { "slug": "background-header", "color": "#ffffff", "name": "Background header" },         { "slug": "primary", "color": "#0d0d0d", "name": "Primary" },         { "slug": "secondary", "color": "#F0EAE6", "name": "Secondary" },         { "slug": "tertiary", "color": "#eb3425", "name": "Tertiary" },         { "slug": "quaternary", "color": "#7c7e83", "name": "Quaternary" }       ]     },     "typography": {       "fontFamilies": [         {         "fontFamily": ""Public Sans", sans-serif",         "name": "Public Sans",         "slug": "public-sans",         "fontFace": [           { "fontFamily": "Public Sans", "fontDisplay": "block", "fontStyle": "normal", "fontStretch": "normal", "src": [ "file:.assets/fonts/publicSans/PublicSans-VariableFont_wght.ttf.woff2" ] },           { "fontFamily": "Public Sans", "fontDisplay": "block", "fontStyle": "italic", "fontStretch": "normal", "src": [ "file:./assets/fonts/publicSans/PublicSans-Italic-VariableFont_wght.ttf.woff2" ] }         ]       }     ]   } }, "styles": {   "blocks": {     "core/image": {       "filter": { "duotone": "var(--wp--preset--duotone--default-filter)" }     },     "core/post-title": {       "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "700", "fontSize": "var(--wp--custom--typography--font-size--gigantic)" }     },     "core/query-title": {       "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "300", "fontSize": "var(--wp--custom--typography--font-size--gigantic)" }     },     "core/post-featured-image": {       "filter": { "duotone": "var(--wp--preset--duotone--default-filter)" }     },     "core/site-logo": {       "filter": { "duotone": "var(--wp--preset--duotone--default-filter)" }     },     "core/site-title": {       "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontSize": "var(--wp--preset--font-size--normal)", "fontWeight": "normal" }     }     },     "color": { "background": "var(--wp--preset--color--background)", "text": "var(--wp--preset--color--foreground)" },     "elements": {       "h1": {         "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "600", "fontSize": "var(--wp--custom--typography--font-size--colossal)" }       },       "h2": {         "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "600", "fontSize": "var(--wp--custom--typography--font-size--gigantic)" }       },       "h3": {         "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "300", "fontSize": "var(--wp--custom--typography--font-size--huge)" }       },       "h4": {         "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "300", "fontSize": "var(--wp--preset--font-size--x-large)" }       },       "h5": {         "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "700", "textTransform": "uppercase", "fontSize": "var(--wp--preset--font-size--medium)" }       },       "h6": {         "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontWeight": "400", "textTransform": "uppercase", "fontSize": "var(--wp--preset--font-size--medium)" }       },       "link": {         "color": { "text": "var(--wp--custom--color--foreground)" }       }     },     "typography": { "fontFamily": "var(--wp--preset--font-family--public-sans)", "fontSize": "var(--wp--preset--font-size--normal)" }   } }

This code swaps color palettes from theme.json and also registers and defines the local Public Sans font files.

The black.json is also very similar and uses Source Serif Pro fonts registered in the functions.php file.

Screenshot of the light color theme on the left. And screenshot of the dark color theme on the right. The heading navigation and first blog entry are visible.
Side-by-side comparison of the light (left) and dark (right) color themes for TT2 Gopher.

Example of block themes with theme styles variations

  • Twenty Twenty-Two – the first default theme to include style variations. Its updated 1.2, bundled with WordPress 6.0 includes three style variations — “Blue”, “Pink”, and “Swiss” — allowing users to quickly swap between different visual styles.
  • Frost – an experimental block theme with dark theme style variation.
  • Alara – has above 100 active installs and includes 7 style variations.
  • Wabi– which powers Rich Tabor website contains 3 style variants and 300+ active installations.
  • Brisky – has more than 600 installs and one dark theme style variation.
  • Pendant – a theme by Automattic theme team under development at GitHub contains 3 theme style variation.

In this WP Tavern article, Justin speculates that this new feature may be utilized by theme authors by tying to the site visitor’s settings, while some users may prefer to tweak their site giving a seasonal or event-based design look. This is probably a little early, but only time will tell how this powerful feature would be utilized by both theme authors and users.

Wrapping up

Creating style variations of a block theme with different typography and color combination has been greatly simplified, without using plugins. It’s one of my favorite feature of the block editor that I plan to apply in my personal projects.

In my opinion, theme style variations are definitely a game changer for block themes and with this handy feature there might not be a need for child themes or even many cooky-cutter block themes. A few well-designed base block themes, similar to Automattic theme team’s block-canvas or blockbase (work-in-progress base block themes at GitHub), could be customized with theme style variation.


Dark Mode

Creating Style Variations in WordPress Block Themes originally published on CSS-Tricks. You should get the newsletter.


, , , , ,

Adding Tailwind CSS to New and Existing WordPress Themes

In the 15 or so years since I started making WordPress websites, nothing has had more of an impact on my productivity — and my ability to enjoy front-end development — than adding Tailwind CSS to my workflow (and it isn’t close).

When I began working with Tailwind, there was an up-to-date, first-party repository on GitHub describing how to use Tailwind with WordPress. That repository hasn’t been updated since 2019. But that lack of updates isn’t a statement on Tailwind’s utility to WordPress developers. By allowing Tailwind to do what Tailwind does best while letting WordPress still be WordPress, it’s possible to take advantage of the best parts of both platforms and build modern websites in less time.

The minimal setup example in this article aims to provide an update to that original setup repository, revised to work with the latest versions of both Tailwind and WordPress. This approach can be extended to work with all kinds of WordPress themes, from a forked default theme to something totally custom.

Why WordPress developers should care about Tailwind

Before we talk about setup, it’s worth stepping back and discussing how Tailwind works and what that means in a WordPress context.

Tailwind allows you to style HTML elements using pre-existing utility classes, removing the need for you to write most or all of your site’s CSS yourself. (Think classes like hidden for display: hidden or uppercase for text-transform: uppercase.) If you’ve used frameworks like Bootstrap and Foundation in the past, the biggest difference you’ll find with Tailwind CSS is its blank-slate approach to design combined with the lightness of being CSS-only, with just a CSS reset included by default. These properties allow for highly optimized sites without pushing developers towards an aesthetic built into the framework itself.

Also unlike many other CSS frameworks, it’s infeasible to load a “standard” build of Tailwind CSS from an existing CDN. With all of its utility classes included, the generated CSS file would simply be too large. Tailwind offers a “Play CDN,” but it’s not meant for production, as it significantly reduces Tailwind’s performance benefits. (It does come in handy, though, if you want to do some rapid prototyping or otherwise experiment with Tailwind without actually installing it or setting up a build process.)

This need to use Tailwind’s build process to create a subset of the framework’s utility classes specific to your project makes it important to understand how Tailwind decides which utility classes to include, and how this process affects the use of utility classes in WordPress’s editor.

And, finally, Tailwind’s aggressive Preflight (its version of a CSS reset) means some parts of WordPress are not well-suited to the framework with its default settings.

Let’s begin by looking at where Tailwind works well with WordPress.

Where Tailwind and WordPress work well together

In order for Tailwind to work well without significant customization, it needs to act as the primary CSS for a given page; this eliminates a number of use cases within WordPress.

If you’re building a WordPress plugin and you need to include front-end CSS, for example, Tailwind’s Preflight would be in direct conflict with the active theme. Similarly, if you need to style the WordPress administration area — outside of the editor — the administration area’s own styles may be overridden.

There are ways around both of these issues: You can disable Preflight and add a prefix to all of your utility classes, or you could use PostCSS to add a namespace to all of your selectors. Either way, your configuration and workflow are going to get more complicated.

But if you’re building a theme, Tailwind is an excellent fit right out of the box. I’ve had success creating custom themes using both the classic editor and the block editor, and I’m optimistic that as full-site editing matures, there will be a number of full-site editing features that work well alongside Tailwind.

In her blog post “Gutenberg Full Site Editing does not have to be full,” Tammie Lister describes full-site editing as a set of separate features that can be adopted in part or in full. It’s unlikely full-site editing’s Global Styles functionality will ever work with Tailwind, but many other features probably will.

So: You’re building a theme, Tailwind is installed and configured, and you’re adding utility classes with a smile on your face. But will those utility classes work in the WordPress editor?

With planning, yes! Utility classes will be available to use in the editor so long as you decide which ones you’d like to use in advance. You’re unable to open up the editor and use any and all Tailwind utility classes; baked into Tailwind’s emphasis on performance is the limitation of only including the utility classes your theme uses, so you need to let Tailwind know in advance which ones are required in the editor despite them being absent elsewhere in your code.

There are a number of ways to do this: You can create a safelist within your Tailwind configuration file; you can include comments containing lists of classes alongside the code for custom blocks you’ll want to style in the block editor; you could even just create a file listing all of your editor-specific classes and tell Tailwind to include it as one of the source files it monitors for class names.

The need to commit to editor classes in advance has never held me back in my work, but this remains the aspect of the relationship between Tailwind and WordPress I get asked about the most.

A minimal WordPress theme with a minimal Tailwind CSS integration

Let’s start with the most basic WordPress theme possible. There are only two required files:

  • style.css
  • index.php

We’ll generate style.css using Tailwind. For index.php, let’s start with something simple:

<!doctype html> <html lang="en">   <head>     <?php wp_head(); ?>     <link rel="stylesheet" href="<?php echo get_stylesheet_uri(); ?>" type="text/css" media="all" />   </head>   <body>     <?php     if ( have_posts() ) {       while ( have_posts() ) {         the_post();         the_title( '<h1 class="entry-title">', '</h1>' );         ?>         <div class="entry-content">           <?php the_content(); ?>         </div>         <?php       }     }     ?>   </body> </html>

There are a lot of things a WordPress theme should do that the above code doesn’t — things like pagination, post thumbnails, enqueuing stylesheets instead of using link elements, and so on — but this will be enough to display a post and test that Tailwind is working as it should.

On the Tailwind side, we need three files:

  • package.json
  • tailwind.config.js
  • An input file for Tailwind

Before we go any further, you’re going to need npm. If you’re uncomfortable working with it, we have a beginner’s guide to npm that is a good place to start!

Since there is no package.json file yet, we’ll create an empty JSON file in the same folder with index.php by running this command in our terminal of choice:

echo {} > ./package.json

With this file in place, we can install Tailwind:

npm install tailwindcss --save-dev

And generate our Tailwind configuration file:

npx tailwindcss init

In our tailwind.config.js file, all we need to do is tell Tailwind to search for utility classes in our PHP files:

module.exports = {   content: ["./**/*.php"],   theme: {     extend: {},   },   plugins: [], }

If our theme used Composer, we’d want to ignore the vendor directory by adding something like "!**/vendor/**" to the content array. But if all of your PHP files are part of your theme, the above will work!

We can name our input file anything we want. Let’s create a file called tailwind.css and add this to it:

/*! Theme Name: WordPress + Tailwind */  @tailwind base; @tailwind components; @tailwind utilities;

The top comment is required by WordPress to recognize the theme; the three @tailwind directives add each of Tailwind’s layers.

And that’s it! We can now run the following command:

npx tailwindcss -i ./tailwind.css -o ./style.css --watch

This tells the Tailwind CLI to generate our style.css file using tailwind.css as the input file. The --watch flag will continuously rebuild the style.css file as utility classes are added or removed from any PHP file in our project repository.

That’s as simple as a Tailwind-powered WordPress theme could conceivably be, but it’s unlikely to be something you’d ever want to deploy to production. So, let’s talk about some pathways to a production-ready theme.

Adding TailwindCSS to an existing theme

There are two reasons why you might want to add Tailwind CSS to an existing theme that already has its own vanilla CSS:

  • To experiment with adding Tailwind components to an already styled theme
  • To transition a theme from vanilla CSS to Tailwind

We’ll demonstrate this by installing Tailwind inside Twenty Twenty-One, the WordPress default theme. (Why not Twenty Twenty-Two? The most recent WordPress default theme is meant to showcase full-site editing and isn’t a good fit for a Tailwind integration.)

To start, you should download and install the theme in your development environment if it isn’t installed there. We only need to follow a handful of steps after that:

  • Navigate to the theme folder in your terminal.
  • Because Twenty Twenty-One already has its own package.json file, install Tailwind without creating a new one:
npm install tailwindcss --save-dev
  • Add your tailwind.config.json file:
npx tailwindcss init
  • Update your tailwind.config.json file to look the same as the one in the previous section.
  • Copy Twenty Twenty-One’s existing style.css file to tailwind.css.

Now we need to add our three @tailwind directives to the tailwind.css file. I suggest structuring your tailwind.css file as follows:

/* The WordPress theme file header goes here. */  @tailwind base;  /* All of the existing CSS goes here. */  @tailwind components; @tailwind utilities;

Putting the base layer immediately after the theme header ensures that WordPress continues to detect your theme while also ensuring the Tailwind CSS reset comes as early in the file as possible.

All of the existing CSS follows the base layer, ensuring that these styles take precedence over the reset.

And finally, the components and utilities layers follow so they can take precedence over any CSS declarations with the same specificity.

And now, as with our minimal theme, we’ll run the following command:

npx tailwindcss -i ./tailwind.css -o ./style.css --watch

With your new style.css file now being generated each time you change a PHP file, you should check your revised theme for minor rendering differences from the original. These are caused by Tailwind’s CSS reset, which resets things a bit further than some themes might expect. In the case of Twenty Twenty-One, the only fix I made was to add text-decoration-line: underline to the a element.

With that rendering issue resolved, let’s add the Header Banner Component from Tailwind UI, Tailwind’s first-party component library. Copy the code from the Tailwind UI site and paste it immediately following the “Skip to content” link in header.php:

Showing a Tailwind CSS component on the front end of a WordPress theme.

Pretty good! Because we’re now going to want to use utility classes to override some of the existing higher-specificity classes built into the theme, we’re going to add a single line to the tailwind.config.js file:

module.exports = {   important: true,   content: ["./**/*.php"],   theme: {     extend: {},   },   plugins: [], }

This marks all Tailwind CSS utilities as !important so they can override existing classes with a higher specificity. (I’ve never set important to true in production, but I almost certainly would if I were in the process of converting a site from vanilla CSS to Tailwind.)

With a quick no-underline class added to the “Learn more” link and bg-transparent and border-0 added to the dismiss button, we’re all set:

Showing a Tailwind CSS component in the front end of a WordPress theme, but with more refined styles for the buttons and links.

It looks a bit jarring to see Tailwind UI’s components merged into a WordPress default theme, but it’s a great demonstration of Tailwind components and their inherent portability.

Starting from scratch

If you’re creating a new theme with Tailwind, your process will look a lot like the minimal example above. Instead of running the Tailwind CLI directly from the command line, you’ll probably want to create separate npm scripts for development and production builds, and to watch for changes. You may also want to create a separate build specifically for the WordPress editor.

If you’re looking for a starting point beyond the minimal example above — but not so far beyond that it comes with opinionated styles of its own — I’ve created a Tailwind-optimized WordPress theme generator inspired by Underscores (_s), once the canonical WordPress starter theme. Called _tw, this is the quick-start I wish I had when I first combined Tailwind with WordPress. It remains the first step in all of my client projects.

If you’re willing to go further from the structure of a typical WordPress theme and add Laravel Blade templates to your toolkit, Sage is a great choice, and they have a setup guide specific to Tailwind to get you started.

However you choose to begin, I encourage you to take some time to acclimate yourself to Tailwind CSS and to styling HTML documents using utility classes: It may feel unusual at first, but you’ll soon find yourself taking on more client work than before because you’re building sites faster than you used to — and hopefully, like me, having more fun doing it.

Adding Tailwind CSS to New and Existing WordPress Themes originally published on CSS-Tricks. You should get the newsletter.


, , , ,

A Deep Introduction to WordPress Block Themes

The relatively new WordPress editor, also known as the WordPress Block Editor, always under development via the Gutenberg plugin, has been with us since 2018. You can use the block editor on any WordPress theme, provided the theme loads CSS that the blocks use. But there are new themes that lean into the Block Editor much more deeply.

WordPress Block Themes allow you to build out the entire site using blocks, meaning the theme’s responsibly is mostly design guidelines, and less about controlling the pages and the content on them. This is referred to as Full-Site Editing in WordPress and the themes that are built for this are called Block Themes, because you build out everything with blocks.

Let’s dig into all this.

Illustration of a black vinyl record coming out of a record sleep sleeve from the left that contains a blue tinted image of jazz singer Joséphine Baker's profile looking right with a soft smile and parted lips. The image includes white text that says WordPress 5.9 and code is poetry.
Credit: WordPress.org

Table of Contents


Except for those who follow its day-to-day development iterations at GitHub, most development surrounding the block editor is largely visible to users — and that’s not necessarily a bad thing. I have been personally trying to keep myself updated with the block editor through WP Tavern and Gutenberg posts, and have been using both the legacy — or “Classic” editor — as well as the block editor in my personal learning project sites.

After taking a detour to learn and experience headless WordPress sites with Gatsby and Frontity frameworks, I am now back to my native WordPress home.

Though I have been aware of the WordPress theme-experiment GitHub repository for a while — themes made completely out of blocks! — I have only started digging into block themes recently. In fact, I have been using an experimental block-based theme here in this project site.

WordPress 5.9 is now out in the wild and with it comes block-based theming for the masses. This release, dubbed Joséphine, is the formal introduction to WordPress full site editing and Block Themes.

Even though the block-based theming functionality has been available in various iterative forms in previous releases, this is a big deal for the WordPress platform and the ecosystem that relies on it. I’ve had my hands on it and thought I’d share what I’ve learned about block themes in my hands-on experience, as well as some personal thoughts on how it works.

Disclaimer: I am not a block themes expert by any stretch. I am well-versed in WordPress and a major fan of the content management system. My goal here is not to critique WordPress 5.9 or steer you in the direction of whether or not you should like it or want to use it. I’m merely coming from the perspective of an open-minded learner who is building personal sites with fairly deep understanding and familiarity with the WordPress Block Editor.

Before we dive straight into block themes, I think it’s a good idea to form a baseline understanding of just what we’re talking about when we’re tossing around terms, like blocks and site editing, as they’re so incredibly new and game-changing relative to what we’ve known and loved about WordPress for decades.

Block Editor

This is really what we mean any time we refer to the “WordPress Editor.” We call the WordPress Editor the Block Editor because it allows us to create pages and posts where each element— including text, images, videos, headers, footers, etc. — is inserted into the post using blocks that can be arranged modularly to complete page layouts. It evolved from what’s now called the “classic” editor, which was more squarely based on entering content to be published on a page or post in a predefined layout.

A full screenshot of the WordPress Block editor split into three numbers parts that are highlighted in red.
WordPress Block Editor including the block inserter (1), block editor content area (2), and the document and block settings (3)
Credit: WordPress Block Editor Handbook.

It’s sort of like content and layout coming together, where both are managed in the WordPress Editor. So, where we used to rely on the editor for content and (more or less) theme templates to define layout, both are directly editable in the WordPress Editor interface.

You can find more detail here on using the Block Editor.

Block Theme

As explained in the WordPress docs:

A block theme is a WordPress theme with templates entirely composed of blocks so that in addition to the post content of the different post types (pages, posts, …), the block editor can also be used to edit all areas of the site: headers, footers, sidebars, etc.

This WordPress documentation provides an overview of block themes in its knowledgebase, including how to create block themes and styling in this primer.

The bottom line: Block themes are different than “classic” WordPress themes. Rather than relying strictly on PHP files that conform to the WordPress Template Hierarchy, a WordPress Block Theme consists of block-based HTML templates — assembled groups of blocks that can be styled and arranged in the WordPress Site Editor (that’s coming up next) as well as using a theme.json file for global styling tokens.

Site Editor

This is the crown jewel of WordPress 5.9. While it is officially called the WordPress Site Editor, it’s largely been referred to as Full-Site Editing** (FSE) during development and is described as “the cohesive experience that allows you to directly edit and navigate between various templates, template parts, styling options, and more.” Phew, that’s a lot!

Credit: WordPress Support

The WordPress Site Editor allows us to create and editing templates that are made of blocks. So the idea is that we can assemble a group of blocks that can be applied globally to a site. Like a header component, for example. That might consist of blocks for a site logo, a primary menu, and a tagline. The site editor allows us to create a new block theme or modify an existing theme to give the site’s global appearance a completely new look without writing a line of code.

So, you know how you’ve had to build a theme in the past with a bunch of PHP templates? That’s no longer the case. Theme “development” now has a UI that’s available directly in WordPress.

More detail on using site editor is in the WordPress documentation.

The official WordPress Glossary has additional terms and definitions you may want to explore as we dig deeper into WordPress Block Themes and FSE.

Using the block editor with classic themes

The WordPress Block Editor can be used in both the classic and block themes. When the Gutenberg editor project began, the classic TinyMCE-based editor was detached from WordPress Core into the Classic Editor plugin. As long as the Classic Editor plugin is installed and active, content writing is pretty normal as it was before blocks were introduced.

Prior to the formal introduction of block editor features, we had to install the experimental Gutenberg plugin. By simply switching plugins, individual page or post contents could be created with either editor. The WordPress 5.0 release introduced the block editor alongside the default Twenty Nineteen theme, demonstrating how to add block editor features and explore its power.

In other words, the evolution toward FSE has been building for a while. And because of that, we get to enjoy a high level of backwards compatibility that allows us all to adopt block-based features when we’re good and ready.

The anatomy of block-based themes

Experimental block-based themes have been in development since early 2020. At the time I’m writing this, the GitHub theme experiment repository lists 12 block themes that explore some aspect of creating themes using blocks or block templates.

But it was probably the Twenty Twenty-One theme that was the first “default” theme to make blocks a first-class citizen, introducing block styles and block patterns, though the recently updated versions of Twenty Nineteen, and Twenty Twenty also include bundled block styling and block patterns. Currently, there are more than 130 themes from the community with bundled block editor patterns, block styles feature, including my favorite, Anders Noren’s Eksell theme.

With the ongoing development of the WordPress Block Editor’s FSE features, even more block-based themes are also being introduced.

So, what does the development of block-based themes mean for those of us who are deeply entrenched in the “classic” way of building WordPress themes? That’s what I want to look at in this section.

The file structure of block themes

In classic PHP-powered theming, markup elements are generated with PHP and JavaScript, while in block themes those templates are entirely composed of HTML blocks and structural CSS provided by the block editor. This might sound scary for lots of folks, but it’s easy to imagine just how liberating it is for others as it lowers the bar when it comes to developing a WordPress theme.

The structure of a block theme is drastically different from the classic WordPress Template Hierarchy that we all are used to. In classic PHP-based themes, page element markup has to be generated with PHP and JavaScript, whereas in block themes, the WordPress Core provides both the markup and basic styling. For example, the default Twenty Twenty-One theme contains 48 PHP files and 11 JavaScript files weighing in at 4.5 MB. Its block-based sibling, the experimental TT1 Blocks theme, contains only four PHP files, one JavaScript file, and eight HTML files at 3.5 MB.

Screenshot of a Mac window open to the default Twenty Twenty-One WordPress theme, displaying a long list of files.
Twenty Twenty-One theme folder

Screenshot of a Mac window open to the TT1 theme folder, showing that WordPress Block Themes contain fewer files.
TT1 theme folder

A block theme structure can be very simple with just a few required files : index.php, style.css and template/index.html. The following is a typical block theme file structure, pulled from the WordPress Editor Handbook:

#! basic block-theme structure theme |__ style.css |__ functions.php |__ index.php |__ theme.json |__ templates     |__ index.html     |__ single.html     |__ archive.html     |__ ... |__ parts     |__ header.html     |__ footer.html     |__ sidebar.html     |__ ...
  • styles.css: Contains theme’s style sheet
  • functions.php: Contains theme setup and may include additional files, enable an editor style sheet, and enqueue style.css, if there are any
  • index.php: An empty file to switch to default file in case the block theme is activated without the WordPress Block Editor.
  • theme.json: Optional configuration file used to enable or disable features and set default styles for both the website and blocks
  • templates: Contains page templates that are composed of blocks. These files follow the same template hierarchy as traditional themes.
    • index.html: The primary template to generate a post or page, analogous to index.php in classic themes
    • single.html: The template to generate single posts or pages
    • archive.html: The template to generate archive lists of posts
  • parts: The common collections of blocks to be used in block templates
    • header.html: The global header block
    • footer.html: The global footer block
    • sidebar.html: An optional global sidebar block

A list of theme blocks including that are specific to block themes is available in WordPress Block Editor Handbook.

Templates and template parts

Templates are basically group of assembled blocks that might include reusable block parts, like a site header or footer. Different blocks are used to compose a page template. For example, that might be a list of blog posts, a list of products, or even a widget.

Here’s an example of a block template pulled from the WordPress Block Editor Handbook.

 <!-- wp:site-title /-->  <!-- wp:image {"sizeSlug":"large"} --> <figure class="wp-block-image size-large">     <img src="https://cldup.com/0BNcqkoMdq.jpg" alt="" /> </figure> <!-- /wp:image -->  <!-- wp:group --> <div class="wp-block-group">     <!-- wp:post-title /-->     <!-- wp:post-content /--> </div> <!-- /wp:group -->  <!-- wp:group --> <div class="wp-block-group">     <!-- wp:heading -->     <h2>Footer</h2>     <!-- /wp:heading --> </div> <!-- /wp:group -->

Creating WordPress Block Themes

The WordPress Site Editor is now the primary tool for defining the look and feel of a WordPress website. You may be used to using the WordPress Customizer to do these things — and some themes have heavily tapped into that to do what the site editor is now designed to do.

So, no longer is the block editor for pages and posts; it’s the way WordPress themes are created.

I’m assuming that many of you have already used the block editor, and don’t really need a deep lesson on what it is or how to use it. That said, it’s worth poking at it a bit since it’s the impetus for everything related to WordPress theming moving forward, now that WordPress 5.9 is in the wild.

In fact, when we talk about block editing and theming, yes, we’re talking about the block editor. But really what we’re talking about is the WordPress Site Editor.

The WordPress Site Editor interface

Even as an early adopter of the Gutenberg plugin, I find the experience of the site editor intimidating and frustrating. It changes frequently and often drastically with each new release. I am hopeful, though, that WordPress 5.9 is a sort of line in the sand that helps stabilize that rocky feeling.

The site editor is accessed the same way you’re already used to accessing the WordPress Customizer. It’s located under Appearance in the dashboard menu, called Editor.

Screenshots of the WordPress admin Themes screen side-by-side, the first showing the classic WordPress menu items like Customize, Widgets, and Menus, while the second shows how a WordPress Block Themes only displays a single Editor menu item.
The site editor option is available only when a block theme is activated. If you’re using a classic theme, you’ll get the classic UI to go with it.

Let’s briefly walk-through the new Editor interface.

First, navigate to the site editor by clicking Appearance → Editor from the WordPress admin menu. That menu item may have a red “beta” label on it, like it currently does in WordPress 5.9.

That takes you to the site editor, which displays either your homepage or post archive, depending on what you have your homepage set to in Settings → Reading. From there it sort of looks like the fullscreen version of the block editor when creating or editing a page or post. But click on the WordPress logo in the top-left of the screen, and a left panel opens up revealing the WordPress Site Editor and its menu to navigate between different parts of the site. This includes Site, Templates, and Template Parts.

Screenshot of the WordPress Site Editor. There is a dark gray left panel open with an Editor heading and three links for Site, Templates, and Template Parts. The main content shows a preview of the site homepage in the WordPress Block Editor.

Let’s click into Templates. This shows us a list of the available templates provided by the theme, complete with a description of each one and where it is registered (e.g. the parent or a child theme).

Screenshot of the site editor's Templates screen which shows a two-column table listing template on the left and who a template was added by on the right.

The other way to get to this screen is from the initial page we landed on when entering the site editor. Click the name of the template in the top admin bar to reveal a button that takes you directly to the same Templates screen.

Screenshot of the Home template open in the WordPress Site Editor. The template name is at the top of the screen in a white toolbar and is expanded with a submenu that describes the template and provides a black button with white text to view all templates.

Any of templates can be edited just like any page or post in the block editor. Let’s say I don’t like to have a featured image on my index page and want to remove it. Simply delete the featured image block and save the template.

The other key part of the site editor UI is a list view that outlines the current blocks that are placed in the template. This has been a feature in WordPress since the introduction of the block editor, but what’s new this time around is that you can open and close parent blocks that contain child blocks like an accordion. Not only that, but it supports dragging and dropping blocks to change the layout directly from there.

The WordPress Site Editor with a white left panel expanded revealing an outline of the current blocks that are applied to the template.

One more thing in the site editor UI: you can clear out customizations with the click of a button. From the Templates screen, click the kebob menu next to a template and select the option to Clear customizations. This is a nice way to reset and start from scratch, should you need to.

Screenshot of the Template Parts screen in the WordPress Site Editor, showing a two-column able with a column that displays template names, and a column that identifies the location of the template part.

The WordPress Core team publishes regular updates on what’s new at Make WordPress Core. It’s worth bookmarking that to stay posted on the latest changes to the WordPress Block Editor and Site Editor.

Creating Templates and Template Parts

Templates, as you know, are core to WordPress theming. They enforce consistent and reusable layouts. That doesn’t change in WordPress 5.9. And neither does the fact that we can create template parts that are like module pieces that can be used in multiple template, say a post query that lives in an archive template and the home template.

What’s different in WordPress 5.9 is that they are created and managed with the site editor rather than PHP files that live in the theme folder.

The Block Editor Handbook lists three ways to create templates and template parts: (a) manually, by creating HTML files containing block markup, (b) using the site editor, and (c) using the template editing mode in the block editor.

Brief descriptions of creating template in the site editor and template editing mode are available in the Block Theme handbook. The WordPress 5.9 allows to create a new template using editor mode.

Screenshot of the Template Parts screen open in the WordPress Site Editor. A modal is open above the UI that contains an interface to create a template part, including the part's name and area.

The customized templates can then be exported to include in a block theme. So, yeah, we now have the ability to create a fully functioning WordPress theme without writing a line of code! The exported folder currently does not contain theme.json file, however there is a proposal over at GitHub to allow exporting both block themes and styles.

Screenshot of the WordPress Site Editor preferences panel open as a white panel to the left of the screen.

But for those who prefer working more closely with code, then manually creating WordPress templates and template parts is still a thing. You can still crack open a code editor and create HTML files containing block markup.

Global settings and styles (theme.json)

In classic themes, we write the styling rules in a style.css file. In block themes, styling is more challenging because CSS comes from different sources (e.g. core blocks, themes, and users). WordPress 5.8 introduced a concept of Global Styles — which is essentially a theme.json file — that, according to the docs, consolidate “the various APIs related to styles into a single point – a theme.json file that should be located inside the root of the theme directory.“

Screenshot of a theme dot jayson file open in the VS Code editor. The file contains objects for version and settings. The settings object contains a color object. The color object contains a palette objects which contains properties for slightly, color, name, and default.

The theme.json file is said to have been designed to offer more granular styling structure for theme authors with options to manage and customize the CSS styles coming from various origins. For example, a theme author may set certain styling features that are hidden from users, define default colors, font sizes and other features available to the user, and may set the default layout of the editor as well. Plus, theme.json allows you to customize styling on a per-block basis. It’s powerful, flexible, and super maintainable!

The block editor is expected to provide all the basic styling that theme authors are allowed to customize style, as defined by the theme.json file. However, the theme.json file could get quite long for a complex theme, and currently there is no way to partition it in a more digestible way. There is a GitHub ticket to restructure it so that different theme.json files map to theme hierarchy to /styles folder. That would be a nice enhancement for developer experience.

The default Twenty Twenty-Two theme is a good example of how WordPress full-site editing features use theme.json for global settings and styling blocks.

WordPress Block Theme approaches

Maybe you’ve always made WordPress themes from scratch. Perhaps you’ve relied on the Underscores theme as a starting point. Or maybe you have a favorite theme you extend with a child theme. The new features of the WordPress Site Editor really change the way we make themes.

Following are a few emerging strategies for block-based theme development that are deeply integrated with the WordPress Site Editor.

Universal themes

The Automattic team has built a Blockbase universal theme that’s dubbed as a new way to build themes, sort of similar to the Underscores starter theme. The Blockbase theme provides temporary “ponyfill” styles that the block editor “does not yet take into account on theme.json ‘custom’ properties” and that may eventually become obsolete once the Gutenberg plugin fully matures and is integrated into WordPress Core.

Using the universal parent theme approach, the Automattic has already released eight Blockbase child themes, and several others are in progress over at GitHub.

Twenty Twenty-Two default theme

The Twenty Twenty-Two default theme is another excellent starting point, as it’s really the first WordPress theme that ships with WordPress that is designed to work with the site editor.

In my opinion, this theme is excellent for theme developers who are already familiar with FSE features to showcase what is possible. For others users who are not developers and are not familiar with FSE features, customizations it in the block editor, then exporting it as a child theme could be painfully frustrating and overwhelming.

Hybrid themes

The concept of “Hybrid” themes in the context of FSE is discussed in this GitHub ticket. The idea is to provide paths for any user to use the site or template editor to override traditional theme templates.

Justin Tadlock in this WP Tavern post predicts four types of themes — block only, universal, hybrid, and classic — and speculates that theme authors may split between “block themes and a mashup of classic/hybrid themes.”

Proof in the pudding is provided by Frank Klein in “What I Learned Building a Hybrid Theme”:

A hybrid theme mixes the traditional theming approach with full-site editing features. A key component here is the theme.json file. It offers more control over the block editor’s settings, and simplifies styling blocks. A hybrid theme can use block templates as well, but that’s optional.

Frank is the author of the Block-Based Bosco theme and has expanded further on what a “hybrid theme” is by creating a hybrid version of the default Twenty Twenty theme. The theme is available on GitHub. Currently, there are no hybrid themes in the WordPres Theme Directory.

WordPress community themes

At the time of writing, there are 47 block-based themes with FSE features available in the theme directory. As expected, this approach is widely varied.

For example, in this post, Aino block theme author Ellen Bower discusses how they converted their classic theme into a block theme, detailing what makes a theme a “block” theme. The file structure of this approach looks different from the standard block theme structure we covered earlier.

Another popular block theme, Tove by Andars Noren, is described as a flexible base theme that follows the standard block theme file structure.

There’s also a very simple single page proof of the concept theme by Carolina Nymark that contains nothing but a single index.html called Miniblock OOAK. It’s already available in the theme directory, as is another one from Justin Tadlock that’s a work in progress (and he wrote up his process in a separate article).

Block Theme Generator app

Even though we’ve already established how friendly WordPress Block Themes are for non-developers, there are tools that help create complete block themes or merely a customized theme.json file.

David Gwyer, an Automattic engineer, has been working on a Block theme generator app which, at the time of writing, is in beta and available for testing by request.

Screenshot of the Block Theme Generator app homepage. It has a bright blue background and dark blue text that welcomes you to the site, and a screenshot of the app.

In my brief testing, the app only allowed me to generate customized theme.json file. But Gwyer told to WP Tavern that the app isn’t fully baked just yet, but features are being added often. Once complete, this could be a very helpful resource for theme authors to create customized block themes.

Block themes that are currently in use

This Twitter thread from Carolina Nymark shows some examples of block themes that are live and in production at the time of this writing. In a recent Yoast article, Carolina listed a bunch of personal and business websites that use block themes.

Personal sites

Business sites

As I mentioned earlier, I also have been using a block theme for one of my personal websites for a while. The default Twenty Twenty-Two theme also currently shows more than 60,000 active installs, which tells me there are many more examples of block-based theme implementations in the wild.

Building Block Child Themes

Child theming is still a thing in this new era of WordPress blocks, though something that’s still in early days. In other words, there is no clear approach to do make a block-based child theme, and there are no existing tools to help at the moment.

That said, a few approaches for creating WordPress child block themes are emerging.

Create Blockbase Theme plugin

The Automattic team is working on a plugin called Create Blockbase Theme. This will make it fairly trivial to create child themes based on the Blockbase universal theme we talked about earlier. Ben Dwyer has discussed how theme authors can build Blockbase child themes with simple six steps and without writing a line of code.

I tested the plugin in my own local environment, making only small changes to my Blockbase theme install, and everything appeared to work. Just note that the plugin is still experimental and under development, though you can follow the roadmap to see what’s up.

Using an alternate theme.json file

Kjell Reigstad, author of the default WordPress Twenty Twenty-Two theme, demonstrates how swapping a single theme.json file with another theme.json file that contains different style configurations can change the look and feel of a block-based theme design.

Kjell has opened a pull request that shows off several experimental child themes that are available for testing at the GitHub theme-experiment GitHub repository.

A three-by-two grid of screenshots of child themes based on the default WordPress Twenty Twenty-Two theme in alternate colors schemes.

Along these same lines, Ryan Welcher is in the process of developing a theme.json builder tool that will generate a customized theme.json file to facilitate non-coders to create similar child themes. More can be found in this WP Tavern post.

The Framboise child theme (available in theme directory) is an early example of that approach which includes only a single theme.json file.

Is there even a need for child themes?

Rich Tabor asks the question:

Indeed, a single theme.json file could serve as a child theme on its own. There is an ongoing discussion about allowing theme authors to ship multiple theme.json files with block themes that offer multiple global style variations. This way, a WordPress user could pick one of the variations to use on the site.

Some features of global style variations are already included in Gutenberg v12. 5 and expected to be available with WordPress 6.0.

Some personal thoughts

I’d be remiss to end this without weighing in on all this from a personal level. I’ll do this briefly in a few points.

Block themes are a WordPress answer to Jamstack criticisms

Jamstack enthusiasts have lobbed criticisms at the WordPress platform, most notably that WordPress themes are bloated with PHP files. Well, that’s no longer the case with WordPress Block Themes.

We saw earlier how an entire theme can be a single index.html file and a theme.json file. No bloat there. And nothing but markup.

I miss the WordPress Customizer

Especially the ability to inject custom code. From here on out, it’s going to require a deep level of familiarity with the WordPress Site Editor UI to accomplish the same thing.

Customizations a site is easy-peasy.

Customizing a classic theme — even something as minimal as changing fonts — can be difficult if you don’t know what you’re doing. That’s changed now with the site editor and the introduction of the theme.json file, where a theme can be customized (and even exported!) without writing a single line of code.

I still hold my opinion, though that the site editor interface is confusing. I think a pleasant user experience is a far ways off but looking forward to the next WordPress 6.0 release for better user experience.

Barriers to designing themes is getting lower.

It’s less about PHP and template files, and more about developing patterns and creating content. That sounds exactly what a content management system should be designed to do! I am already excited with new features being considered for the WordPress 6.0 release.


There is already a ton of other articles that cover WordPress Block Themes, full-site editing, and the block editor. And many of those came before WordPress 5.9 was released!

So, in addition to this article, here’s a collection of others for you to consider as you begin or continue down your journey of WordPress blocks and site editing.

WordPress 5.9

Site editor and block themes

Selected blog posts

As expected in beta testing, the site editor is still intimating and confusing, nevertheless, I am finding it a fun to work with block themes. Indeed, I have been already modifying Twenty Twenty-Two as a child theme and plan to create style alternatives using single theme.json file.

Have you been using block themes in your project, if so, share your experience and thoughts; I love reading any comments and feedback!

A Deep Introduction to WordPress Block Themes originally published on CSS-Tricks. You should get the newsletter and become a supporter.


, , , ,

Defining and Applying UI Themes Using the Mimcss CSS-in-JS Library

Theming UI refers to the ability to perform a change in visual styles in a consistent manner that defines the “look and feel” of a site. Swapping color palettes, à la dark mode or some other means, is a good example. From the user’s perspective, theming involves changing visual styles, whether it’s with UI for selecting a theme style, or the site automatically respecting the user’s color theme preference at the OS-level. From the developer’s perspective, tools used for theming should be easy-to-use and define themes at develop-time, before applying them at runtime.

This article describes how to approach theming with Mimcss, a CSS-in-JS library, using class inheritance—a method that should be intuitive for most developer as theming is usually about overriding CSS property values, and inheritance is perfect for those overrides.

Full discloser: I am the author of Mimcss. If you consider this a shameless promotion, you are not far from the truth. Nevertheless, I really do believe that the theming technique we’re covering in this article is unique, intuitive and worth exploring.

General theming considerations

Styling in web UI is implemented by having HTML elements reference CSS entities (classes, IDs, etc.). Since both HTML and CSS are dynamic in nature, changing visual representation can be achieved by one of the following methods:

  1. Changing the CSS selector of an HTML element, such as a different class name or ID.
  2. Changing actual CSS styling for that HTML element while preserving the selector.

Depending on the context, one method can be more efficient than another. Themes are usually defined by a limited number of style entities. Yes, themes are more than just a collection of colors and fonts—they can define paddings, margins, layouts, animations and so on . However, it seems that the number of CSS entities defined by a theme might be less than a number of HTML elements referencing these entities, especially if we are talking about heavy widgets such as tables, trees or code editors. With this assumption, when we want to change a theme, we’d rather replace style definitions than go over the HTML elements and (most likely) change the values of their class attributes.

Theming in plain CSS

In regular CSS, one way theming is supported is by using alternate stylesheets. This allows developers to link up multiple CSS files in the HTML <head>:

<link href="default.css" rel="stylesheet" type="text/css" title="Default Style"> <link href="fancy.css" rel="alternate stylesheet" type="text/css" title="Fancy"> <link href="basic.css" rel="alternate stylesheet" type="text/css" title="Basic">

Only one of the above stylesheets can be active at any given time and browsers are expected to provide the UI through which the user chooses a theme name taken from the values of the <link> element’s title attribute. The CSS rule names (i.e. class names) within the alternative stylesheets are expected to be identical, like:

/* default.css */ .element {   color: #fff; }  /* basic.css */ .element {   color: #333; }

This way, when the browser activates a different stylesheet, no HTML changes are required. The browser just recalculates styles (and layout) and repaints the page based on the “winning” values, as determined by The Cascade.

Alternate stylesheets, unfortunately, are not well-supported by mainstream browsers and, in some of them, work only with special extensions. As we will see later, Mimcss builds upon the idea of alternate stylesheets, but leverages it in a pure TypeScript framework.

Theming in CSS-in-JS

There are too many CSS-in-JS libraries out there, and there is no way we can completely cover how theming works in CSS-in-JS in a single post to do it justice. As far as CSS-in-JS libraries that are tightly integrated with React (e.g. Styled Components), theming is implemented on the ThemeProvider component and the Context API, or on the withTheme higher-order component. In both cases, changing a theme leads to re-rendering. As far as CSS-in-JS libraries that are framework-agnostic, theming is achieved via proprietary mechanisms, if theming is even supported at all.

The majority of the CSS-in-JS libraries—both React-specific and framework-agnostic—are focused on “scoping” style rules to components and thus are mostly concerned with creating unique names for CSS entities (e.g. CSS classes). In such environments, changing a theme necessarily means changing the HTML. This goes against the alternative stylesheets approach described above, in which theming is achieved by just changing the styles.

Here is where Mimcss library is different. It tries to combine the best of both theming worlds. On one hand, Mimcss follows the alternate stylesheets approach by defining multiple variants of stylesheets with identically named CSS entities. On the other hand, it offers the object-oriented approach and powerful TypeScript typing system with all the advantages of CSS-in-JS dynamic programming and type safety.

Theming in Mimcss

Mimcss is in that latter group of CSS-in-JS libraries in that it’s framework-agnostic. But it’s also created with the primary objective of allowing everything that native CSS allows in a type-safe manner, while leveraging the full power of the TypeScript’s typing system. In particular, Mimcss uses TypeScript classes to mimic the native CSS stylesheet files. Just as CSS files contain rules, the Mimcss Style Definition classes contain rules.

Classes open up the opportunity to use class inheritance to implement theming. The general idea is that a base class declares CSS rules used by the themes while derived classes provide different style property values for these rules. This is very similar to the native alternative stylesheets approach: activate a different theme class and, without any changes to the HTML code, the styles change.

But first, let’s very briefly touch on how styles are defined in Mimcss.

Mimcss basics

Stylesheets in Mimcss are modeled as Style Definition classes, which define CSS rules as their properties. For example:

import * as css from "mimcss"  class MyStyles extends css.StyleDefinition {   significant = this.$  class({     color: "orange",     fontStyle: "italic"   })    critical = this.$  id({     color: "red",     fontWeight: 700   }) }

The Mimcss syntax tries to be as close to regular CSS as possible. It is slightly more verbose, of course; after all, it is pure TypeScript that doesn’t require any plug-ins or pre-processing. But it still follows regular CSS patterns: for every rule, there is the rule name (e.g. significant), what type of rule it is (e.g. $ class), and the style properties the rule contains.

In addition to CSS classes and IDs, style definition properties can define other CSS rules, e.g. tags, keyframes, custom CSS properties, style rules with arbitrary selectors, media, @font-face, counters, and so on. Mimcss also supports nested rules including those with pseudo classes and pseudo-elements.

After a style definition class is defined, the styles should be activated:

let styles = css.activate(MyStyles);

Activating styles creates an instance of the style definition class and writes the CSS rules to the DOM. In order to use the styles, we reference the instance’s properties in our HTML rendering code:

render() {   return <div>     <p className={styles.significant.name}>       This is a significant paragraph.     </p>     <p id={styles.critical.name}>       This is a critical paragraph.     </p>   </div> }

We use styles.significant.name as a CSS class name. Note that the styles.significant property is not a string, but an object that has the name property and the CSS class name. The property itself also provides access to the CSS Object Model rule, which allows direct rule manipulation; this, however, is outside of the scope of this article (although Louis Lazaris has a great article on it).

If the styles are no longer needed, they can be deactivated which removes them from the DOM:


The CSS class and ID names are uniquely generated by Mimcss. The generation mechanism is different in development and production versions of the library. For example, for the significant CSS class, the name is generated as MyStyles_significant in the development version, and as something like n2 in the production version. The names are generated when the style definition class is activated for the first time and they remain the same no matter how many times the class is activated and deactivated. How the names are generated depends on in what class they were first declared and this becomes very important when we start inheriting style definitions.

Style definition inheritance

Let’s look at a simple example and see what Mimcss does in the presence of inheritance:

class Base extends css.StyleDefinition {   pad4 = this.$  class({ padding: 4 }) } class Derived extends Base {   pad8 = this.$  class({ padding: 8 }) } let derived = css.activate(Derived);

Nothing surprising happens when we activate the Derived class: the derived variable provides access to both the pad4 and the pad8 CSS classes. Mimcss generates a unique CSS class name for each of these properties. The names of the classes are Base_pad4 and Derived_pad8 in the development version of the library.

Interesting things start happening when the Derived class overrides a property from the base class:

class Base extends css.StyleDefinition {   pad = this.$  class({ padding: 4 }) } class Derived extends Base {   pad = this.$  class({ padding: 8 }) } let derived = css.activate(Derived);

There is a single name generated for the derived.pad.name variable. The name is Base_pad; however, the style is { padding: 8px }. That is, the name is generated using the name of the base class, while the style is taken from the derived class.

Let’s try another style definition class that derives from the same Base class:

class AnotherDerived extends Base {   pad = this.$  class({ padding: 16 }) } let anotherDerived = css.activate(AnotherDerived);

As expected, the anotherDerived.pad.name has the value of Base_pad and the style is { padding: 16px }. Thus, no matter how many different derived classes we may have, they all use the same name for the inherited properties, but different styles are assigned to them. This is the key Mimcss feature that allows us to use style definition inheritance for theming.

Creating themes in Mimcss

The main idea of theming in Mimcss is to have a theme declaration class that declares several CSS rules, and to have multiple implementation classes that are derived from the declaration while overriding these rules by providing actual styles values. When we need CSS class names, as well as other named CSS entities in our code, we can use the properties from the theme declaration class. Then we can activate either this or that implementation class and, voilà, we can completely change the styling of our application with very little code.

Let’s consider a very simple example that nicely demonstrates the overall approach to theming in Mimcss.: a theme simply defines the shape and style of an element’s border.

First, we need to create the theme declaration class. Theme declarations are classes that derive from the ThemeDefinition class, which itself derives from the StyleDefinition class (there is an explanation why we need the ThemeDefinition class and why themes should not derive directly from the StyleDefinition class, but this is a topic for another day).

class BorderTheme extends css.ThemeDefinition {   borderShape = this.$  class() }

The BorderTheme class defines a single CSS class, borderShape. Note that we haven’t specified any styles for it. We are using this class only to define the borderShape property type, and let Mimcss create a unique name for it. In a sense, it is a lot like a method declaration in an interface—it declares its signature, which should be implemented by the derived classes.

Now let’s define two actual themes—using SquareBorderTheme and RoundBorderTheme classes—that derive from the BorderTheme class and override the borderShape property by specifying different style parameters.

class SquareBorderTheme extends BorderTheme {   borderShape = this.$  class({     border: ["thin", "solid", "green"],     borderInlineStartWidth: "thick"   }) }  class RoundBorderTheme extends BorderTheme {   borderShape = this.$  class({     border: ["medium", "solid", "blue"],     borderRadius: 8 // Mimcss will convert 8 to 8px   }) }

TypeScript ensures that the derived classes can only override a property using the same type that was declared in the base class which, in our case, is an internal Mimcss type used for defining CSS classes. That means that developers cannot use the borderShape property to mistakenly declare a different CSS rule because it leads to a compilation error.

We can now activate one of the themes as the default theme:

let theme: BorderTheme = css.activate(SquareBorderTheme);

When Mimcss first activates a style definition class, it generates unique names for all of CSS entities defined in the class. As we have seen before, the name generated for the borderShape property is generated once and will be reused when other classes deriving from the BorderTheme class are activated.

The activate function returns an instance of the activated class, which we store in the theme variable of type BorderTheme. Having this variable tells the TypeScript compiler that it has access to all the properties from the BorderTheme. This allows us to write the following rendering code for a fictional component:

render() {   return <div>     <input type="text" className={theme.borderShape.name} />   </div> }

All that is left to write is the code that allows the user to choose one of the two themes and activate it.

onToggleTheme() {   if (theme instanceof SquareBorderTheme)     theme = css.activate(RoundBorderTheme);   else     theme = css.activate(SquareBorderTheme); }

Note that we didn’t have to deactivate the old theme. One of the features of the ThemeDefinition class (as opposed to the StyleDefintion class) is that for every theme declaration class, it allows only a single theme to be active at the same time. That is, in our case, either RoundBorderTheme or SquareBorderTheme can be active, but never both. Of course, for multiple theme hierarchies, multiple themes can be simultaneously active. That is, if we have another hierarchy with the ColorTheme declaration class and the derived DarkTheme and LightTheme classes, a single ColorTheme-derived class can be co-active with a single BorderTheme-derived class. However, DarkTheme and LightTheme cannot be active at the same time.

Referencing Mimcss themes

In the example we just looked at, we used a theme object directly but themes frequently define elements like colors, sizes, and fonts that can be referenced by other style definitions. This is especially useful for separating the code that defines themes from the code that defines styles for a component that only wants to use the elements defined by the currently active theme.

CSS custom properties are perfect for declaring elements from which styles can be built. So, let’s define two custom properties in our themes: one for the foreground color, and one for the background color. We can also create a simple component and define a separate style definition class for it. Here is how we define the theme declaration class:

class ColorTheme extends css.ThemeDefinition {   bgColor = this.$  var( "color")   frColor = this.$  var( "color") }

The $ var method defines a CSS custom property. The first parameter specifies the name of the CSS style property, which determines acceptable property values. Note that we don’t specify the actual values here; in the declaration class, we only want Mimcss to create unique names for the custom CSS properties (e.g. --n13) while the values are specified in the theme implementation classes, which we do next.

class LightTheme extends ColorTheme {   bgColor = this.$  var( "color", "white")   frColor = this.$  var( "color", "black") }  class DarkTheme extendsBorderTheme {   bgColor = this.$  var( "color", "black")   frColor = this.$  var( "color", "white") }

Thanks to the Mimcss (and of course TypeScript’s) typing system, developers cannot mistakenly reuse, say, the bgColor property with a different type; nor they can specify values that are not acceptable for a color type. Doing so would immediately produce a compilation error, which may save developers quite a few cycles (one of the declared goals of Mimcss).

Let’s define styles for our component by referencing the theme’s custom CSS properties:

class MyStyles extends css.StyleDefinition {   theme = this.$  use(ColorTheme)    container = this.$  class({     color: this.theme.fgColor,     backgroundColor: this.theme.bgColor,   }) }

The MyStyles style definition class references the ColorTheme class by calling the Mimcss $ use method. This returns an instance of the ColorTheme class through which all its properties can be accessed and used to assign values to CSS properties.

We don’t need to write the var() function invocation because it’s already done by Mimcss when the $ var property is referenced. In effect, the CSS class for the container property creates the following CSS rule (with uniquely generated names, of course):

.container {   color: var(--fgColor);   backgroundColor: var(--bgColor); }

Now we can define our component (in pseudo-React style):

class MyComponent extends Component {   private styles = css.activate(MyStyles);    componentWillUnmount()   {     css.deactivate(this.styles);   }    render()   {     return <div className={this.styles.container.name}>       This area will change colors depending on a selected theme.     </div>   } }

Note one important thing in the above code: our component is completely decoupled from the classes that implement actual themes. The only class our component needs to know about is the theme declaration class ColorTheme. This opens a door to easily “externalize” creation of themes—they can be created by third-party vendors and delivered as regular JavaScript packages. As long as they derive from the ColorTheme class, they can be activated and our component reflects their values.

Imagine creating a theme declaration class for, say, Material Design styles along with multiple theme classes that derive from this class. The only caveat is that since we are using an existing system, the actual names of the CSS properties cannot be generated by Mimcss—they must be the exact names that the Material Design system uses (e.g. --mdc-theme--primary). Thankfully, for all named CSS entities, Mimcss provides a way to override its internal name generation mechanism and use an explicitly provided name. Here is how it can be done with Material Design CSS properties:

class MaterialDesignThemeBase extends css.ThemeDefinition {   primary = this.$  var( "color", undefined, "mdc-theme--primary")   onPrimary = this.$  var( "color", undefined, "mdc-theme--on-primary")   // ... }

The third parameter in the $ var call is the name, which is given to the CSS custom property. The second parameter is set to undefined meaning we aren’t providing any value for the property since this is a theme declaration, and not a concrete theme implementation.

The implementation classes do not need to worry about specifying the correct names because all name assignments are based on the theme declaration class:

class MyMaterialDesignTheme extends MaterialDesignThemeBase {   primary = this.$  var( "color", "lightslategray")   onPrimary = this.$  var( "color", "navy")   // ... }

Multiple themes on one page

As mentioned earlier, only a single theme implementation can be active from among the themes derived from the same theme declaration class. The reason is that different theme implementations define different values for the CSS rules with the same names. Thus, if multiple theme implementations were allowed to be active at the same time, we would have multiple definitions of identically-named CSS rules. This is, of course, a recipe for disaster.

Normally, having a single theme active at a time is not a problem at all—it is likely what we want in most cases. Themes usually define the overall look and feel of the entire page and there is no need to have different page sections to use different themes. What if, however, we are in that rare situation where we do need to apply different themes to different parts of our page? For example, what if before a user chooses a light or dark theme, we want to allow them to compare the two modes side-by-side?

The solution is based on the fact that custom CSS properties can be redefined under CSS rules. Since theme definition classes usually contain a lot of custom CSS properties, Mimcss provides an easy way to use their values from different themes under different CSS rules.

Let’s consider an example where we need to display two elements using two different themes on the same page. The idea is to create a style definition class for our component so that we could write the following rendering code:

public render() {   return <div>     <div className={this.styles.top.name}>       This should be black text on white background     </div>     <div className={this.styles.bottom.name}>       This should be white text on black background     </div>   </div> }

We need to define the CSS top and bottom classes so that we redefine the custom properties under each of them taking values from different themes. We essentially want to have the following CSS:

.block {   backgroundColor: var(--bgColor);   color: var(--fgColor); }  .block.top {   --bgColor: while;   --fgColor: black; }  .block.bottom {   --bgColor: black;   --fgColor: white; }

We use the block class for optimization purposes and to showcase how Mimcss handles inheriting CSS styles, but it is optional.

Here is how this is done in Mimcss:

class MyStyles extends css.StyleDefinition {   theme = this.$  use(ColorTheme)    block = this.$  class({     backgroundColor: this.theme.bgColor,     color: this.theme.fgColor   })    top = this.$  class({     "++": this.block,     "--": [LightTheme],   })    bottom = this.$  class({     "++": this.block,     "--": [DarkTheme],   }) }

Just as we did previously, we reference our ColorTheme declaration class. Then we define a helper block CSS class, which sets the foreground and background colors using the custom CSS properties from the theme. Then we define the top and bottom classes and use the "++" property to indicate that they inherit from the block class. Mimcss supports several methods of style inheritance; the "++" property simply appends the name of the referenced class to our class name. That is, the value returned by the styles.top.name is "top block" where we’re combining the two CSS classes (the actual names are randomly generated, so it would be something like "n153 n459").

Then we use the "--" property to set values of the custom CSS variables. Mimcss supports several methods of redefining custom CSS properties in a ruleset; in our case, we just reference a corresponding theme definition class. This causes Mimcss to redefine all custom CSS properties found in the theme class with their corresponding values.

What do you think?

Theming in Mimcss is intentionally based on style definition inheritance. We looked at exactly how this works, where we get the best of both theming worlds: the ability to use alternate stylesheets alongside the ability to swap out CSS property values using an object-oriented approach.

At runtime, Mimcss applies a theme without changing the HTML whatsoever. At build-time, Mimcss leverages the well-tried and easy-to-use class inheritance technique. Please check out the Mimcss documentation for a much deeper dive on the things we covered here. You can also visit the Mimcss Playground where you can explore a number of examples and easily try your own code.

And, of course, tell me what you think of this approach! This has been my go-to solution for theming and I’d like to continue making it stronger based on feedback from developers like yourself.

The post Defining and Applying UI Themes Using the Mimcss CSS-in-JS Library appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , , , , ,

Easy Dark Mode (and Multiple Color Themes!) in React

I was working on a large React application for a startup, and aside from just wanting some good strategies to keep our styles organized, I wanted to give this whole “dark mode” thing a shot. With the huge ecosystem around React, you might think that there would be a go-to solution for style themes, but a little web searching shows that really isn’t the case.

There are plenty of different options out there, but many of them tie into very specific CSS strategies, like using CSS Modules, some form of CSS-in-JS, etc. I also found tools specific to certain frameworks, like Gatsby, but not a generic React project. What I was looking for was a basic system that’s easy to set up and work with without jumping through a ton of hoops; something fast, something easy to get a whole team of front-end and full-stack developers onboarded with quickly.

The existing solution I liked the best centered around using CSS variables and data attributes, found in this StackOverflow answer. But that also relied on some useRef stuff that felt hack-y. As they say in every infomercial ever, there’s got to be a better way!

Fortunately, there is. By combining that general CSS variable strategy with the beautiful useLocalStorage hook, we have a powerful, easy-to-use theming system. I’m going to walk through setting this thing up and running it, starting from a brand new React app. And if you stick around to the end, I also show you how to integrate it with react-scoped-css, which is what makes this my absolutely preferred way to work with CSS in React.

Project setup

Let’s pick this up at a very good place to start: the beginning.

This guide assumes a basic familiarity with CSS, JavaScript, and React.

First, make sure you have a recent version of Node and npm installed. Then navigate to whatever folder you want your project to live in, run git bash there (or your preferred command line tool), then run:

npx create-react-app easy-react-themes --template typescript

Swap out easy-react-themes with the name of your project, and feel free to leave off the --template typescript if you’d rather work in JavaScript. I happen to like TypeScript but it genuinely makes no difference for this guide, other than files ending in .ts/.tsx vs .js/.jsx.

Now we’ll open up our brand new project in a code editor. I’m using VS Code for this example, and if you are too, then you can run these commands:

cd easy-react-themes code .
Not much to look at yet, but we’ll change that!

Running npm start next starts your development server, and produces this in a new browser window:

And, finally, go ahead and install the use-local-storage package with:

npm i use-local-storage

And that’s it for the initial setup of the project!

Code setup

Open the App.tsx file and get rid of the stuff we don’t need.

We want to go from this…

…to this.

Delete the entire content in App.css:

Woot! Now let’s create our themes! Open up the index.css file and add this to it:

:root {   --background: white;   --text-primary: black;   --text-secondary: royalblue;   --accent: purple; } [data-theme='dark'] {   --background: black;   --text-primary: white;   --text-secondary: grey;   --accent: darkred; }

Here’s what we have so far:

See what we just did there? If you’re unfamiliar with CSS Custom Properties (as also known as CSS variables), they allow us to define a value to be used elsewhere in our stylesheets, with the pattern being --key: value. In this case, we’re only defining a few colors and applying them to the :root element so they can be used be used wherever else we need them across the whole React project.

The second part, starting with [data-theme='dark'], is where things get interesting. HTML (and JSX, which we’re using to create HTML in React) allows us to set completely arbitrary properties for our HTML elements with the data-* attribute. In this case, we are giving the outermost <div> element of our application a data-theme attribute and toggling its value between light and dark. When it’s dark, the CSS[data-theme='dark'] section overrides the variables we defined in the :root, so any styling which relies on those variables is toggled as well.

Let’s put that into practice. Back in App.tsx, let’s give React a way to track the theme state. We’d normally use something like useState for local state, or Redux for global state management, but we also want the user’s theme selection to stick around if they leave our app and come back later. While we could use Redux and redux-persist, that’s way overkill for our needs.

Instead, we’re using the useLocalStorage hook we installed earlier. It gives us a way to store things in local storage, as you might expect, but as a React hook, it maintains stateful knowledge of what it’s doing with localStorage, making our lives easy.

Some of you might be thinking “Oh no, what if the page renders before our JavaScript checks in with localStorage and we get the dreaded “flash of wrong theme?” But you don’t have to worry about that here since our React app is completely rendered client-side; the initial HTML file is basically a skeleton with a with a single <div> that React attaches the app to. All of the final HTML elements are generated by JavaScript after checking localStorage.

So, first, import the hook at the top of App.tsx with:

import useLocalStorage from 'use-local-storage'

Then, inside our App component, we use it with:

const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light'); 

This does a few things for us. First, we’re checking if the user has set a theme preference in their browser settings. Then we’re creating a stateful theme variable that is tied to localStorage and the setTheme function to update theme. useLocalStorage adds a key:value pair to localStorage if it doesn’t already exist, which defaults to theme: "light", unless our matchMedia check comes back as true, in which case it’s theme: "dark". That way, we’re gracefully handling both possibilities of keeping the theme settings for a returning user, or respecting their browser settings by default if we’re working with new users.

Next, we add a tiny bit of content to the App component so we have some elements to style, along with a button and function to actually allow us to toggle the theme.

The finished App.tsx file

The secret sauce is on line 14 where we’ve added data-theme={theme} to our top-level <div>. Now, by switching the value of theme, we are choosing whether or not to override the CSS variables in :root with the ones in the data-theme='dark' section of the index.css file.

The last thing we need to do is add some styling that uses those CSS variables we made earlier, and it’ll up and running! Open App.css and drop this CSS in there:

.App {   color: var(--text-primary);   background-color: var(--background);   font-size: large;   font-weight: bold;   padding: 20px;   height: calc(100vh - 40px);   transition: all .5s; } button {   color: var(--text-primary);   background-color: var(--background);   border: 2px var(--text-primary) solid;   float: right;   transition: all .5s; }

Now the background and text for the main <div>, and the background, text, and outline of the <button> rely on the CSS variables. That means when the theme changes, everything that depends on those variables update as well. Also note that we added transition: all .5s to both the App and <button> for a smooth transition between color themes.

Now, head back to the browser that’s running the app, and here’s what we get:

Tada! Let’s add another component just to show how the system works if we’re building out a real app. We’ll add a /components folder in /src, put a /square folder in /components, and add a Square.tsx and square.css, like so:

Let’s import it back into App.tsx, like so:

Here’s what we have now as a result:

And there we go! Obviously, this is a pretty basic case where we’re only using a default (light) theme, and a secondary (dark) theme. But if your application calls for it, this system could be used to implement multiple theme options. Personally, I’m thinking of giving my next project options for light, dark, chocolate, and strawberry—go nuts!

Bonus: Integrating with React Scoped CSS:

Using React Scoped CSS is my favorite way to keep each component’s CSS encapsulated to prevent name collision messiness and unintended style inheritance. My previous go-to for this was CSS Modules, but that has the downside of making the in-browser DOM look like a robot wrote all of the class names… because that’s exactly the case. This lack of human-readability makes debugging far more annoying than it has to be. Enter React Scoped CSS. We get to keep writing CSS (or Sass) exactly the way we have been, and the output looks like a human wrote it.

Seeing as the the React Scoped CSS repo provides full and detailed installation instructions, I’ll merely summarize them here.

First, install and configure Create React App Configuration Override (CRACO) according to their instructions. Craco is a tool that lets us override some of the default webpack configuration that’s bundled into create-react-app (CRA). Normally, if you want to adjust webpack in a CRA project, you first have to “eject” the project, which is an irreversible operation, and makes you fully responsible for all of the dependencies that are normally handled for you. You usually want to avoid ejecting unless you really, really know what you’re doing and have a good reason to go down that road. Instead, CRACO let’s us make some minor adjustments to our webpack config without things getting messy.

Once that’s done, install the React Scoped CSS package:

npm i craco-plugin-scoped-css

(The README instructions use yarn for installation instead of npm, but either is fine.) Now that it’s installed, simply rename the CSS files by adding .scoped before the .css, like so:

app.css -> app.scoped.css

And we need to make sure we’re using a new name when importing that CSS into a component:

import './app.css'; -> import './app.scoped.css';

Now all of the CSS is encapsulated so that it only applies to the components they’re imported into. It works by using data-* properties, much like our theme system, so when a scoped CSS file is imported into a component, all of that component’s elements are labeled with a property, like data-v-46ef2374, and the styles from that file are wrapped so that they only apply to elements with that exact data property.

That’s all wonderful, but the little trick to making that work with this theming system is that we explicitly don’t want the CSS variables encapsulated; we want them applied to the whole project. So, we simply don’t change index.css to have scoped in it… in other words, we can leave that CSS file alone. That’s it! Now we have a powerful theming system working in harmony with scoped CSS— we’re living the dream!

Thank you so much taking a read through this guide, and if it helped you build something awesome, I would love to know about it!

The post Easy Dark Mode (and Multiple Color Themes!) in React appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , , , , ,

A DRY Approach to Color Themes in CSS

The other day, Florens Verschelde asked about defining dark mode styles for both a class and a media query, without repeat CSS custom properties declarations. I had run into this issue in the past but hadn’t come up with a proper solution.

What we want is to avoid redefining—and thus repeating—custom properties when switching between light and dark modes. That’s the goal of DRY (Don’t Repeat Yourself) programming, but the typical pattern for switching themes is usually something like this:

:root {   --background: #fff;   --text-color: #0f1031;   /* etc. */ }  @media (prefers-color-scheme: dark) {   :root {     --background: #0f1031;     --text-color: #fff;     /* etc. */   } }

See what I mean? Sure, it might not seem like a big deal in an abbreviated example like this, but imagine juggling dozens of custom properties at a time—that’s a lot of duplication!

Then I remembered Lea Verou’s trick using --var: ;, and while it didn’t hit me at first, I found a way to make it work: not with var(--light-value, var(--dark-value)) or some nested combination like that, but by using both side by side!

Certainly, someone smarter must have discovered this before me, but I haven‘t heard of leveraging (or rather abusing) CSS custom properties to achieve this. Without further ado, here’s the idea:

--color: var(--light, orchid) var(--dark, rebeccapurple);

If the --light value is set to initial, the fallback will be used (orchid), which means --dark should be set to a whitespace character (which is a valid value), making the final computed value look like this:

--color: orchid  ; /* Note the additional whitespace */

Conversely, if --light is set to a whitespace and --dark to initial, we end up with a computed value of:

--color:   rebeccapurple; /* Again, note the whitespace */

Now, this is great but we do need to define the --light and --dark custom properties, based on the context. The user can have a system preference in place (either light or dark), or can have toggled the website‘s theme with some UI element. Just like Florens‘s example, we’ll define these three cases, with some minor readability enhancement that Lea proposed using “on” and “off” constants to make it easier to understand at a glance:

:root {    /* Thanks Lea Verou! */   --ON: initial;   --OFF: ; }  /* Light theme is on by default */ .theme-default, .theme-light {   --light: var(--ON);   --dark: var(--OFF); }  /* Dark theme is off by default */ .theme-dark {   --light: var(--OFF);   --dark: var(--ON); }  /* If user prefers dark, then that's what they'll get */ @media (prefers-color-scheme: dark) {   .theme-default {     --light: var(--OFF);     --dark: var(--ON);   } }

We can then set up all of our theme variables in a single declaration, without repetition. In this example, the theme-* classes are set to the html element, so we can use :root as a selector, as many people like to do, but you could set them on the body, if the cascading nature of the custom properties makes more sense that way:

:root {   --text: var(--light, black) var(--dark, white);   --bg: var(--light, orchid) var(--dark, rebeccapurple); }

And to use them, we use var() with built-in fallbacks, because we like being careful:

body {   color: var(--text, navy);   background-color: var(--bg, lightgray); }

Hopefully you’re already starting to see the benefit here. Instead of defining and switching armloads of custom properties, we’re dealing with two and setting all the others just once on :root. That’s a huge improvement from where we started.

Even DRYer with pre-processors

If you were to show me this following line of code out of context, I’d certainly be confused because a color is a single value, not two!

--text: var(--light, black) var(--dark, white);

That’s why I prefer to abstract things a bit. We can set up a function with our favorite pre-processor, which is Sass in my case. If we keep our code above defining our --light and --dark values in various contexts, we need to make a change only on the actual custom property declaration. Let’s create a light-dark function that returns the CSS syntax for us:

@function light-dark($  light, $  dark) {   @return var(--light, #{ $  light }) var(--dark, #{ $  dark }); }

And we’d use it like this:

:root {    --text: #{ light-dark(black, white) };    --bg: #{ light-dark(orchid, rebeccapurple) };    --accent: #{ light-dark(#6d386b, #b399cc) }; }

You’ll notice there are interpolation delimiters #{ … } around the function call. Without these, Sass would output the code as is (like a vanilla CSS function). You can play around with various implementations of this but the syntax complexity is up to your tastes.

How’s that for a much DRYer codebase?

More than one theme? No problem!

You could potentially do this with more than two modes. The more themes you add, the more complex it becomes to manage, but the point is that it is possible! We add another theme set of ON or OFF variables, and set an extra variable in the list of values.

.theme-pride {   --light: var(--OFF);   --dark: var(--OFF);   --pride: var(--ON); }  :root {   --text:     var(--light, black)     var(--dark, white)     var(--pride, #ff8c00)   ; /* Line breaks are absolutely valid */    /* Other variables to declare… */ }

Is this hacky? Yes, it absolutely is. Is this a great use case for potential, not-yet-existing CSS booleans? Well, that’s the dream.

How about you? Is this something you’ve figured out with a different approach? Share it in the comments!

The post A DRY Approach to Color Themes in CSS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, ,