Block themes for classic theme developers – part two

If you bumped into this article by accident and noticed the “part two” in the title, that’s because there is a “part one” ( Block themes for classic theme developers – part one 🔗 ) on this topic, where I wrote about the WordPress Site Editor, Templates, Template Parts and Patterns, the basic principles and how to start creating your own templates, parts, and patterns. I recommend starting with part one:

This article, as well as the previous article, uses starter FSE theme examples. The “Advanced” starter theme variant is generated and downloaded from fullsiteediting.com 🔗

Theme.json

If you followed my previous article, you’ll see I mentioned that the theme.json file is probably the most important part of the FSE theme and this might be the reason I dedicated one article just to theme.json.

The theme.json is a theme configuration file where the block settings, styles, custom templates, and template parts for an FSE theme are set ( 🔗 ).

As a part of an effort to set all configuration and settings in the theme.json, it replaces many of the add_theme_support() function calls:

Theme supportTheme.json setting
add_theme_support( ‘editor-font-sizes’, array() );settings.typography.fontSizes
add_theme_support( ‘custom-line-height’ );settings.typography.lineHeight
add_theme_support( ‘align-wide’ );settings.layout
add_theme_support( ‘editor-color-palette’, array() );settings.color.palette
add_theme_support( ‘editor-gradient-presets’, array() );settings.color.gradients
add_theme_support( ‘custom-spacing’ );settings.spacing
add_theme_support( ‘custom-units’, array() );settings.spacing.units

To keep backward compatibility with add_theme_support, please refer to this table of theme.json replacements 🔗.

When referring to a specific parameter in theme.json, I will use JSON nested object hierarchy reference – for example, settings.color.palette, or styles.color.background (see example above)

The theme.json file configuration can be applied to a theme globally and to individual blocks. Example of how configuration is differently set globally and for blocks:

{
    "settings": {
        "color": { ... }, // Global color settings
        "typography": { ... }, // Global typography settings
        "blocks": {
            "core/group": { 
                "color": { ... }, // Group block color settings
                "typography": { ... } // Group block typography
            }
        }
    }
}

The specification for all main sections in theme.json, with the key:value pairs:

 {
    "$schema": "https://schemas.wp.org/trunk/theme.json",
    "version": 2,
    "settings": {},
    "styles": {},
    "customTemplates": {},
    "templateParts": {},
    "patterns": []
}

Let’s start with settings:

Theme.json – settings

The settings are used primarily to configure the block editor and have no (direct) effect on the global styles. Settings like color palette, gradients, and duotone will create theme block color options and many of them are part of the resulting stylesheet presets. For example spacing.units will set an array of units, like so: ["px","em","rem" ...etc], spacing.blockGap, spacing.padding, or spacing.margin will only allow or disallow those options in blocks and block editor (boolean value). In the example below I edited starter theme spacing to limit spacing units only to px and em and disallowed the block’s padding and margin controls.

{ 
    ... other settings
    "spacing": {
        "units": [ "px", "em", "rem", "vh", "vw", "%" ],
        "blockGap": true
    }
    ... other settings
}
{
     ... other settings
    "spacing": {
        "units": [ "px", "em"],
        "blockGap": true,
        "padding": false,
        "margin": false
    }
    ... other settings
}

Which resulted in limited spacing controls for blocks:

The following example will show how WordPress uses the “settings” section to create presets, or options, with custom CSS properties used for the frontend and editor, in a form of CSS variables:

{
    "$schema": "https://schemas.wp.org/trunk/theme.json",
    "version": 2,
    "settings": {
        "color": {
            "palette": [
                {
                    "name": "Background",
                    "slug": "background",
                    "color": "#333333"
                },
                {
                    "name": "Foreground",
                    "slug": "foreground",
                    "color": "#f5f5f5"
                }
            ]
        },
        "blocks": {
            "core/paragraph": {
                "color": {
                    "palette": [
                        {
                            "name": "Red",
                            "slug": "red",
                            "color": "#ff0000"
                        }
                    ]
                }
            }
        }
    }
}
body {
    --wp--preset--color--background: #333333;
    --wp--preset--color--foreground: #f5f5f5;
}

.wp-block-paragraph {
    --wp--preset--color--red: #ff0000;
}

TIP: notice the $schema key at the top. The schema will provide hints and autocomplete while editing in your IDE of choice. I recommend including the $schema in theme.json as you will probably find it difficult to remember all theme.json settings and properties.
More info about the $schema here 🔗

The “Settings” section has definitions for creating both global and block-specific CSS variables. These presets will add up to a number of WordPress default presets (there are 12 colors, 12 gradients, 8 duotones, and 4 font-size default presets), which I compacted to this list :

  • --wp--preset--color--{color name}
  • --wp--preset--gradient--{gradient name}
  • --wp--preset--duotone--{duotone}
  • --wp--preset--font-size--{font size name}

To see all default WordPress presets, simply temporarily remove all settings in theme.json (leave only opening/closing curly braces {} ) and check the body CSS classes in your browser Development Tools.

All the major sections in the “settings” section are:

"settings":{
    "color": {},
    "typography": {},
    "layout": {},
    "appearanceTools": boolean,
    "custom": {},
    "spacing": {},
    "border": {},
    "blocks": {},
}

Naming schema

How the naming schema applies to the theme.json settings and translates to CSS properties is explained here:

  • Custom Properties: --wp--preset--{preset-category}--{preset-slug} translates to : --wp--preset--color--background globally
  • Classes: .has-{preset-slug}-{preset-category} translates to: .has-background-color for blocks.

Knowing how naming schema translates to CSS properties may come in handy when using presets in the “styles” theme.json category of settings, for example, setting the global background color, by using preset as it translates into a CSS variable (example for usage in “styles” section):

Theme.json

{
    "settings":{
        "color":{
            "palette":[
                {
                    "name":"Background",
                    "slug":"background",
                    "color":"#f5f5f5"
                }
            ]
        }
    }
    "styles": {
        "color": {
            "background": "var(--wp--preset--color--background)"
        }
    }
}

Settings – color

In the example above, the way how settings.color.palette properties are set, applies also to “gradient” and “duotone” settings. As well as for many other theme.json properties: “name”, “slug”, and an intrinsic setting :

"settings":{
    "color":{
        "pallete":[
            {
                "name":"Primary",
                "slug":"primary",
                "color":"#ffffff"
            }, ...other colors in pallete
        ],
        "gradients":[
            {
                "name":"White to black",
                "slug":"white-to-black",
                "gradient":"linear-gradient(#fff 40%,#000 50%)"
            }, ...other gradients
        ],
        "duotone":[
            {
                "name":"Orange blue duotone",
                "slug":"orange-blue-duotone",
                "colors":[ "#FF9900", "#0000FF"]
            }, ... other duotones
        ],
        ... other color settings
    }
}

There are several properties in the “settings” section for disabling block controls too, such as Custom colors, Custom duotone, Custom gradient (Color pickers), Custom font size, Drop cap. All those settings can be enabled with settings.appearanceTools set to true.

I tested how instantly can theme.json (in generated starter theme) settings affect the site appearance, so I simply replaced the settings.color.palette.background and settings.color.palette.foreground “color” values:

The site background is set in styles.color.background section, using the --wp--preset--color--background CSS variable, so that was pretty straightforward. I will cover the “styles” section below, after the “settings” section.

Besides the settings.color (for the color palette, gradients, and duotone), settings that will probably be commonly used are settings.layout, and settings. typography.

Settings – layout

The settings.layout replaces the add_theme_support( 'align-wide' ) and sets the content (that is, blocks) width options – default and wide size (the WordPress takes care of the full width). This is the “layout” section in our starter theme:

{
    "settings":{
       ... other settings
        "layout:{
            "contentSize":"700px",
            "wideSize:"1100px"
        },
        ... other settings
    }
}

Unlike the settings.color presets or settings.typography, the layout setting does not output CSS custom properties (variables). It is used by the nesting-capable blocks (group, columns, cover) and outputs content and wide sizes per block CSS.

Settings – typography

Two most common subsets of definitions in settings.typography sections are settings.typography.fontFamilies and settings.typography.fontSizes. Both subsets are made of an array of a JSON objects with naming schema defining “name”, “slug”, and “fontFamily” and “size”, respectively. Additional property for font families is a “fontFace” property. Example from our starter theme:

Typography section in settings

{
    "settings":{
        "typography":{
            "fontFamilies":[
                {
                    "name": "Serif",
                    "slug": "serif",
                    "fontFamily": "\"Times New Roman\",\"New York\",Times,\"Noto Serif\",serif"
 
                },
                ... other font families
            ],
            "fontSizes":[
                {
                    "name": "Tiny",
                    "slug": "tiny",
                    "size": "0.95rem"
                },
                ... other font sizes
            ],
            ... other typography settings
        }
    }
}

Rendered CSS variables

body {
    --wp--preset--font-family--serif: "Times New Roman","New York",Times,"Noto Serif",serif;
    --wp--preset--font-size--tiny: 0.95rem;
}

The font family object’s additional property “fontFace” has a number of properties for including custom fonts (notice how the $scheme hints for all available properties):

Our starter theme has custom font Lora included in the “assets/fonts” directory, which is great for dealing with performance and privacy issues (WP Tavern 🔗). But there’s another way to include custom fonts in the theme without “manually” downloading and including them in the theme – the WordPress Themes Team WebFonts Loader 🔗 . This script uses WP core WP_Filesystem to download fonts in the “wp-content/fonts” directory and creates the CSS needed. I included the web fonts loader file in the “inc” directory and hooked the loader to the frontend and editor with the wp_add_inline_style ( WordPress.org Code Reference 🔗 ) function:

require get_parent_theme_file_path( 'inc/wptt-webfont-loader.php' );

my_theme_load_font_styles() {
    $font_families = array(
        'Outfit:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;1,100;1,200;1,300;1,400;1,500;1,600',
        'Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;1,100;1,200;1,300;1,400;1,500;1,600',
        'Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;1,100;1,200;1,300;1,400;1,500;1,600',
    );

    $fonts_url = add_query_arg(
        array(
            'family'  => implode( '&family=', $font_families ),
            'display' => 'swap',
        ),
        'https://fonts.googleapis.com/css2'
    );

    return wptt_get_webfont_styles( esc_url_raw( $fonts_url ), 'woff' );
}
// Frontend.
add_action( 'wp_enqueue_scripts', function() {
    // ... enqueue theme style here.
    wp_add_inline_style( 'micemade-fse-store-style', my_theme_load_font_styles() );
} ):
// WP editor.
add_action( 'admin_init', function() {
    wp_add_inline_style( 'wp-block-library', my_theme_load_font_styles() );
} ):

Ok, enough of PHP, let’s go back to our theme.json settings.typography. The PHP code above adds 3 fonts to the loader: “Outfit”, “Poppins”, and “Montserrat”, so we simply need to add them to settings.typography.fontFaces:

{
    "settings":{
        "typography":{
            "fontFamilies":[
                {
                    "name": "Outfit",
                    "slug": "outfit",
                    "fontFamily": "Outfit"
                },
                {
                    "name": "Poppins",
                    "slug": "poppins",
                    "fontFamily": "Poppins"
                },
                ... etc
            ],
        }
    }
}
body {
    --wp--preset--font-family--poppins: Poppins;
    --wp--preset--font-family--outfit: Outfit;
    --wp--preset--font-family--montserrat: Montserrat;
}

Notice the inconsistency in the naming schema:
settings.typography.fontFamilies.fontFamily parses to
–wp–preset–font-family–outfit
settings.color.palette.background parsed to
–wp–preset–color–background

The color preset name skipped the “palette”, and the font family skipped typography.fontFamilies, but added a font family value. For these inconsistencies, you’ll probably have to check the output often.

The settings.typography.fontSize structure follows the similar principle explained above, it creates CSS variables in the following way:

{
    "settings":{
        "typography":{
            "fontFamily": { ... },
            "fontSizes":[
                {
                    "name": "Medium",
                    "slug": "medium",
                    "size": "1.25rem"
                },
                {
                    "name": "Large",
                    "slug": "large",
                    "size": "clamp(2rem, calc(1rem + 1.5vw), 2.75rem)"
                },
                ... etc
            ],
        }
    }
}
body {
    --wp--preset--font-size--medium: 1.25rem;
    --wp--preset--font-size--large: clamp(2rem, calc(1rem + 1.5vw), 2.75rem);
}

In our starter theme (by now you already may have noticed I named mine “Micemade FSE Store” 🙂 ) I replaced fixed font sizes with a nice CSS clamp()function for flexible and responsive font sizes, but only for larger text (mostly headings). With clamp(), headings will nicely adapt to mobile screens. Better than fixed values and it can reduce the CSS media queries usage.
Learn more about the CSS clamp() here 🔗

Settings – custom

Besides the custom presets, described in previous article sections, it is possible to create completely custom properties. Any values declared within the custom section will be transformed to CSS Custom Properties following this naming schema: --wp--custom--<variable-name> 🔗

In our starter theme’s theme.json file, I used the custom properties to define CSS variables for settings.layout:

{
    "settings":{
        "layout":{
            "contentSize":"var(--wp--custom--layout--content-size)",
            "wideSize":"var(--wp--custom--layout--wide-size)"
        },
        "custom":{
            "layout":{
                "contentSize":"min( calc(100vw - 30px), 780px)",
                "wideSize":"min( calc(100vw - 30px), 1280px)"
            }
        }
    }
}

For setting content and wide sizes, I used the CSS min() function, in combination with the calc(), while keeping the space of 30px (15px on each side) on smaller screens. Also, this way the content and wide size values are accessible via body CSS variables, making it possible to use elsewhere (in style.css or other enqueued styles)

body {
    --wp--custom--layout--content-size: min( calc(100vw - 30px), 780px);
    --wp--custom--layout--wide-size: min( calc(100vw - 30px), 1280px);
}

Reminder: the name of a CSS variable is created by adding -- in between each nesting level and camelCase fields are transformed to kebab-case (contentSize to content-size). Something to keep in mind while creating custom settings.

Custom settings can be used in a variety of ways. In the case of our starter theme, there are a number of custom settings. For instance, let’s see how this one is created and used:

"custom": {
    "h-spacing": "2.375rem"
}

Creates a CSS variable with the value --wp--custom--h-spacing: 2.375rem; used in the woocommerce.css (in the “assets/css” dir)

button.wc-block-pagination-page {
    margin: calc(0.2 * var(--wp--custom--h-spacing));
}

And in the footer.html template part

<!-- wp:group {"align":"full","style":{"spacing":{"padding":{"top":"var(--wp--custom--v-spacing, 1.25rem)","right":"var(--wp--custom--h-spacing, 2.375rem)","bottom":"calc(var(--wp--custom--v-spacing, 1.25rem) *2)","left":"var(--wp--custom--h-spacing, 2.375rem)"}}},"layout":{"inherit":true}} -->
<div class="wp-block-group alignfull" style="padding-top:var(--wp--custom--v-spacing, 1.25rem);padding-right:var(--wp--custom--h-spacing, 2.375rem);padding-bottom:calc(var(--wp--custom--v-spacing, 1.25rem) *2);padding-left:var(--wp--custom--h-spacing, 2.375rem)"> ...

Unfortunately, the settings.custom CSS properties cannot be used in the block editor, no custom property is available in block settings. The moment the padding of our footer template part is edited, custom styles are replaced with the new values. However, edits can be reverted to the default theme-defined state.

Settings – border and blocks

The settings.border has only properties that can be enabled or disabled (boolean values) – settings.border.color, settings.border.radius, settings.border.style, and settings.border.width.
But, the settings.appeanceTools: true override these properties.
There are a number of other settings which can be enabled or disabled – for more details check the WP FSE/Global Styles/Enable or disable features 🔗. If you want to limit users, to prevent “dismantling” your theme ( 🙂 ), there’s also a nice cheatsheet on this WP FSE page 🔗 with all things you can disable.

In the settings.blocks section, blocks properties can be set per block. Any block can be customized for any available settings property. This is where the $schema provided hints and autocomplete will come in very handy.

Note: not all settings are relevant for all blocks. The settings section provides an opt-in/opt-out mechanism for themes, but it’s the block’s responsibility to add support for the features that are relevant to it. For example, if a block doesn’t implement the dropCap feature, a theme can’t enable it for such a block through theme.json. ( source 🔗 )

Theme.json – styles

The “styles” section is a configuration of global styles for the theme. This is the place where all the presets and custom settings can be applied to theme layout and style. Unlike the “settings”, “styles” section doesn’t have any influence on block editor settings. The style properties which can be set here are the following:

{
    "styles"{
        "color": {},
        "spacing": {},
        "typography": {},
        "elements": {},
        "blocks": {},
        "border": {},
        "filter": {},
    }
}

There are three levels of style properties and enqueuing: top-level, block-level, and elements-level. The latter can be used within the top and the block scope.
Style properties at the top level will be enqueued using the body selector and the block style properties will be enqueued per block level, using block selectors. At the moment, core supports only h1 to h6 and link elements

For example, styles in theme.json will be enqueued like this:

"styles": {
    "color": {
        "text": "var(--wp--preset--color--primary)"
    },
    "blocks": {
        "core/paragraph": {
            "color": {
                "text": "var(--wp--preset--color--secondary)"
            },
            elements": {
                "h2": {
                    "typography": {
                        "fontSize": "var(--wp--preset--font-size--big)"
                    }
                }
            }
        }
    },
    "elements": {
        "h1": {
            "typography": {
                "fontSize": "var(--wp--preset--font-size--huge)"
            }
        }
    }
}
body {
    color: var(--wp--preset--color--primary);
}

.wp-block-paragraph {
    color: var(--wp--preset--color--secondary);
}

.wp-block-paragraph  h2 {
    font-size: var(--wp--preset--font-size--big)
}

h1 {
    font-size: var(--wp--preset--font-size--huge)
}

Notice the “heavy” usage of CSS variables, created from preset settings? This is how the theme.json and Global styles will enable fast changing of theme overall “look and feel” with style variations.

In version 5.9, WordPress introduced the “Styles” interface to enable global style variations. More on Global styles and style variations later on.

Styles – color

Color styles are enqueued to top-level scope, added to a body CSS selector properties, and, if used in block level scope, color styles will be added to wp-block-{block name} selector

I already mentioned the “styles” in the “settings” section above, where I changed the site from “dark” to a “light” theme (not as a “WP theme” 😉 ), using settings.color.palette.background and settings.color.palette.foreground presets:

"styles"{
    "color": {
        "background": "var(--wp--preset--color--background)",
        "text": "var(--wp--preset--color--foreground)"
    }
}
body {
    background-color: var(--wp--preset--color--background);
    color: var(--wp--preset--color--foreground);
}

There is (currently) one more option to customize in styles.color section – the “gradient“. If you add a gradient option to styles.color, it will automatically override the “background” option, because it renders differently, and then, the CSS overrides takes place:

body {
  background: var(--wp--preset--gradient--cool-to-warm-spectrum);
  background-color: var(--wp--preset--color--background);
}

Styles – typography

Typography styles apply the same scoping as color styles, for top and block levels.

Similarly to colors, I changed the default font in the styles section, using the settings.typography.fontFamilies and .fontSizes with the CSS variables created in the setting preset:

body {
    font-family: var(--wp--preset--font-family--outfit);
    font-size: var(--wp--preset--font-size--medium);
}

These are all available options to set in the styles.typography:

So, with a couple of changes (the styles.background, and the styles. typography), this is how my “Micemade FSE theme” front page looks like (before/after):

Of course, this is only to show the changes to styles described above. I will remove the background “gradient” option. Although it looks interesting, it is not what I intend to have. But, I’ll keep the default font.

Styles – border, spacing, blocks

Same as color and typography, the border and spacing are enqueuing top level and block level styles. Adding border and spacing to the top level may look like this.

"styles": {
    "border": {
        "color":"red",
        "width":"20px",
        "style":"solid"
    },
    "spacing":{
        "blockGap":"200px",
        "margin":{
            "top":"100px",
            "right":"100px",
            "bottom":"100px",
            "left":"100px"
        }
    }
}
body {
  border-color: red;
  border-width: 20px;
  border-style: solid;
  margin-top: 100px;
  margin-right: 100px;
  margin-bottom: 100px;
  margin-left: 100px;
  --wp--style--block-gap: 200px;
}

The “blockGap” is the only property not applying to body elements. It actually is used for all container blocks, as in the example above. The –wp–style–block-gap CSS variable will be used for .wp-contaniner-{number} gap.

( I don’t recommend using the style configuration from above, it will make your theme look really weird and unusable 🙂. I used too big values, just for testing purposes. )

Following is an example of configuring the styles, the border, and spacing, in block level scope. The resulting enqueued CSS is as shown below. Blocks can include any top-level style property.

"styles": {
    "blocks": {
        "core/cover": {
           "border": {
                "color":"green",
                "width":"20px",
                "style":"solid"
            },
            "spacing":{
                "padding":{
                    "top":"100px",
                    "right":"100px",
                    "bottom":"100px",
                    "left":"100px"
                }
            }
        }
    }
}
.wp-block-cover {
    border-color: green;
    border-width: 20px;
    border-style: solid;
    padding-top: 100px;
    padding-right: 100px;
    padding-bottom: 100px;
    padding-left: 100px;
}

This is the example of using border and spacing properties in block-level scope, only for the Cover block.

Styles used on a block level will be overridden with the block settings in the editor – if you haven’t disabled padding for the block editor, these ridiculous padding and border values are easily replaced in the block settings.

Styles – elements

Elements” property in styles section are top-level scoped to define styles for HTML elements. At the moment of writing this, the core supports h1-h6, link, button, and caption and the style properties which can be configured are border, color, filter, spacing, and typography.

“Elements” property can also be defined in the block level scope. In the following example, I tested adding elements.h2.color and blocks.core/column.elements.color. H2 element globally was set to have red text color on yellow background, but when inside the Column block, the global style is overridden with fuchsia-colored text on a white background.

"styles": {
    "elements": {
        "h2":{
            "color": {
                "background": "yellow",
                "text": "red"
            }
        }
    },
    "blocks": {
        "core/column": {
            "elements": {
                "h2": {
                    "color": {
                        "text": "fuchsia",
                        "background": "#fff"
                    }
                }
            }
        }
    }
}
h2 {
    background-color: yellow;
    color: red;
}

.wp-block-column h2 {
    background-color: #fff;
    color: fuchsia;
}

Theme.json – other sections

I already wrote about the templates, template parts, and patterns in a previous article (Block themes for classic theme developers – part one 🔗), and about the $schema ⬆️ (a very useful configuration option). The “version”: “2” is added to use the current theme.json format, and this is also used with $schema. More about versioning here 🔗

Global styles and variations

“Global styles” is a feature that enables users to customize options set by your Block theme even further – a user can change typography, color, and layout options, based on what’s set in the theme.json file.

The first image shows a snapshot of how the theme is configured in theme.json, styled with the theme palette, and the typography. Below the snapshot there are options available for user customization, starting with settings and styles from theme.json (see images 2 and 3).

The last image is particularly interesting as it shows the theme style variations. You can think of variations as “theme skins”. Variations can be accessed by clicking on “Browse styles” under the current global style snapshot. (image 1 above).

Users can change those options and save customization to theme style variation using the “Save” button on the top.

Variations are JSON files, variants of theme.json, placed in the theme’s “styles” directory. In {style variation name}.json file(s) you can change only configuration options which will fit the skin. For example, I have created two variations called Atlantic.json and Pinkpurple.json (image 4) and customized only settings.palette and styles.typography options. I have kept the same palette organization for consistency.

The Color Hunt 🔗 is a great resource for color palettes – there you will surely find color combinations for your theme style variations.

Atlantic.json style variation

{
    "version": 2,
    "settings": {
        "color": {
            "palette": [
                {
                    "slug": "background",
                    "color": "#F9F5EB",
                    "name": "Background"
                },
                ... other palette colors
            ]
        }
    },
    "styles": {
        "typography": {
            "fontFamily": "var(--wp--preset--font-family--lora)"
        }
    }
}

Pinkpurple.json style variation

{
    "version": 2,
    "settings": {
        "color": {
            "palette": [ 
                {
                    "slug": "background",
                    "color": "#7A4495",
                    "name": "Background"
                },
                ... other palette colors
            ]
        }
    },
    "styles": {
        "typography": {
            "fontFamily": "var(--wp--preset--font-family--poppins)"
        }
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: