theme.json
theme.json is the design source of truth for WordPress block themes. Tokens, settings, styles, and the patterns that keep the editor clean.
WordPress — theme.json
EXAMPLE
// ===== Why theme.json =====
// In block themes, theme.json centralises:
// - Design tokens (colours, fonts, spacing, layout)
// - Editor settings (what is allowed)
// - Global styles (block-level defaults)
// It generates CSS custom properties + inline editor UI.
// ===== Minimal theme.json =====
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"layout": { "contentSize": "720px", "wideSize": "1200px" },
"color": {
"palette": [
{ "slug": "primary", "color": "#2563eb", "name": "Primary" },
{ "slug": "foreground", "color": "#0f172a", "name": "Foreground" },
{ "slug": "background", "color": "#ffffff", "name": "Background" }
],
"custom": false,
"customGradient": false,
"defaultPalette": false
},
"typography": {
"fluid": true,
"fontFamilies": [
{
"slug": "sans", "name": "Sans",
"fontFamily": "Inter, system-ui, sans-serif"
}
],
"fontSizes": [
{ "slug": "sm", "size": "14px", "name": "Small" },
{ "slug": "md", "size": "16px", "name": "Medium" },
{ "slug": "lg", "size": "20px", "name": "Large" },
{ "slug": "xl", "size": "32px", "name": "X-Large" }
]
},
"spacing": {
"units": ["px", "rem", "em"],
"spacingScale": { "steps": 8 }
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--background)",
"text": "var(--wp--preset--color--foreground)"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--sans)",
"fontSize": "var(--wp--preset--font-size--md)"
},
"elements": {
"link": { "color": { "text": "var(--wp--preset--color--primary)" } },
"h1": { "typography": { "fontSize": "var(--wp--preset--font-size--xl)" } }
},
"blocks": {
"core/button": {
"color": {
"background": "var(--wp--preset--color--primary)",
"text": "var(--wp--preset--color--background)"
}
}
}
}
}
// ===== Generated CSS custom properties =====
// --wp--preset--color--primary: #2563eb;
// --wp--preset--font-size--md: 16px;
// Use these anywhere in templates / parts.
// ===== Disabling features =====
// settings.color.custom = false -> remove the colour picker in editor
// settings.typography.fontSizes (with no slugs) -> remove default sizes
// settings.spacing.units -> restrict allowed units
// Discipline: lock the design system inside theme.json so editors can't drift.
// ===== Per-block overrides =====
// styles.blocks['core/heading'].typography.fontWeight = '700'
// Applies to that block specifically, not globally.
// ===== Patterns (template parts can reference tokens) =====
// patterns/cta.php:
// register_block_pattern( 'mytheme/cta', [
// 'title' => __( 'Call to action', 'mytheme' ),
// 'content' => '<!-- wp:button { "backgroundColor": "primary" } --><div class="wp-block-button"><a class="wp-block-button__link wp-element-button has-primary-background-color has-background">Sign up</a></div><!-- /wp:button -->',
// ] );
// ===== When theme.json wins =====
// - Block themes (FSE)
// - Strict brand guidelines you want editors to follow
// - Consistent design across multiple block patterns
// - Avoiding stylesheet drift
// ===== Pitfalls =====
// - Mixing custom CSS that overrides theme.json -> drift between editor + frontend
// - Not pinning '$schema' URL — schema validators help during edit
// - Overriding core blocks via styles.blocks AND functions.php register_block_style -> precedence confusion
// - Removing default colour palette without providing one -> empty colour picker
Why it matters
theme.json is the design contract for block themes. Define palette, typography, spacing once; the editor + the frontend consume the same tokens. Lock down customisation flags to prevent drift and reference CSS custom properties in templates. Less stylesheet, more system.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
{
"version": 2,
"settings": {
"color": { "palette": [{ "slug": "brand", "color": "#04AA6D", "name": "Brand" }] }
}
}
Try it Yourself »
Discussion
Loading…