Dannci has been building and selling WordPress themes since 2008. He knows classic theme architecture inside out; PHP templates, the Customizer, widget areas, all of it. This article comes from the middle of his own shift to block themes: the first FSE theme he’s currently developing for Themnific. If the learning curve feels steep, he’s on it right now too.
Why This File Keeps Coming Up
Every full site editing tutorial eventually tells you to open theme.json. Few of them explain what the file actually is before sending you in to edit it. You end up changing values that seem to work, without understanding why, and then something breaks and you have no idea where to look.
This article is not a reference document. It walks through what the file does, how it’s structured, and why it’s designed the way it is. Once you understand the logic behind it, it stops being intimidating. It’s a config file with a very specific job.
What theme.json Actually Is: Three Jobs in One File
Most explanations describe theme.json as “a settings file.” That’s accurate but incomplete. The file does three distinct things, and conflating them is the source of most confusion.
Settings declaration. The file tells the block editor what controls to show you. Whether you see a color picker, font size options, or padding controls in the editor sidebar depends on what the active theme declares in this file. The theme sets the menu; WordPress serves from it.
Design token system. A design token is a named value that represents a design decision. Instead of writing the color #1a1a2e in twelve places, you define it once under a name like primary-dark, and everything that uses that color references the name. theme.json is where those names and values are registered. WordPress then compiles them into CSS custom properties automatically.
Default stylesheet. The file also applies those tokens to the page. You can assign your primary-dark color to body text, heading styles, or button backgrounds directly in theme.json, without writing a separate CSS file. The styles you write there become the rendered output.
The CSS Variables It Generates
This is the part most tutorials skip over, and it’s the part that makes the whole system click.
When WordPress processes theme.json, it converts every preset value into a CSS custom property (also called a CSS variable) that gets added to the page automatically. The naming pattern is fixed: --wp--preset--[category]--[slug]. A color defined with the slug primary-dark becomes --wp--preset--color--primary-dark. A font size with the slug 4xl becomes --wp--preset--font-size--4xl.
You can use these variables in three places: inside the styles section of theme.json itself, in template HTML files, and in your own additional CSS. The syntax differs depending on where you use them. Inside theme.json, you write var(--wp--preset--color--primary-dark). Inside template files, you’ll sometimes see a shorthand pipe syntax instead: var:preset|color|primary-dark. These refer to the same value. The pipe syntax appears in the markup WordPress generates for block attributes; the CSS variable syntax is what you write in JSON. Mixing them up in the wrong context causes styles to silently fail.
The File Structure: What Each Section Does
A theme.json file has several possible top-level sections. Most of them you’ll rarely touch.
$schema— A reference to the JSON schema definition. Enables validation in code editors. Don’t edit this.version— Tells WordPress which version of the theme.json specification the file targets. Don’t change it unless you’re deliberately upgrading.settings— Declares what the editor exposes and defines preset values. This is where most of your work happens.styles— Applies values to the rendered page. The other main area you’ll work in.customTemplates— Registers template files by name. Informational only.templateParts— Registers template parts. Also informational.patterns— Registers block patterns from a remote source.
Day-to-day, settings and styles are the only sections that affect how the site looks and what the editor lets you do. The WordPress developer documentation maintains the official theme.json schema reference if you want the complete specification.
The ‘settings’ Section: What You’re Declaring to WordPress
The settings section has four main areas: color, typography, spacing, and layout. Each one does two jobs simultaneously.
The first job is toggling capabilities on or off. You can disable the color picker entirely, hide the font size control, or remove the padding options from the sidebar. A theme might do this to prevent editors from making changes that break the design.
The second job is defining preset values. You list the colors, font sizes, or spacing steps you want available, and WordPress adds them to the editor as named options.
One pattern that confuses first-time theme.json readers: defaultPalette: false and defaultFontSizes: false. These lines strip out WordPress’s built-in color swatches and type size presets. A theme does this on purpose. WordPress ships with a default color palette that includes colors like “vivid red” and “luminous vivid amber.” If your theme has its own color system, those defaults appear alongside your palette as noise. Disabling them gives you a clean slate. The risk is that if you disable the defaults but don’t define replacements, the user ends up with no color options at all.
The ‘styles’ Section: Applying Tokens to the Page
The styles section has three levels of specificity, and understanding which level to use is the most common point of confusion in block theme customization.
Global styles apply to the <body> element. Text color, background color, and base font settings set here cascade down to everything on the page unless something more specific overrides them.
Elements styles apply to specific HTML elements regardless of which block renders them. If you set a color for h2 under elements, every H2 on the site gets that color, whether it comes from a Heading block, a Query Loop, or a Post Title block. Link styles and button styles live here too.
Blocks styles apply to a specific block type. Setting a background color on the Group block only affects Group blocks, not the page globally.
The cascade runs in one direction: blocks overrides elements, elements overrides global. This mirrors how CSS specificity works, but in JSON form. If your heading color defined at the global level isn’t applying, it’s almost always because a block-level or element-level style is overriding it further down the chain.
The One Rule That Prevents Most Mistakes
Never hardcode a value in styles that you haven’t defined as a token in settings.
Here’s the wrong way:
"styles": {
"color": {
"text": "#1a1a2e"
}
}Here’s the right way:
"settings": {
"color": {
"palette": [
{ "slug": "primary-dark", "color": "#1a1a2e", "name": "Primary Dark" }
]
}
},
"styles": {
"color": {
"text": "var(--wp--preset--color--primary-dark)"
}
}The hardcoded version works visually, but it breaks the token system in three ways. Style variations (covered in the next section) can’t override a hardcoded value. Users can’t change it through the editor. And if you later want to update that color, you have to find every place you wrote the hex code manually. The token approach means you change the value in one place and it propagates everywhere.
Style Variations: How One Click Changes the Whole Site
A style variation is a partial theme.json file stored in the theme’s styles/ folder. It doesn’t replace the base theme.json; WordPress deep-merges it on top at runtime. Any value the variation defines overwrites the matching value in the base file. Anything the variation doesn’t mention stays as-is from the base.
A minimal variation file might only redefine the color palette. The typography, spacing, and layout settings stay exactly as the base file defined them. The user sees a completely different color scheme, and you wrote maybe 20 lines of JSON to create it.
Users access style variations through the editor: Appearance → Editor → Styles → Browse styles. Each variation file you include in the theme appears there as a named option. No PHP. No template edits. A single click applies the variation sitewide.
Because WordPress merges rather than replaces, a poorly written variation can produce unexpected combinations. If your variation redefines heading colors but forgets to account for the link color defined at the elements level, you can end up with clashing styles that aren’t obvious until you test the variation thoroughly.
Per-Block Settings Overrides
The settings.blocks section lets you override settings for a single block type without affecting the rest of the editor.
A practical use: a premium theme might lock down the Navigation block’s layout options. The navigation structure is part of the template design. Allowing site users to change it freely breaks the layout. You can disable layout controls for the Navigation block specifically in settings.blocks while leaving those same controls active everywhere else. The editor can still change menu items and links; they just can’t restructure the navigation container.
This is most useful in themes designed for clients or teams where protecting layout decisions matters, but you don’t want to hard-code the structure in PHP.
What You Should and Shouldn’t Edit Directly
Safe to edit: Color palette values, font size scales, spacing tokens, and global styles for HTML elements. These are what the file is designed for.
Edit carefully: layout.contentSize and layout.wideSize. These values control the maximum width of content and wide-aligned blocks across all templates. Changing them affects every page simultaneously.
Leave alone unless you understand the cascade: settings.blocks overrides and disabling default presets without defining replacements. Both can remove editor options completely if done without replacements in place.
Don’t touch: $schema and version. These aren’t design decisions; they’re compatibility declarations. Changing the version number without understanding what changed between specification versions can silently break behavior.
When theme.json Is Not Enough
theme.json handles global styles and editor configuration. It doesn’t replace everything a stylesheet can do.
Hover states beyond the basic link and button styles still require CSS. Complex animations, pseudo-elements, and media query-specific overrides are outside what the file supports. If you find yourself trying to target :hover on a card or write a ::before rule, open a stylesheet.
Conditional logic also stays in PHP. Showing a different layout to logged-in users, pulling data from a plugin, or making decisions based on post meta are not things theme.json can express.
The accurate way to think about theme.json is this: it replaces the default global styles that used to live in a theme’s stylesheet. Those styles now live in a structured, editor-aware format. Everything else still belongs where it always did.
Where to Go From Here
theme.json has more depth than one article can cover, schema versioning, per-block style overrides, and fluid typography each deserve their own treatment. The links below are the right next steps depending on where you want to go.
If you haven’t set up your block theme yet — the second article in this series, I Just Activated a Block Theme — What Do I Do Now?, covers the practical first steps in the Site Editor: editing your header and footer, setting global styles, and working with templates. The theme.json concepts from this article will make more sense once you’ve seen where the tokens surface in the editor UI.
If FSE is still conceptually unclear — the first article in this series, WordPress Block Themes: What Site Owners Need to Know About FSE, covers the difference between classic and block themes and explains where theme.json fits in the overall architecture. It’s worth reading before going deeper into the file itself.
For the full technical reference — the WordPress Block Editor Handbook covers the complete theme.json schema: every supported property, accepted values, and version differences. It’s dense but authoritative — the right place to go when you need to know exactly what a property accepts.
For Global Styles specifically — the Global Settings and Styles documentation on developer.wordpress.org covers how the styles section works in detail, including element and block-level style application. More approachable than the schema reference and the better starting point if you’re focused on the visual output rather than the full file structure.
Dannci has been building and selling WordPress themes since 2008. He knows classic theme architecture inside out; PHP templates, the Customizer, widget areas, all of it. This article comes from the middle of his own shift to block themes: the first FSE theme he’s currently developing for Themnific. If the learning curve feels steep, he’s on it right now too.
Last-Updated Date: 29 June 2026
Last modified:



