Using theme.json in a classic theme

Published on:
Last Updated:
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

Default Settings

A current issue in Gutenberg (open ticket) is by using a theme.json file you need to opt-in to all the default settings for Gutenberg, otherwise they will be disabled.

For now, to opt-in to each, you can add the following:

{
    "version": 1,
    "settings": {
        "border": {
            "color": true,
            "radius": true,
            "style": true,
            "width": true
        },
        "spacing": {
            "margin": true,
            "padding": true,
            "units": true
        },
        "typography": {
            "customFontSize": true,
            "lineHeight": true
        } 
    }
}

The issue is being addressed now and looks like it may require only a single property to opt-in to all setting for WP 5.9.

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.

7 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, true );
wp> $th   = new WP_Theme_JSON( $dec );
wp> $th->get_stylesheet();
Trevor R November 8, 2021 Reply

I agree with Carrie, it’s really handy to have a list of all the custom properties your `theme.json` generates. Thanks @mkaz for coming up with a solution for this! Unfortunately for me `$th = new WP_Theme_JSON( $dec );` chucks this error on my system:

“`
PHP Fatal error: Uncaught Error: Cannot use object of type stdClass as array in /home/myuser/sites/wpdev/wp-includes/class-wp-theme-json.php:279
“`

Any ideas why that may be happening?

Long term it would be awesome if someone comes up with a more automated way to grab these values. One idea would be an extension for VS Code so the values could be updated on the fly and be usable for autocomplete purposes.

mkaz November 8, 2021 Reply

I forgot the `true` parameter in the decode function, I updated above. Also double check the path to the theme.json is correct, and that you have no syntax errors in your JSON.

For developing with theme.json in VS Code, see this tip I added recently to the docs on using a schema: https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/#developing-with-theme-json

Add: `”$schema”: “https://json.schemastore.org/theme-v1.json”` to the top of you JSON file and you’ll get autocomplete and tips.

Trevor R November 14, 2021

Thanks for update, that works perfectly now! Yes I did recently see the note about adding the $schema to the top of your JSON file and that’s a game changer. Love it! Thank you for doing that. I’ve been telling all my WP dev connections about it.

However, what I meant was it would be great to automate the generation of your theme.json CSS custom properties (i.e. –wp–preset–color–tertiary: #acacac;) so that if I need to use those in my theme CSS or custom blocks, all those WP theme.json generated ones are readily available. Hope that makes more sense?

Juergen November 18, 2021 Reply

So far I haven’t converted my site to Gutenberg, but this approach to the global settings very much appeals to me.
Years ago, when I was creating websites for a living, I had written my own CSS file for the Zurb Foundation framework – with exactly the same idea!

I was able to set fonts and colours for the new website once and then called classnames like “bodyfont, medium, dark” or “headerfont, large, color1” to format individual sections.

That way I could quickly adjust pre-made page templates for a client’s individual colour scheme and font choices.

Leave a Reply