theme.json & Child Themes
By the end of this lesson you will write a theme.json file that sets your site's colours, fonts and layout widths, and you will see the CSS that WordPress creates from it. You will also build a child theme, so you can customise someone else's theme without losing your work when it updates.
This is the first developer lesson. You will need a code editor (such as VS Code) and a local site. Knowing basic CSS and JSON helps.
What is theme.json?
theme.json is a settings file in the theme's main folder. It tells WordPress two things:
- settings: which options the editor offers (the colour palette, font sizes, spacing sizes, layout widths)
- styles: the default look of the site and of each block
Think of it as the house rules of a hostel, pinned on one notice board. Instead of telling every student separately, the warden writes the rules once and everyone follows them. Without theme.json, those rules would be scattered across many CSS files.
The current format is version 3, used since WordPress 6.6.
A first theme.json, step by step
Step 1: In your own block theme folder (you will build one fully in the next lesson), create a file called theme.json.
Step 2: Add the version, a colour palette and font sizes:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"palette": [
{ "slug": "primary", "name": "Primary", "color": "#21759b" },
{ "slug": "accent", "name": "Accent", "color": "#f28c28" },
{ "slug": "base", "name": "Base", "color": "#ffffff" },
{ "slug": "contrast", "name": "Contrast", "color": "#1d2327" }
]
},
"typography": {
"fontSizes": [
{ "slug": "small", "name": "Small", "size": "0.875rem" },
{ "slug": "medium", "name": "Medium", "size": "1.125rem" },
{ "slug": "large", "name": "Large", "size": "1.75rem" }
]
},
"layout": {
"contentSize": "720px",
"wideSize": "1200px"
}
}
}Step 3: Save the file and open any page in the editor. Select a paragraph and open its colour settings. You should see exactly your four colours, with the names you gave them.
The $schema line is optional. It lets editors such as VS Code suggest keys and warn about typing mistakes.
contentSize is the normal width of text, and wideSize is the width used when a block is set to Wide width. A 720px reading width keeps lines comfortable, like the column width of a newspaper.
The CSS WordPress generates
From your palette, WordPress creates CSS custom properties (CSS variables) and helper classes. View the page source and you will find something like this:
:root {
--wp--preset--color--primary: #21759b;
--wp--preset--color--accent: #f28c28;
--wp--preset--font-size--large: 1.75rem;
}
.has-primary-color { color: var(--wp--preset--color--primary) !important; }
.has-accent-background-color { background-color: var(--wp--preset--color--accent) !important; }The real output is squeezed onto fewer lines and includes more presets (WordPress adds some defaults); these are the ones from your file. The naming pattern is always --wp--preset--{type}--{slug}. You can use these variables in your own CSS, so a colour change in theme.json updates everything.
Adding styles
Now give the site a default look. Add a styles section next to settings:
{
"version": 3,
"styles": {
"color": {
"background": "var(--wp--preset--color--base)",
"text": "var(--wp--preset--color--contrast)"
},
"typography": {
"fontFamily": "system-ui, sans-serif",
"lineHeight": "1.6"
},
"elements": {
"link": {
"color": { "text": "var(--wp--preset--color--primary)" }
},
"button": {
"color": {
"background": "var(--wp--preset--color--accent)",
"text": "var(--wp--preset--color--base)"
}
}
},
"blocks": {
"core/quote": {
"border": { "left": { "color": "var(--wp--preset--color--accent)", "width": "4px", "style": "solid" } }
}
}
}
}(In your real file, settings and styles sit together in one object.) Three levels appear here:
| Level | Affects | Example |
|---|---|---|
| Top-level styles | The whole page | Background, text colour, font |
elements | HTML elements everywhere | All links, all buttons, all headings |
blocks | One block type | Every Quote block |
Anything set here can still be changed by the site owner in the Site Editor's Styles panel. Their choices are saved in the database and win over the theme's file.
Turning options off
You can also limit choices, which is useful for client sites. This stops custom colour pickers so editors can use only your brand palette:
{
"version": 3,
"settings": {
"color": {
"custom": false,
"defaultPalette": false
}
}
}custom: false removes the free colour picker. defaultPalette: false hides WordPress's own default colours. Now nobody can turn the fee table neon green by accident.
Child themes: customise without losing changes
A child theme is a small theme that borrows everything from a parent theme and changes only what you specify. When the parent updates, your changes stay safe in the child.
It is like a photocopied school timetable where you write your own notes in the margin. If the school issues a new timetable, you copy your notes onto it, and you never have to redraw the whole grid.
Build a child of Twenty Twenty-Five
Step 1: Make sure Twenty Twenty-Five is installed (it does not need to be active).
Step 2: Create the folder wp-content/themes/bright-child/.
Step 3: Create style.css with a Template line naming the parent's folder:
/*
Theme Name: Bright Child
Template: twentytwentyfive
Description: Bright Future Coaching customisations for Twenty Twenty-Five.
Version: 1.0.0
Text Domain: bright-child
*/Step 4: Create a theme.json in the child with only the parts you want to change, such as the palette from earlier. WordPress merges it with the parent's file, so everything you leave out still comes from the parent.
Step 5: Go to Appearance > Themes and activate Bright Child.
Step 6: Visit the site. It looks like Twenty Twenty-Five, but with your colours. Open the Site Editor: your palette appears in Styles.
To change a template, copy it from the parent into the child at the same path (for example templates/single.html) and edit the copy. The child's file is used instead of the parent's.
Common mistakes
- Invalid JSON. One missing comma or a trailing comma stops the whole file working. Use an editor with a JSON checker and the
$schemaline. - Wrong
Templatevalue. It must be the parent's folder name (twentytwentyfive), not its display name. - Hard-coding colours in CSS instead of using the
--wp--preset--color--...variables. - Expecting theme.json changes to override Site Editor changes. Saved user styles win; reset them in Styles if you want the file's values.
Key points
theme.json(version 3) holdssettings(what the editor offers) andstyles(the default look).- WordPress turns presets into CSS variables named
--wp--preset--{type}--{slug}. - Styles can target the whole page,
elements(links, buttons) or specificblocks. "custom": falseand"defaultPalette": falselimit colour choices for clients.- A child theme needs
style.csswithTemplate:set to the parent folder; itstheme.jsonmerges with the parent's. - Site Editor changes are saved in the database and override the theme files.
Exercise
Create a child theme of Twenty Twenty-Five called "Kochi Bakes Child". Give it a palette with three colours (a cream base, a chocolate brown contrast and a cherry red accent), set contentSize to 680px, and make all buttons cherry red with cream text. Turn off the custom colour picker.
Show solution
style.css needs Theme Name: Kochi Bakes Child and Template: twentytwentyfive. The child's theme.json:
{
"version": 3,
"settings": {
"color": {
"custom": false,
"palette": [
{ "slug": "base", "name": "Cream", "color": "#fff8ec" },
{ "slug": "contrast", "name": "Chocolate", "color": "#4a2c2a" },
{ "slug": "accent", "name": "Cherry", "color": "#c2185b" }
]
},
"layout": { "contentSize": "680px" }
},
"styles": {
"elements": {
"button": {
"color": {
"background": "var(--wp--preset--color--accent)",
"text": "var(--wp--preset--color--base)"
}
}
}
}
}Activate the child theme and check a Buttons block: it should be cherry red with cream text.
Quick Quiz
1. Which theme.json section controls which options the editor offers?
settings defines palettes, font sizes and layout options; styles sets the default look.
2. What CSS variable does WordPress create for a palette colour with slug accent?
Presets follow the pattern --wp--preset--{type}--{slug}.
3. In a child theme's style.css, what must Template: contain?
WordPress finds the parent by its folder, such as twentytwentyfive.
4. A colour set by the owner in Styles differs from theme.json. Which wins?
User customisations saved in the database override the theme's defaults.
Learn WordPress with a mentor
Free lessons get you started. Our WordPress & Website Building training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
