WordPress 5.8 made theme.json the canonical way for block themes to declare their design system: the colour palette, font size scale, spacing scale, typography settings, and layout constraints. A single JSON file in the theme root replaces dozens of scattered add_theme_support() calls and eliminates the need for theme-specific CSS variables for these design tokens. Every value declared in theme.json is automatically exposed as a CSS custom property (--wp--preset--color--primary, --wp--preset--font-size--large, etc.) that can be used anywhere in the theme’s CSS. The block editor reads theme.json to populate the colour pickers, font size sliders, and spacing controls in the sidebar — creating a direct connection between the design system and the editor UI without any PHP. Classic themes (non-block themes) can also include a theme.json to get editor colour palettes and font size support without full FSE adoption.
Problem: A new block theme needs a consistent colour palette, a type scale with five sizes, and a spacing scale — all surfaced in the editor sidebar for content editors, and all available as CSS custom properties for use in block stylesheets, without writing separate PHP registration calls for each.
Solution: Define the full design system in theme.json under settings.color.palette, settings.typography.fontSizes, and settings.spacing.spacingSizes. WordPress generates the CSS custom properties and editor controls automatically.
// theme.json (place in theme root directory)
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 2,
"settings": {
"color": {
"palette": [
{ "slug": "primary", "name": "Primary", "color": "#1e40af" },
{ "slug": "secondary", "name": "Secondary", "color": "#0d7377" },
{ "slug": "accent", "name": "Accent", "color": "#f5c842" },
{ "slug": "light", "name": "Light", "color": "#f8fafc" },
{ "slug": "dark", "name": "Dark", "color": "#0f172a" }
],
"gradients": [
{
"slug": "primary-to-secondary",
"name": "Primary to Secondary",
"gradient": "linear-gradient(135deg, #1e40af 0%, #0d7377 100%)"
}
],
"custom": false, // disallow custom color picker
"customGradient": false // disallow custom gradients
},
"typography": {
"fontSizes": [
{ "slug": "small", "name": "Small", "size": "0.875rem" },
{ "slug": "normal", "name": "Normal", "size": "1rem" },
{ "slug": "medium", "name": "Medium", "size": "1.25rem" },
{ "slug": "large", "name": "Large", "size": "1.75rem" },
{ "slug": "x-large","name": "Extra Large","size": "2.5rem" }
],
"fontFamilies": [
{
"slug": "sans",
"name": "Sans Serif",
"fontFamily": "Inter, system-ui, sans-serif"
},
{
"slug": "mono",
"name": "Monospace",
"fontFamily": "'JetBrains Mono', monospace"
}
]
},
"spacing": {
"spacingSizes": [
{ "slug": "20", "name": "XS", "size": "0.5rem" },
{ "slug": "30", "name": "S", "size": "1rem" },
{ "slug": "40", "name": "M", "size": "1.5rem" },
{ "slug": "50", "name": "L", "size": "2rem" },
{ "slug": "60", "name": "XL", "size": "3rem" },
{ "slug": "70", "name": "2XL", "size": "4.5rem" }
],
"padding": true,
"margin": true
},
"layout": {
"contentSize": "720px", // max width for paragraph, heading blocks
"wideSize": "1280px" // max width for wide-aligned blocks
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--light)",
"text": "var(--wp--preset--color--dark)"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--sans)",
"fontSize": "var(--wp--preset--font-size--normal)",
"lineHeight": "1.6"
}
}
}
NOTE: WordPress generates CSS custom properties from the palette automatically — the primary colour slug becomes --wp--preset--color--primary and the class .has-primary-color / .has-primary-background-color. These classes and properties are used by blocks when a user selects a preset colour in the editor. Setting "custom": false hides the custom colour picker in the editor — users can only choose from the defined palette, which enforces brand colour usage. The styles section in theme.json applies CSS to the document (body) and to specific block types — for example, "styles.blocks.core/heading" sets defaults for all heading blocks. Classic themes (not full-site-editing) can use a minimal theme.json with just the settings section to get editor colour and font-size controls without adopting the rest of FSE.