Using theme.json in a classic theme

Published on:
Category: WordPress

WordPress 5.8 introduced theme.json, a way to specify styles to be generated for the editor and front-end. Theme.json is part of a suite of full site editing tools being developed for WordPress. You can start using it now in existing themes.

Let’s dive in and show a few things you can do with it.

Colors

You can define a color palette that will show in the editor. Previously, you needed to specify a palette in functions.php, otherwise you will see the default colors.

Default color palettes

If you want to create your own theme specific colors, you can specify them in the theme.json file. Create a file called theme.json in the root of your theme, WordPress will loud it if it exists, no enqueuing necessary.

{
    "version": 1,
    "settings": {
        "color": {
            "palette": [
                {
                    "name": "Black",
                    "slug": "black",
                    "color": "#111111"
                },
                {
                    "name": "White",
                    "slug": "white",
                    "color": "#ffffff"
                },
                {
                    "name": "Dark Blue",
                    "slug": "dark-blue",
                    "color": "#1D3567"
                },
                {
                    "name": "Cyan",
                    "slug": "cyan",
                    "color": "#55BABD"
                },
                {
                    "name": "Orange",
                    "slug": "orange",
                    "color": "#E36137"
                }

            ]
        }
    }
}
Theme color palette

NOTE: If nothing is working quite right, make sure the "version": 1 property is specified. I played with the feature when it was still experimental and my file initially did not have that property.

The above still allows an author to use their own custom color. If you really want to lock down a theme color choices, you can add the property "custom": false to the color settings and it removes the option for adding custom colors.

{
    "version": 1,
    "settings": {
        "color": {
            "custom": false,
            "palette": [ ... ]
        }
    }
}
Theme color palette , no custom color

What does theme.json do ?

WordPress reads the theme.json file and generates a set of CSS properties that is added to each page. For the colors example it will create a CSS custom property (variable) and utility classes based on the slug specified. For the dark blue example it would be:

--wp--preset--color--dark-blue: #1D3567;

.has-dark-blue-color {
    color: var(--wp--preset--color--dark-blue);
}

.has-dark-blue-background {
    background-color: var(--wp--preset--color--dark-blue);  
}

If you are not familiar with CSS custom properties, see the MDN documentation for more.

When you use a color in the block editor, for example when you set the background color on a paragraph, the editor will add the has-dark-blue-background class. This is what is saved to in the post content and served when viewed.

Why use CSS properties? You could imagine the system designed to simply insert the hexadecimal color code each time it is set; preventing the need to generate CSS rules.

Using straight hex codes in the markup would lose out on some flexibility. For example if you need to update the dark blue color. Using theme.json you can update the definition once and everywhere the class is used will use the new colors. If the hex color code was added direct to markup you would need to modify all posts that use it.

Another benefit of theme.json and classnames is the possibility to standardize some of the markup and class names, something long needed for WordPress themes, see Rich Tabor’s proposal for standard naming. It would be awesome to be able to switch a theme and all previously published posts come along for the ride.

Layout

Using theme.json to control layout makes it simpler to manage alignments and widths. This works by default in the editor, but for classic themes will require adding code to your front-end stylesheet. The newer block themes coming in WordPress 5.9 will automatically handle the layouts.

Even if you don’t add any extra code to your front-end, using layout settings is still an easy way control the editor to match your theme’s layout.

Here is what the editor looked like for my theme before using a theme.json. Note the text is full width of the editor, not matching the width of my layout.

Layout in editor before

By adding the following settings to theme.json the editor view matches the published view.

{
    "version": 1,
    "settings": {
        "layout": {
           "contentSize": "720px",
           "wideSize": "960px"
       }
   }
}
Layout in editor after

Note: A recent fix added default styles to the editor if none specified. The fix is now in the Gutenberg plugin and will be deployed in WP 5.9. However, if you want the editor to match your layout styles, it is best to specify in the theme.json.

On the front-end, the layout for my site is a touch more complicated, but if I had a single column layout, I could add these properties to my CSS and have the wide/full layouts work properly.

.entry-content > * {
    max-width: 720px;
    margin-left: auto !important;
    margin-right: auto !important;
}

.entry-content > .alignwide {
    max-width: 960px;
}

.entry-content > .alignfull {
    max-width: none;
}

.entry-content > .alignleft {
    float: left;
    margin-right: 2em;
}

.entry-content > .alignright {
    float: right;
    margin-right: 2em;
}

See the Layout dev note for additional details.

Typography

Just like with colors, you can specify settings for font sizes, likewise you can disable users from specifying their own custom font size, this makes it easier to have consistent typography across the site.

The following example disables the custom font sizes, and creates a set of five sizes to choose from.

{
    "version": 1,
    "settings": {
        "typography": {
            "customFontSize": false,
            "fontSizes": [
                {
                    "slug": "small",
                    "size": "16px",
                    "name": "Small"
                },
                {
                    "slug": "normal",
                    "size": "18px",
                    "name": "Normal"
                },
                {
                    "slug": "large",
                    "size": "24px",
                    "name": "Large"
                },
                {
                    "slug": "extra-large",
                    "size": "28px",
                    "name": "XL"
                },
                {
                    "slug": "huge",
                    "size": "32px",
                    "name": "Huge"
                }
            ]
        }
    }
}

The above JSON definition will generate the following CSS.

--wp--preset--font-size--small: 16px;
--wp--preset--font-size--normal 18px:
--wp--preset--font-size--large: 24px;
--wp--preset--font-size--extra-large: 28px;
--wp--preset--font-size--huge: 32px;


.has-small-font-size {
    font-size: var(--wp--preset--font-size--small) !important;
}

.has-normal-font-size {
    font-size: var(--wp--preset--font-size--normal) !important;
}

.has-large-font-size {
    font-size: var(--wp--preset--font-size--large) !important;
}

.has-extra-large-font-size {
    font-size: var(--wp--preset--font-size--extra-large) !important;
}

.has-huge-font-size {
    font-size: var(--wp--preset--font-size--huge) !important;
}
Font size options

Custom Properties

The colors, layout, and typography are preset properties to set, with theme.json you can create your own custom properties. For example:

{
    "version": 1,
    "settings": {
        "custom": {
            "myBorder": "1px solid #0044CC"
        }
    }
}

The above definition will generate the CSS variable:

--wp--custom--my-border: 1px solid #0044CC;

These can be used in your stylesheet or markup, or as you’ll see below, you can also specify in the theme.json how they should be applied.

Using properties in elements or blocks

The settings is a top level property of theme.json, another is styles that allow you to specify specific styles for blocks or elements.

An example setting the margin, and color for quote blocks,

{
    "version": 1,
    "styles": {
        "blocks": {
            "core/quote": {
                "spacing": {
                    "margin": {
                        "top": "0",
                        "right": "9%",
                        "bottom": "30px",
                        "left": "9%"
                    }
                },
                "color": {
                    "text": "var(--wp--preset--color--orange)"
                }
            }
        }
    }
}

This will produce the following CSS:

.wp-block-quote{
    color: var(--wp--preset--color--orange);
    margin-right: 9%;
    margin-bottom: 30px;
    margin-left: 9%;
}

You can do similar with a small set of HTML elements, WordPress 5.8 it is limited support, but the Gutenberg plugin supports additional with more being added frequently getting ready for WP 5.9. See the documentation for all supported features.

Why specify styles in JSON !?

So a question that comes to my mind is that specifying styles in JSON format is more cumbersome, why do it this way instead of just normal CSS?

First in my book is the JSON format is structured. I’m not sure about your CSS files but my latest themes are growing to over thousands of lines of unorganized mess. Using a structured file makes it easier to reason about.

Secondly, the single JSON format works for both the editor and front-end, so the design will look the same in both spots. You don’t need two different style sheets or a build system using sass to organize.

Additionally, the theme.json also handles theme settings that used to go in add_theme_support() in a functions.php file. Consolidating settings down to a single source makes it easier to manage.

Lastly, and possible the most important, all themes can use the same format. One of the biggest issues moving from theme to theme is traditionally each theme has its own classes and markup. Using standards will allow a greater ability to migrate from theme to theme with fewer inconsistencies and issues.

Bonus: JSON is a more flexible format than a mix of CSS and PHP, it will allow for composing, mixing and matching JSON snippets from different themes. I can imagine tools will be built to create, edit, share JSON files. Also, you could preview themes by just an API call to fetch a theme.json file from a directory.

The new format to define settings and styles in a structured way really sets up WordPress for a bright future.

Additional resources on theme.json

There is even more you can customize with theme.json, and more being developed for global styles to support global styles and full site editing in WordPress 5.9.

Here is the PR for this site that I added a theme.json file and started to switch over pieces.

3 Comments

Carrie Dils September 7, 2021 Reply

Thanks, Marcus. I really appreciate how you break this out! I’m currently incorporating theme.json into a “classic” theme build and this is helpful.

I’m curious if there’s a tool that parses the JSON and spits out a list of the generated CSS custom properties? I know I can grab them using browser inspect tools to view the CSS, but it would be helpful to get the full list to make it easier to incorporate into my stylesheet.

mkaz September 13, 2021 Reply

Hi Carrie, I’m glad you found it useful.

There isn’t an easy command-line function to convert from JSON to the corresponding CSS. You can look at the WP_Theme_JSON functions for details.

However, what I do is just look at the source of the page and look for the style block with id='global-styles-inline-css' and it is all included. I often will copy from source to an editor for better formatting.

mkaz September 13, 2021 Reply

Here’s how you can get the stylesheet using wp-cli and the WP_Theme_JSON functions:


wp shell
wp> $data = file_get_contents("./theme.json");
wp> $dec  = json_decode( $data );
wp> $th   = new WP_Theme_JSON( $dec );
wp> $th->get_stylesheet();

Leave a Reply