How to Configure Your Site with Theme.json


In this presentation, we will talk about theme.json, what it is and how to use it to configure your block-based theme.

Objectives

After completing this lesson, participants will be able to:

  • Explain the purpose of theme.json (what the file does)
  • Distinguish between the different parts of theme.json and explain their purpose (what each part does)
  • Build a basic theme.json file for a block theme

Prerequisite Skills

Participants will get the most from this lesson if they have familiarity with:

  • Completed the Basic WordPress Concepts lesson
  • Basic experience with a text-editor
  • Basic experience with JSON or other programming languages

Readiness Questions

  • Have you built a theme?
  • Do you want to configure a theme?
  • Have you worked in a text editor?
  • Have you created JSON files?
  • Can you locate WordPress files?
  • Do you know how to navigate WordPress source files using a text editor?

Slides

Materials Needed

Notes for the Presenter/Reviewers

  • theme.json will still work if Gutenberg is not installed but some features may not work with just WordPress core
  • If there are participants who don’t have the necessary setup then, downloading Local by Flywheel should help; it comes with WordPress ready to go and WordPress comes bundled with the Twenty TwentyTwo theme so they can follow up
  • theme.json is large enough that it may need more than one session to cover its entirety
  • This lesson plan deals only with theme.json as it works with block themes

Lesson Outline

  • Participant Introductions
    • Where are you in your Gutenberg Journey?
    • Have you built or configured a theme?
  • Introduction (discussion)
    • What is theme.json?
    • What does it do?
    • Why is it important?
  • theme.json structure (combination of show and tell and discussion)
    • The 10000 feet overview
      • Demo
    • More detailed overview
      • $schema
      • Version
      • Settings
      • Presets
      • Custom
      • Styles
        • Top-level
        • Block-level
        • Elements
      • Typography
        • fontFace
      • Custom Templates
      • Template Parts
      • Patterns
      • Putting it all together
        • Building a basic theme.json configuration file

Exercises

Building the skeleton

Before we start, we’ll build the skeleton where we will insert content as we complete the exercises.

Copy and paste the following JSON code into a file and save the file as theme.json

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

Exercise 1: Basic settings

The first portion we’ll work with is border settings. These settings will display border settings for all the block types that support borders (not all blocks support all properties that we’ll cover in these exercises).

Boolean values represent items that will be enabled or disabled in the post or full site editor.

  "border": {
    "color": true,
    "radius": true,
    "style": true,
    "width": true
  },

Exercise 2: Color and duotone settings

The next section deals with colors in the theme. The first set of attributes will enable or disable color settings for the theme.

The main items to highlight are duotone and gradient settings. There are two sets of attributes that can be enabled independently from each other: custom and default duotones and gradients.

Copy the following code after the code from the previous exercise:

    ,
    "color": {
      "background": true,
      "custom": true,
      "customDuotone": true,
      "customGradient": true,
      "defaultGradients": true,
      "defaultPalette": true,
      "link": false,
      "text": true,
      "duotone": [
        {
          "colors": [
            "#000",
            "#FFF"
          ],
          "slug": "black-and-white",
          "name": "Black and White"
        }
      ]
    },
    "gradients": [
      {
        "name": "Red to blue",
        "slug": "red-blue",
        "gradient": "linear-gradient(90deg, rgb(255,0,0) 0%, rgb(0,0,255) 50%"
      }
    ]

For additional exercises, add a new duotone and a new gradient.

Experiment with the values of the linear gradient attribute following the instructions from the linear-gradient() article at MDN.

Exercise 3: Custom palette colors

Adding colors comes next. The palette is an array of colors that will be used in the theme in addition to the default colors WordPress maes available.

Copy the code below after the gradient code from the last exercise:

      ,
      "palette": [
        {
          "slug": "link-red",
          "color": "#cd2653",
          "name": "Link red"
        }
      ],

For this exercise we’ll add a new color with the following information

  • Name: Rebecca Purple
  • Slug: rebeccapurple
  • Color: #663399

Once you’ve added the color, look at the Color settings under the paragraph block. You should see the new color appear in the theme palette.

Exercise 4: Layout width

When setting the layout dimensions you specify the width of the content area and the dimensions of the wide layout. Copy the following below the previous example:

    ,
    "layout": {
      "contentSize": "800px",
      "wideSize": "1000px"
    },

Exercise 5: Spacing

The spacing section has two areas, defining what elements are allowed in the theme and then defining what units of measurements we want to use.

These are all valid CSS measurement units. If you’ve done work with CSS in the past, these should look familiar to you.

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

Exercise 6: Global Styles

Insert the following block in your theme.json file under the styles element. Create it if it doesn’t exist.

    "button": {
      "color": {
        "background": "#fff",
        "text": "#000"
      }
    },
    "caption": {
      "color": {
        "background": "rebeccapurple",
        "text": "white"
      }
    },
    "cite": {
      "color": {
        "background": "#00f",
        "text": "#fff"
      }
    },
    "heading": {
      "color": {
        "background": "#000",
        "text": "#fff"
      }
    }

Test your theme with these new settings and modify them to the following:

  • button element
    • Change the background color to rgb(34,139,34)
    • Change the text color to #fff
  • cite
    • Change the background color to navy

Exercise 7: Interactive States

Add the following content to your theme.json file under the elements block. Add the elements block if it doesn’t exist in your theme.

"link": {
  ":hover": {
    "color": {
      "text": "#000"
    }
  },
  ":active": {
    "color": {
      "text": "#f00"
    }
  },
  ":focus": {
    "color": {
      "text": "#00f"
    }
  }
}

Once you’ve added the settings block, make the following changes.

  1. Change the color of the :hover link to rebeccapurple
  2. Change the color of the :focus element to rebeccapurple

Exercise 8: Outlines

Copy the following block into your theme’s `theme.json` under the elements section. Create this section if it doesn’t exist on your `theme.json` file

Add the following block inside `core/paragraph` block. Add the `core/paragraph` block if it’s not in your `theme.json` file.

"button": {
  "outline": {
    "offset": "3px",
    "width": "3px",
    "style": "dashed",
    "color": "red"
  },
  ":hover": {
    "outline": {
      "offset": "3px",
      "width": "3px",
      "style": "solid",
      "color": "blue"
    }
  }
}

Once you add the block, make the following changes.

Change the value of the text color from blue to red.

Test different hover colors to see what matches your theme the best.

Exercise 9: Fluid typography

Add the following block under `typography`.

If there is no fontSizes block, this will create it. If there is already a fontSizes block configured you will have to replace it with the fluid sizes block we’re configuring now.

"typography": {
  "fluid": true,
  "fontSizes": [
    {
      "size": "2rem",
      "fluid": {
        "min": "2rem",
        "max": "2.5rem"
      },
      "slug": "medium",
      "name": "Medium"
    }
  ]
}

Once you’ve added the fontSizes section, make the following changes.

  1. Add a new font size with the following characteristics
    • size: 1em
    • fluid:
      • min: 1rem
      • max: 2rem
      • slug: small
      • name: Small
  2. Add a new font size with the following characteristics
    • size: 3rem
    • fluid:
      • min: 3rem
      • max: 4rem
      • slug: large
      • name: Large

Exercise 10: Assigning a minimal font size for fluid typography

This exercise builds on the result

Add the following entry to your fluid typography

"minFontSize": "16px"

So now the flued section of the typography block looks like including the sizes that you added in exercise 9:

Copy the following block into your spacing settings. If the spacing block doesn’t exist create it under settings.

"typography": {
  "fluid": true,"minFontSize": "16px",
  "fontSizes": [
    {
      "size": "2rem",
      "fluid": {
        "min": "2rem",
        "max": "2.5rem"
      },
      "slug": "medium",
      "name": "Medium"
    },
    {
      "size": "1rem",
      "fluid": {
        "min": "1rem",
        "max": "2rem"
      },
      "slug": "small",
      "name": "Small"
    },
    {
      "size": "3rem",
      "fluid": {
        "min": "3rem",
        "max": "4rem"
      },
      "slug": "large",
      "name": "Large"
    }
  ]
}

Once you’ve added the block, complete the following tasks:

Add the following elements to the spacingSizes object:

{
  "spacingSizes": [
    {
      "size": "min(16px, 2vw)",
      "slug": "20",
      "name": "2"
    },
    {
      "size": "min(40px, 4vw)",
      "slug": "30",
      "name": "3"
    }
  ]
}

Exercise 11: Shadows

Add the following block inside the settings section of your theme.json file. If the block doesn’t exist, then this will create it.

These definitions add three size shadow presets and three variations on the large preset.

"shadow": {
  "presets": [
    {
      "name": "Small",
      "slug": "sm",
      "shadow": "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)"
    },
    {
      "name": "Medium",
      "slug": "md",
      "shadow": "0 4px  10px 0 rgba( 0, 0, 0, 0.3 )"
    },
    {
      "name": "Large",
      "slug": "lg",
      "shadow": "0 8px  15px 0 rgba( 0, 0, 0, 0.3 )"
    },
    {
      "name": "Large Navy",
      "slug": "lg-navy",
      "shadow": "0 8px  15px 0 rgba( 0, 0, 128, 0.6 )"
    },
    {
      "name": "Large Magenta",
      "slug": "lg-magenta",
      "shadow": "0 8px  15px 0 rgba( 255, 0, 255, 0.5 )"
    },
    {
      "name": "Large Lime Green",
      "slug": "lg-lime-green",
      "shadow": "0 8px  15px 0 rgba( 50, 205, 50, 0.5 )"
    }
  ]
}

Once you have added the presets, add the following block/image customization code to your block styles settings.

"styles": {
  "blocks": {
    "core/image": {
      "shadow": "var( --wp--preset--shadow--lg )"
    }
  }
}

Insert an image block and look at the result. Is the shadow visible?

Change the value of the shadow attribute to --var--wp--shadow--lg-navy

Assessment

theme.json act as a theme’s configuration file

  1. Yes
  2. Yes, but only for block themes
  3. No

Answer: 1. Correct answer

Settings in theme.json control what content creators see in the UI

  1. No
  2. Yes

Answer: 2. Correct answer

When adding colors to theme.json you can add colors in the following formats

  1. RGB
  2. Named Colors
  3. RGB, Named Colors, Hexadecimal
  4. Hexadecimal

Answer: 3. Correct Answer

the $schema element allows supporting editors to validate your work

  1. True
  2. False

Answer: 1. Correct answer

Why is the version attribute required in theme.json?

  • WordPress wants to know what version you’re using in case something breaks
  • There are multiple versions of theme.json that support different features
  • It’s a good habit to get into
  • Your theme will not work if theme.json doesn’t have a version

Answer: 2. Correct Answer

All blocks support the same features.

  • True
  • False

Answer: 2. Correct Answer.

If an item in settings is set to false:

  • The item will work just fine
  • WordPress will restrict what you can do with the item
  • The item will not be available to people working with your theme
  • WordPress will issue warnings every time you try to use the item

Answer: 3. Correct Answer

You can disable cusomGradients and default Gradients in your theme by setting them to false.

  • False
  • True

Answer: 2. Correct Answer

You can have custom and default gradients in your theme at the same time.

  • True. You can have both if you set them to true
  • False

Answer: 1. Correct Answer

What does blockGap control?

  • Spacing between blocks based in their direction
  • Vertical space between blocks
  • Horizontal space between blocks
  • Both horizontal and vertical space between blocks

Answer: 1. Correct Answer

How do you disable the spacingScale in your theme.json?

  • set the steps value to 0
  • remove the spacingScale declaration from theme.json
  • Change the mediumStep to 0

Answer: 1. Correct answer

When defining a font stack with fontFamily, where is the font loaded from?

  • A local server
  • An external server
  • The user’s computer
  • A server you specify

Answer: 3. Correct Answer

When defining a font using fontFace what does the fontWeight attribute control?

  • The italic value for the font
  • The stretch value for the font
  • The height value for the font
  • The bold value for the font

Answer: 4. Correct Answer

When would you add two values to the font-weight attribute?

  • When working with variable fonts to indicate upper and lower boundaries for weight
  • Whenever you’re using the fontFace font declaration
  • Whenever you’re loading the font from Google fonts
  • All the time

What happens when two entries in fontSizes have the same size value

  • WordPress will show an error
  • Only the first entry will work
  • Only the last entry will work
  • Both entries will work

Answer: 4. Correct Answer

What do you use the custom section of theme.json for?

  • Whatever you want
  • Store data to generate custom properties that you can use elsewhere
  • Create a new custom theme.json file
  • The feature is reserved for future use

Answer: 2. Correct answer

What are the levels of configurable styles provided in theme.json

  • 1
  • 5
  • 3
  • 7

Answer: 3. Correct Answer

What elements can you style as a group in WordPress 6.2?

  • Links
  • h1 through h6 elements individually
  • buttons
  • captions
  • cite elements
  • headings as a group
  • All of the above

Answer: 7. Correct Answer

:active, :focus and :hover control interactive behaviors of html elements

  • True
  • False

Answer: 1. Correct Answer

The outline attributes (offset, width, style, and color) can be used for the different interactive states (:hover, :active, :focus)

  • False
  • True

Answer: 2. Correct answer

What is the purpose of the shadow presets available in WordPress?

  • Make sure that shadows work as designed
  • Just to have them available
  • To enforce consistency
  • Provide shadows without authors having to create their own

Answer: 4. Correct answer

Can you have custom shadow definitions in addition to the default ones?

  • Yes
  • No

Answer: 1. Correct Answer

Why would set a minimum font size using fluid typography in your site?

  • To comply with the site design specification
  • To make sure the site remains readable in smaller form factors
  • Because it looks good
  • Because you have to

Answer: 2. Correct Answer

Additional Resources

Full Sample Lesson Plan

Introduction

Theme.json allows you to customize your WordPress theme from one central location.

It takes over functions.php for most of a theme’s configuration settings and provides new functionality that may not have a direct equivalent in functions.php.

While theme.json will work on any WordPress theme, for these lessons we’ll concentrate on block-based themes.

See Using theme.json in a classic theme for more information about theme.json as applied to classic themes.

About JSON and JSON requirements

theme.json is a JSON file and, as such, presents some things that may not be familiar if you haven’t worked with the format before.

All strings in JSON must be enclosed in double quotation marks (). anything else will be flagged as an error.

The first example below will work as intended:

The second example is a valid part of the theme.json schema but will fail validation because of the single quotes around the version string.

ValidInvalid
“version”: 2‘version’: 2

Boolean values (true or false) and numbers must not be quoted.

Even a single misplaced comma or colon can cause a JSON file to not work. You should be careful to validate the file you’re creating with tools like JSONLint or CodeBeautify’s JSON Validator.

For a good introduction to working with JSON, see Working with JSON at MDN.

Necessary tools

In order to use theme.json, you will need the latest version of WordPress.

theme.json structure: The 10000 feet view

The structure of the theme.json file follows the JSON conventions as defined in ECMA-404.

An empty theme.json file looks like this:

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

The settings section provides a way to enable or disable features in the post editor and establish theme settings like colors, duotones, and gradients.

Styles allow theme developers to customize styles for the theme.

customTemplates and customParts provide places where to add metadata about templates and template parts

Finally, patterns enable you to associate patterns available in the WordPress Pattern directory

We will look at each major section of the theme.json file in turn.

$schema

The location of the JSON schema for the theme.json file.

Adding this string will allow you to validate the theme.json against the schema in your text editor.

The only valid value is the string: https://schemas.wp.org/trunk/theme.json that points to the latest revision of the schema.

"$schema": "https://schemas.wp.org/trunk/theme.json",

version

The version attribute represents the version of the schema the theme.json conforms to.

Possible values are 1 and 2. The default value is 2 and indicates the latest version of the schema:

"version": 2

It is expected that the number will increase as new features are introduced or features from the Gutenberg plugin are integrated to WordPress core.

If the default version value changes, your theme won’t break, the items in the new version will not be available and you will need to update the theme.json file and figure out if any existing part of the file needs to be changed.

presets / settings

The theme.json file provides a canonical way to define the settings of the block editor. These settings include things like:

  • The customization options that will be available or hidden from the user
  • The default colors and font sizes available to the user customizing the theme
  • The default layout of the editor and layout-related items for the given theme

Note:

Not all blocks support all the features we’ll configure in theme.json. This is intentional and may change over time.


The first setting, appearanceTools is special. Rather than specify a single setting, it enables a group of settings:

  • border: color, radius, style, width
  • color: link
  • spacing: blockGap, margin, padding
  • typography: lineHeight

The first example shows the border family of attributes. The code below indicates that all the border attributes are enabled.

If you set any value to false, the attribute will not be shown to the user working with your theme.

"settings": {
  "appearanceTools": true,
  "border": {
    "color": true,
    "radius": true,
    "style": true,
    "width": true
  }
}
Border radius settings. The value you enter in the box to the left will be used in all 4
Border radius settings. This will use the same value for all borders (top, left, bottom, right).
Border radius settings. You can enter individual values for the 4 borders (top, left, bottom, right)
Border radius settings. You can enter individual values for the 4 borders (top, left, bottom, right)

The color block combines both controls for what UI attributes will appear in the editor and what values will be available to the user.

duotones, gradients and colors provide additional values for the Gutenberg editor to use in addition to any available defaults.

You can disable either the custom values (customGradient and customDuotone) or the default values (defaultDuotone, defaultGradient or defaultPalette) by setting them to false in the configuration.

You can remove the defaulPalette and only leave a set of custom colors in place if you have branding requirements or a preferred color scheme for your site.

Paragraph block showing default and theme palettes for the user to select
Paragraph block color picker showing both a custom theme and the default palettes. The same colors will be available for text, background, and link colors.

When choosing colors you should always consider accessibility, particularly color blindness and contrast between your text and background colors.

"settings": {
  "color": {
    "background": true,
    "custom": true,
    "customDuotone": true,
    "customGradient": true,
    "defaultDuotone": true,
    "defaultGradients": true,
    "defaultPalette": true,
    "link": true,
    "text": true
  }
}

The duotone array uses a common pattern that we’ll see moving forward:

  • The slug, the machine-readable name of the item
    • Usually lowercase, camel-cased if it has more than one word, the second and subsequent words are capitalized
  • The (human-readable) label
    • Can be capitalized as needed
    • Can have spaces in it
    • This will be shown to the user in the UI
    • Translators will take this value when translating the content of your theme
  • One or more values for the item
    • In the case of duotone it is an array of two colors, representing the color for highlights and shadows
Image block showing a duotone fileter applied and the duotone selection window over it
Image block showing a duotone fileter applied and the duotone selection window over it

Because duotone filters are highly specialized, It is strongly recommended that you test the duotone colors that you add to your theme since the use of colors may have accessibility implications.

gradients use a similar schema:

  • The slug, the machine-readable name of the item
  • The (human-readable) name
  • The gradient, indicates the CSS gradient that you want to use.
  • Most of the time you will use linear gradients with default settings that you can modify in the editor’s UI when you enable gradients for the theme.

If you don’t plan to add duotone pairs and gradients, you can remove your theme settings for them.

"settings": {
  "colors": {
    "duotone": [{
      "slug": "black-and-white",
      "name": "Black and White",
      "colors": [
        "#000",
        "#FFF"
      ]
    }],
    "gradients": [
      {
        "slug": "blush-bordeaux",
        "gradient": "linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%)",
        "name": "Blush bordeaux"
      }
    ]
  }
}

The palette object presents a list of colors that you want to be available in the editor’s UI.

You can present the colors in any color format supported by CSS. The example block presents three of the most common formats:

  • Strong Magenta is written using a six-digit hexadecimal number format where every pair of digits represents a color channel (red, green, blue) using hexadecimal notation
  • Rebecca Purple is written as a CSS named color
  • Very Dark Grey is written using an RGB function that indicates the percentage of red, green, and blue in the color
"settings": {
  "colors": {
    "palette": [
      {
        "slug": "strong-magenta",
        "color": "#a156b4",
        "name": "Strong magenta"
      },
      {
        "slug": "rebeccapurple",
        "color": "rebeccapurple",
        "name": "Rebecca Purple"
      },
      {
        "slug": "very-dark-grey",
        "color": "rgb(131, 12, 8)",
        "name": "Very dark grey"
      }
    ]
  }
}

IMPORTANT:

  • Do not use color as the only way to convey information. For example: If you want to convey success, use text or another way to indicate success in addition to the color you choose.
  • This is also important from a cultural sensitivity standpoint. Not everyone using your site may understand your use of color.

For more information see: Creating Culturally Customized Content for Website Translation.


The Layout object defines the width of the default and wide layouts in the editor.

The wideSize attribute defines the value of the wide layout that you can configure in the editor’s UI

  "layout": {
    "contentSize": "800px",
    "wideSize": "1000px"
  },

In the spacing block, we do two things:

We define the aspect-related spacing items that will be available to the author in the editor.

blockGap controls the space between blocks. The direction for the spacing depends on what block and alignment you’re using.

We also define what units the spacing items will be expressed in. If you don’t specify a unit then the unit will not be available to use.

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

The typography block defines items related to your site’s typography.

The block editor gives you a lot of control over the typographical elements that you can make available on your theme.

You control the display for the following typographical items in blocks that use them.

  "typography": {
    "customFontSize": true,
    "dropCap": true,
    "fontStyle": true,
    "fontWeight": true,
    "letterSpacing": true,
    "lineHeight": true,
    "textDecoration": true,
    "textTransform": true,

Using the paragraph block as an example, we can see that the settings in theme.json don’t exactly match what we see in the editor UI.

  • To use custom settings you need to click on the settings icon to the right of the Size heading.
    • Otherwise, you will work with predefined font sizes
  • fontStyle and fontWeight combinations are listed under Appearance
  • Drop Cap is a toggle button
    • You can only control whether the paragraph has a drop cap not the drop cap style
Typography settings for the paragraph block
Typography settings for the paragraph block.

Padding, margin and block gap presets

WordPress 6.1 introduces `spacingScale`, used to generate an array of spacing preset sizes for use with padding, margin, and gap settings.

The spacingScale section takes the following attributes:

  • operator: specifies how to calculate the steps with either * for multiplier, or + for sum
  • increment: the amount to increment each step by. Core by default uses a perfect 5th scale multiplier of 1.5
  • steps: the number of steps to generate in the spacing scale. The default is 7
    • To prevent the generation of the spacing presets, and to disable the related UI, this can be set to 0.
  • mediumStep: the steps in the scale are generated descending and ascending from a medium step, so this is the size value of the medium space, without the unit
  • The default medium step is 1.5rem so the mediumStep value is 1.5
  • unit: the unit the scale uses, eg. px, rem, em, %. The default is rem
"spacing": {    
  "spacingScale": {
    "operator": "*",
    "increment": 1.5,
    "steps": 7,
    "mediumStep": 1.5,
    "unit": "rem"
  }
}

Once you’ve added the settings to your theme’s `theme.json` file the spacing settings like margins and paddings will look slightly different.

As the animation below shows, the editor will provide the slider by default and will configure it with the parameters you set.

The biggest difference is that it won’t tell you what the value of each step is, it will just show the number of the step, from 0 to 6.

The default setting for spacing scales will change all values (top, right, bottom left) simultaneously. They will all be set to the same value.

Dimensions step selector with the link to individual selectors highlihted in blue

You can set individual values for each padding or margin property. If you click in the link icon to the right of the name of the dimension you want to modify you will get four separate items you can select from.

The four items: top, right, bottom, and left present the selector scale and a link to enter a custom value.

Padding setting showing four separate selectors for the for individual padding properties

You can still get something similar to the behavior by clicking the set custom size button located to the right of the dimension you’re modifying.

Location of the Set custom size button to the right of the name of the setting you want to change

You can type in a value and indicate what unit you want to use. The new item is the slider to the right, it will allow you to increase or decrease the value for the dimension, all four values will be set together, you can’t indicate separate values.

Custom dimension settings. Slider on the right gives more flexibility in setting the value

You can also click both the `set custom size` and `unlink sides` buttons to create a fully customizable sizing experience. You now can customize each value separately.

Individual settings for dimensions. The default shows the scale selector

You can also set custom sizes for each either as a custom value or using the presets.

Custom sizes for individual settings

Custom spacing sizes

In addition to creating fluid typography settings, we can create fluid spacing presets starting with WordPress 6.1.

The idea is that you can create your own spacing presets with both static or fluid sizes.

We can use these spacing sizes to override WordPress built-in presents.

This example creates custom presents matching the names of those in core but using custom values that better match your theme.

You can also change the default spacing values in your theme.

The example below shows how to create custom spacing size blocks to use accros your theme.

Each entry has three elements:

  • size: a string with the value for this preset
  • slug: the machine-readable name for the preset
  • name: the human-readable name for the preset
{
"settings": {
     "spacing": {
      "spacingSizes": [
        {
          "size": "min(16px, 2vw)",
          "slug": "20",
          "name": "2"
        },
        {
          "size": "min(40px, 4vw)",
          "slug": "30",
          "name": "3"
        }
       ]
    }
  }
}

The `typography` block defines items related to your site’s typography.

The block editor gives you a lot of control as to the typographical elements that you can make available on your theme.

You control the display for the following typographical items

  "typography": {
    "customFontSize": true,
    "dropCap": true,
    "fontStyle": true,
    "fontWeight": true,
    "letterSpacing": true,
    "lineHeight": true,
    "textDecoration": true,
    "textTransform": true,

You can define the font stacks (one or more fonts) that you want to use.

There are two ways to define font stacks to use in WordPress. The first one relies on fonts already loaded into the user’s system.

The attribute that defines the font stack is fontFamily. This attribute list the comma-separated list of fonts that you want to use. It assumes that the fonts are available in the user’s system and will not load them if they are not present.

The System Fonts stack uses the default fonts in every major browser and operating system so you will be guaranteed to have at least one of these fonts available.

    "fontFamilies": [
      {
        "fontFamily": "-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif",
        "slug": "system-fonts",
        "name": "System Fonts"
      },
      {
        "fontFamily": "Geneva, Tahoma, Verdana, sans-serif",
        "slug": "geneva-verdana",
        "name": "Geneva, Verdana"
      },
      {
        "fontFamily": "Cambria, Georgia, serif",
        "slug": "cambria-georgia",
        "name": "Cambria, Georgia"
      },

The second way uses the `fontFace` attribute as the root for each font declaration and additional attributes to define the font characteristics and load it from the server running your WordPress instance.

The values for these attributes are equivalent to those you’d use on the CSS @font-face at-rule declaration.

The attributes inside a fontFace object are:

  • fontFamily: The name of the font or the font stack you choose to use
  • fontWeight: one or two numbers that indicate the weight of the font (if one value) and the range of weight values (if you add two values)
    • The higher the number the bolder the font will be
    • The numeric values available depend on the font you use
  • fontStyle: Indicates if the font is italic or not. It takes one of the following values:
    • normal: normal font style
    • italic: italic font style
    • oblique: oblique font style
  • fontStretch: Let you choose if the font is condensed or expanded. It takes a percentage value between 50 and 200% or one of the following keywords:
    • normal: normal font-stretch
    • ultra-condensed: ultra condensed font-stretch
    • extra-condensed: extra condensed font-stretch
    • condensed: condensed font-stretch
    • semi-condensed: semi-condensed font-stretch
    • semi-expanded: semi-expanded font-stretch
    • expanded: expanded font-stretch
    • extra-expanded: extra expanded font-stretch
    • ultra-expanded: ultra-expanded font-stretch
  • src: the path to the font relative to the location of the theme.json file
      {
        "fontFamily": "Recursive, sans-serif",
        "name": "Recursive",
        "slug": "recursive",
        "fontFace": [
          {
            "fontFamily": "Recursive",
            "fontWeight": "300 1000",
            "fontStyle": "normal",
            "fontStretch": "normal",
            "fontDisplay": "swap",
            "src": [
              "file:./assets/fonts/recursive-latin-subset.woff2"
            ]
          }
        ]
      }
    ],

We can define custom font sizes to use in our theme. These are the values that appear as the default. The individual font sizes use the same format that we’ve seen elsewhere:

  • Slug
  • Name
  • Size containing the value (including the unit) as a string (encased in quotation marks)

You can define as many sizes as you want or need and multiple entries can have the same value. In the example below, both normal and medium have the value 20px.

    "fontSizes": [
      {
        "slug": "tiny",
        "size": "16px",
        "name": "Tiny"
      },
      {
        "slug": "small",
        "size": "18px",
        "name": "Small"
      },
      {
        "name": "Medium",
        "slug": "medium",
        "size": "20px"
      },
      {
        "name": "Normal",
        "slug": "normal",
        "size": "20px"
      }
    ]
  },
Block paragraph showing the list of custom font sizes created in theme.json
Block paragraph showing the list of custom font sizes created in theme.json

The custom area of the theme.json file is where you can define your theme’s properties that will become CSS variables that you can use in your content.

The variables created from custom theme.json values follow this naming schema:

--wp--custom--wp--custom--: value
--: value

Using the following custom section of a theme.json file

  "custom": {
    "baseFont": 16,
    "lineHeight": {
      "small": 1.2,
      "medium": 1.4,
      "large": 1.8
    }
  },

will generate the following CSS:

body {
    --wp--custom--base-font: 16;
    --wp--custom--line-height--small: 1.2;
    --wp--custom--line-height--medium: 1.4;
    --wp--custom--line-height--large: 1.8;
}

Note how the camelCased properties were converted into dash-separated properties and that they all use two dashes (–) as separators instead of one.

We will revisit these custom styles when we talk about styles in later sections

A note for people who worked developing and customizing classic themes

If you’ve worked on classic themes, you may remember adding features to functions.php using add_theme_support.

To retain backward compatibility, the block editor retrofits existing add_theme_support declarations in different theme.json settings.

The current set of add_theme_support declarations and the equivalent theme.json settings are:

add_theme_supporttheme.json setting
custom-line-heightSet typography.lineHeight to true
custom-spacingSet spacing.padding to true
custom-unitsProvide the list of units via spacing.units
disable-custom-colorsSet color.custom to false
disable-custom-font-sizesSet typography.customFontSize to false.
disable-custom-gradientsSet color.customGradient to false
editor-color-paletteProvide the list of colors via color.palette
editor-font-sizesProvide the list of font sizes via typography.fontSizes
editor-gradient-presetsProvide the list of gradients via color.gradients

styles

theme.json provides three levels of configurable styles:

  • Top-level styles where you can set site-wide configurations
  • Per block configuration
  • Per element configuration for HTML elements.

These styles generate CSS Custom Properties, also known as CSS variables.

Top-level styles

The top-level styles are applied to the entire site by attaching them to the body CSS element, the top-level element of a page.

The styles are built hierarchically and require a little bit of explanation.

When you see the name of the variable value, it’s made of several parts:

  • --wp as the prefix used to differentiate the WordPress-generated CSS from other CSS you may use on your theme
  • --preset indicates the type of variable that you’re creating
    . in this care preset
  • --color indicates which preset category the variable belongs to
  • --primary shows the name of the item and the subtype of the resource you’re using.

WordPress uses two dashes (--) in the variable names to avoid conflicts with other properties defined in external stylesheets.

"styles": {
  "color": {
    "text": "var(--wp--preset--color--primary)"
  }
}

The JSON above produces the following CSS. Since we have already created the custom properties earlier, we don’t need to worry about them again here.

WordPress will add the top-level style to the body element so the cascade and inheritance of CSS work properly. The idea is that, unless you override them at lower, more specific levels, the top-level styles will be applied throughout the site.

body {
  color: var( --wp--preset--color--primary );
}

For more information about the CSS Cascade and how it works see Cascade and inheritance

Globally styling elements

WordPress 6.1 introduces a system to consistently style elements. For example, many blocks display buttons (Button block, Search block, File block, etc.) but there is no way to style all these buttons at the same time, they must be styled individually which is prone to error and creates unnecessary repetitions.

Developers no longer need to include custom CSS with the theme to ensure all these buttons look the same.

Prior to the 6.1 release, users could style the link and individual heading elements. With 6.1, it is now possible to style the following elements:

  • button
  • caption
  • cite
  • heading

The heading element will work for all headings, h1 to h6 whereas before you had to work with each heading level separately.

Note that the example only covers colors but you can use the same style attributes as you can in blocks, in addition to the new features we’ll cover later in this section.

{
  "styles": {
    "button": {
      "color": {
        "background": "#fff",
        "text": "#000"
      }
    },
    "caption": {
      "color": {
        "background": "rebeccapurple",
        "text": "white"
      }
    },
    "cite": {
      "color": {
        "background": "#0f0",
        "text": "#fff"
      }
    },
    "heading": {
      "color": {
        "background": "#000",
        "text": "#fff"
      }
    }
  }
}

Some of these elements match predefined classes created by WordPress blocks. The specific mappings are:

  • button: maps to the wp-element-button CSS class.
    • Also maps to wp-block-button__link for backwards compatibility
  • caption: maps to the following classes:
    • .wp-element-caption
    • .wp-block-audio figcaption
    • .wp-block-embed figcaption
    • .wp-block-gallery figcaption
    • .wp-block-image figcaption
    • .wp-block-table figcaption
    • .wp-block-video figcaption`
  • heading maps to the h1 to h6 CSS selectors
Interactive states

WordPress 6.1 introduces a solution for another challenge in working with block themes: how to control interactive states like hover or focus` for elements and blocks from `theme.json`.

The solution introduces three new pseudo-classes to Styles:

  • :active
  • :focus
  • :hover

The example changes the color of the links when you hover, click on them, or give them focus. You can use all the attributes and child elements that you can use in a block.

These selectors can be applied in theme.json using the following blocks as an example.

The example uses six-digit hexadecimal values. We can use any color format supported in theme.json.

\{
  "styles": {
    "elements": {
      "link": {
        ":hover": {
          "color": {
            "text": "#000000"
          }
        },
        ":active": {
          "color": {
            "text": "#00ff00"
          }
        },
        ":focus": {
          "color": {
            "text": "#ff0000"
          }
        }
      }
    }
  }
}

Outlines

buttons can now have outlines declared as part of their definition in theme.json.

The following button definition includes outlines for the base element and the hover state

{
  "styles": {
    "elements": {
      "button": {
        "outline": {
          "offset": "3px",
          "width": "3px",
          "style": "dashed",
          "color": "red"
        },
        ":hover": {
          "outline": {
            "offset": "3px",
            "width": "3px",
            "style": "solid",
            "color": "blue"
          }
        }
      }
    }
  }
}

Shadows

WordPress provides four shadow presets:

  • Natural
  • Crisp
  • Sharp
  • Soft

Theme authors can use these presets to style blocks in `theme.json`, even if they have not defined their own.

The following code example shows how to use the sharp shadow preset on `core/image` blocks:

{
  "styles": {
    "elements": {
      "button": {
        "outline": {
          "offset": "3px",
          "width": "3px",
          "style": "dashed",
          "color": "red"
        },
        ":hover": {
          "outline": {
            "offset": "3px",
            "width": "3px",
            "style": "solid",
            "color": "blue"
          }
        }
      }
    }
  }
}

“`

WordPress 6.2 also introduces custom shadow presets to define your own box shadows and then use them in all elements where shadows are supported.

The following JSON block defines three box shadows using the name, slug, value/shadow scheme.

  "version": 2,
  "settings": {
    "shadow": {
      "presets": [
        {
          "name": "Small",
          "slug": "sm",
          "shadow": "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)"
        },
        {
          "name": "Medium",
          "slug": "md",
          "shadow": "0 4px  10px 0 rgba( 0, 0, 0, 0.3 )"
        },
        {
          "name": "Large",
          "slug": "lg",
          "shadow": "0 8px  15px 0 rgba( 0, 0, 0, 0.3 )"
        }
      ]
    }
  }
}

We can then reference the shadows using the CSS variable syntax.

In this example, the core/image block uses the large shadow preset represented by the variable var(--wp--preset--shadow--lg)

{
  "version": 2,
  "styles": {
    "blocks": {
      "core/image": {
        "shadow": "var( --wp--preset--shadow--lg )"
      }
    }
  }
}
Integrating the color palette with shadows

Working with theme.json makes it easier for theme authors to reuse one set of design tools with another.

It often makes sense to use colors defined in the global presets.

We can then use this color as the shadow color. Theme authors can reference these color presets via the --wp--preset--color--{$slug} variable.

We can also use the shadows in blocks using the --wp--preset--shadow--{$slug} variable.

To do this, follow these steps:

  1. Define a color in your color palette
  2. Create a shadow using the color from your palette
  3. Reference the custom shadow in a block

The following code example creates a solid shadow color and adds it to all `core/image` blocks:

{
  "version": 2,
  "settings": {
    "color": {
      "palette": [{
        "name": "Primary",
        "slug": "primary",
        "color": "#0693e3"
      }]
    },
    "shadow": {
      "presets": [{
        "name": "Primary",
        "slug": "primary",
        "shadow": "16px 16px var( --wp--preset--color--primary )"
      }]
    }
  },
  "styles": {
    "blocks": {
      "core/image": {
        "shadow": "var( --wp--preset--shadow--primary )"
      }
    }
  }
  }

Block-level styles

The next set of style rules applies to blocks.

The structure of the block level styles is made of two parts:

  • The full qualified name of the block that the styles apply to
  • A block of one or more styles to apply

When we talk about a fully qualified name, we use the prefix that we defined in the name property of the block and the slug of the block.

For blocks built into WordPress Core, the fully qualified name is core + name, like core/paragraph or core/image as shown in the examples below.

"styles": {
  "blocks": {
    "core/paragraph": {
      "color": {
        "text": "var(--wp--preset--color--secondary)"
      }
    },
    "core/group": {
      "color": {
        "text": "var(--wp--preset--color--tertiary)"
      }
    }
  }
}

The previous JSON will produce the following CSS:

p { 
  color: var( --wp--preset--color--secondary );
}

.wp-block-group {
  color: var( --wp--preset--color--tertiary );
}

The CSS classes WordPress generates for each block are prefixed with wp-block- and the slug of the block.

The core/paragraph block opts out from the default behavior and uses p as a selector.

Element styles

In addition to top-level and block-level styles, there’s a set of preconfigured elements that can be configured for global use. The set of available elements and what HTML elements they map to (you can use these elements when writing custom CSS:

WordPress ElementCSS/HTML Equivalents
linka
h1h1
h2h2
h3h3
h4h4
h5h5
h6h6
buttonbutton
captioncaption
citecite
headingall h1 to h6 heading elements

When a style is defined at the root of the styles element it will be applied to the body element and then propagate down the CSS cascade, unless it’s overridden at a lower level either in styles or by more specific elements.

"styles": {
  "elements": {
    "typography": {
      "fontSize": "var(--wp--preset--font-size--normal)"
    },
    "h1": {
      "typography": {
        "fontSize": "var(--wp--preset--font-size--huge)"
      }
    },
    "h2": {
      "typography": {
        "fontSize": "var(--wp--preset--font-size--big)"
      }
    },
    "h3": {
      "typography": {
        "fontSize": "var(--wp--preset--font-size--medium)"
      }
    },
    "button": {
      "color": {
        "background": "red"
      }
    }
  },
}

The base fontSize attribute from the styles block will be added to the body element and will act as the default for all elements that don’t have a more specific style rule.

the three levels of headings, h1, h2, and h3 will be applied to corresponding CSS selectors.

body {
    font-size: var( --wp--preset--font-size--normal );
}

h1 {
    font-size: var( --wp--preset--font-size--huge );
}
h2 {
    font-size: var( --wp--preset--font-size--big );
}
h3 {
  font-size: var( --wp--preset--font-size--medium );
}

If you use the element’s styles inside a block, the result will be slightly different.

The example we are using defines h2 and h3 styles inside the core/group block.

This means that the values we’re changing will only be available inside the group block and not elsewhere.

"styles":
  "blocks": {
    "core/group": {
      "elements": {
        "h2": {
          "typography": {
            "fontSize": "var(--wp--preset--font-size--small)"
          }
        },
        "h3": {
          "typography": {
            "fontSize": "var(--wp--preset--font-size--smaller)"
          }
        }
    }
  }
}

The resulting CSS will reflect this restriction by attaching the style to h2 elements inside the group block, represented by the .wp-block-group class and not anywhere else.

.wp-block-group h2 {
  font-size: var( --wp--preset--font-size--small );
}
.wp-block-group h3 {
  font-size: var( --wp--preset--font-size--smaller );
}

See Globally styling elements for more information

Learning about CSS is a long topic worth a workshop or two, and it is outside the scope of this presentation. Some good starting points for your CSS journey:

customTemplates

You can provide additional information about templates that are stored in the theme’s template folder.

The information you provide about your templates includes:

  • name: the name of the template. This is required
  • title: the title of the template. This is required
  • postTypes: the types of content that the post applies to. The default is page

In the example, we define a template called my-custom-template and assign it to posts, pages and my-cpt custom post types.

  "customTemplates": [
    {
      "name": "my-custom-template",
      "title": "The template title",
      "postTypes": [
        "page",
        "post",
        "my-cpt"
      ]
    }
  ]

templateParts

In this section, you can add metadata about the template parts stored in your theme’s parts folder.

Behind the scenes, WordPress will use this information to generate template-specific variations of the template parts for each supported area (header and footer).

Each template part declaration has three components:

  • Name the name of the template part. This is required
  • Title the (human-readable) title of the template part. This is required
  • Area the area where the part is used. If not included the template will be set to uncategorized by default and will not trigger any template part variations.
  "templateParts": [
    {
      "name": "comments",
      "title": "Comments"
    },
    {
      "name": "footer",
      "title": "Footer",
      "area": "Footer"
    },
    {
      "name": "header",
      "title": "Header",
      "area": "Header"
    },
    {
      "name": "main",
      "title": "Main"
    },
    {
      "name": "sidebar-left",
      "title": "Sidebar Left"
    },
    {
      "name": "sidebar-right",
      "title": "Sidebar Right"
    }
  ]

patterns

This section will associate names with patterns from the WordPress pattern directory.

The patterns fields in an array of objects matching the slugs of URLs in the pattern directory. It will not work with patterns that are only part of your theme.

To declare we’re using the Text and links with image on the side and do’s and don’ts patterns from the directory, we capture the slugs (the string after the final slash on the URL) and add them to the patterns array.

  "patterns": [
    "text-and-links-with-image-on-the-side",
    "dos-and-donts"
  ]

Lesson Wrap Up

💡 Follow with the Exercises and Assessment outlined above.