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
- A local installation of WordPress
- Local by Flywheel (WordPress specific and ready to run)
- MAMP / WAMP + A WordPress installation
- A block-enabled theme. Some examples:
- Data to populate the theme. Possible alternatives
- Your existing data
- A copy of the WordPress Theme Unit Test data
- A copy of the Gutenberg Theme Data
- * Look at How to add demo content in WordPress for more information on how to add demo content to your WordPress site
- A code editor. Possible options:
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
- The 10000 feet overview
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.
- Change the color of the :hover link to rebeccapurple
- 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.
- Add a new font size with the following characteristics
- size: 1em
- fluid:
- min: 1rem
- max: 2rem
- slug: small
- name: Small
- 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
- Yes
- Yes, but only for block themes
- No
Answer: 1. Correct answer
Settings in theme.json control what content creators see in the UI
- No
- Yes
Answer: 2. Correct answer
When adding colors to theme.json you can add colors in the following formats
- RGB
- Named Colors
- RGB, Named Colors, Hexadecimal
- Hexadecimal
Answer: 3. Correct Answer
the $schema
element allows supporting editors to validate your work
- True
- 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 fromtheme.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
- Theme.json schema by Alex Lende
- theme.json schema (updated when new items are added to the schema)
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.
Valid | Invalid |
---|---|
“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
}
}
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.
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
- In the case of
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 notationRebecca Purple
is written as a CSS named colorVery 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
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 sumincrement
: the amount to increment each step by. Core by default uses aperfect 5th scale
multiplier of 1.5steps
: 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 themediumStep
value is 1.5 unit
: the unit the scale uses, eg. px, rem, em, %. The default isrem
"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.
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.
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.
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.
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.
You can also set custom sizes for each either as a custom value or using the presets.
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 presetslug
: the machine-readable name for the presetname
: 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 styleitalic
: italic font styleoblique
: 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-stretchultra-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 thetheme.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"
}
]
},
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_support | theme.json setting |
---|---|
custom-line-height | Set typography.lineHeight to true |
custom-spacing | Set spacing.padding to true |
custom-units | Provide the list of units via spacing.units |
disable-custom-colors | Set color.custom to false |
disable-custom-font-sizes | Set typography.customFontSize to false. |
disable-custom-gradients | Set color.customGradient to false |
editor-color-palette | Provide the list of colors via color.palette |
editor-font-sizes | Provide the list of font sizes via typography.fontSizes |
editor-gradient-presets | Provide 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 carepreset
--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
- Also maps to
- 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
toh6
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:
- Define a color in your color palette
- Create a shadow using the color from your palette
- 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 Element | CSS/HTML Equivalents |
---|---|
link | a |
h1 | h1 |
h2 | h2 |
h3 | h3 |
h4 | h4 |
h5 | h5 |
h6 | h6 |
button | button |
caption | caption |
cite | cite |
heading | all 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:
- CSS Basics from MDN
- Learn CSS from Web.dev
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 requiredtitle
: the title of the template. This is requiredpostTypes
: the types of content that the post applies to. The default ispage
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"
]