theme.json Generator
Palette, type scale, spacing and layout in one file — and the CSS custom properties WordPress generates from them, so you know what to write in your stylesheet.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"appearanceTools": true,
"useRootPaddingAwareAlignments": true,
"layout": {
"contentSize": "720px",
"wideSize": "1200px"
},
"color": {
"custom": false,
"customGradient": false,
"customDuotone": false,
"link": true,
"defaultPalette": false,
"palette": [
{
"slug": "base",
"name": "Base",
"color": "#FAF9F7"
},
{
"slug": "contrast",
"name": "Contrast",
"color": "#26221C"
},
{
"slug": "primary",
"name": "Primary",
"color": "#3858E9"
},
{
"slug": "muted",
"name": "Muted",
"color": "#948C7E"
}
]
},
"typography": {
"customFontSize": false,
"fontStyle": false,
"fontWeight": false,
"lineHeight": true,
"letterSpacing": false,
"textDecoration": false,
"textTransform": false,
"fluid": true,
"fontSizes": [
{
"slug": "small",
"name": "Small",
"size": "0.8rem"
},
{
"slug": "medium",
"name": "Medium",
"size": "1rem"
},
{
"slug": "large",
"name": "Large",
"size": "1.25rem"
},
{
"slug": "x-large",
"name": "X Large",
"size": "1.563rem"
},
{
"slug": "xx-large",
"name": "Xx Large",
"size": "1.953rem"
}
],
"fontFamilies": [
{
"slug": "body",
"name": "Body",
"fontFamily": "\"Instrument Sans\", system-ui, sans-serif"
}
]
},
"spacing": {
"customSpacingSize": false,
"padding": true,
"margin": true,
"blockGap": true,
"units": [
"px",
"rem",
"em",
"%",
"vw"
],
"spacingSizes": [
{
"slug": "10",
"name": "Step 1",
"size": "0.444rem"
},
{
"slug": "20",
"name": "Step 2",
"size": "0.667rem"
},
{
"slug": "30",
"name": "Step 3",
"size": "1rem"
},
{
"slug": "40",
"name": "Step 4",
"size": "1.5rem"
},
{
"slug": "50",
"name": "Step 5",
"size": "2.25rem"
},
{
"slug": "60",
"name": "Step 6",
"size": "3.375rem"
},
{
"slug": "70",
"name": "Step 7",
"size": "5.063rem"
}
]
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--base)",
"text": "var(--wp--preset--color--contrast)"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--body)",
"fontSize": "var(--wp--preset--font-size--medium)",
"lineHeight": "1.6"
},
"spacing": {
"padding": {
"left": "clamp(1rem, 4vw, 2rem)",
"right": "clamp(1rem, 4vw, 2rem)"
},
"blockGap": "var(--wp--preset--spacing--30)"
},
"elements": {
"link": {
"color": {
"text": "var(--wp--preset--color--primary)"
},
":hover": {
"typography": {
"textDecoration": "none"
}
}
},
"button": {
"color": {
"background": "var(--wp--preset--color--primary)",
"text": "var(--wp--preset--color--base)"
},
"border": {
"radius": "4px"
},
"spacing": {
"padding": {
"top": "0.7rem",
"bottom": "0.7rem",
"left": "1.4rem",
"right": "1.4rem"
}
},
"typography": {
"fontWeight": "600"
}
},
"heading": {
"typography": {
"fontWeight": "700",
"lineHeight": "1.2"
}
},
"h1": {
"typography": {
"fontSize": "var(--wp--preset--font-size--xx-large)"
}
},
"h2": {
"typography": {
"fontSize": "var(--wp--preset--font-size--x-large)"
}
},
"h3": {
"typography": {
"fontSize": "var(--wp--preset--font-size--large)"
}
}
}
},
"templateParts": [
{
"name": "header",
"title": "Header",
"area": "header"
},
{
"name": "footer",
"title": "Footer",
"area": "footer"
}
]
}
About this generator
theme.json Generator Online
Build a valid version 3 theme.json from a palette, a type scale, a spacing scale and a layout width, and get the CSS custom properties WordPress will generate from it in the same view. Presets become --wp--preset--color--{slug} and --wp--preset--font-size--{slug} names you can use straight from a stylesheet, so the file doubles as your design-token source rather than being a black box.
The CSS vars tab lists every custom property the file produces, and the type scale is computed live from your base size and ratio so you can see the rendered px value of each step. Free to use, no account, and nothing you enter leaves the browser.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why the theme.json generator beats editing the file by hand
Nothing in theme.json throws an error. A colour value with a typo, a contentSize larger than wideSize, a heading font defined with no style layer that applies it — the file parses, WordPress loads it, and the result is quietly wrong in the editor. There is no hook and no PHP to debug. This generator applies the checks the format itself does not, and shows you the custom properties before you go and write CSS against names that do not exist.
Palette slugs validated as CSS names
The slug becomes the custom property name, so a missing slug is an error and a duplicate slug is an error — one property would overwrite the other. Colour values are checked for hex, rgb(), hsl(), oklch() or var() form.
A type scale you set by ratio, not by hand
Small through xx-large are computed from your base size and modular ratio and shown with their px equivalents, then written as settings.typography.fontSizes. A px base size is flagged, because it ignores the reader's browser setting.
The settings-versus-styles distinction made concrete
Naming a colour base/background, contrast/text or primary/accent wires it into the styles layer as the real background, text and link colour — not just an option in the picker. Missing either of the first two raises a warning.
Layout checks that catch a genuine ordering mistake
contentSize and wideSize must both be CSS lengths, and a contentSize larger than wideSize is an error with a one-click swap, because wide alignment would render narrower than normal content.
Root padding and alignment kept consistent
Setting a root padding turns on useRootPaddingAwareAlignments and writes the left/right padding into styles.spacing, so full-width content still clears the viewport edge on phones. Leaving it empty is flagged with a clamp() suggestion.
Honest notes about the controls you enable
The custom colour picker alongside a curated palette, and arbitrary font sizes alongside a defined scale, both undo the consistency you just set up. Both are flagged as notes, along with the reminder that appearanceTools is a shorter way to enable border, colour, spacing and typography controls together.
How does the theme.json Generator work?
Four steps. The JSON and the CSS custom property list both update on every change, so you never wonder what a setting will produce.
- 01
Define the palette
Add colours with a slug, a display name and a value. Slugs named base, contrast and primary are picked up automatically by the styles layer for background, text and link colour.
- 02
Set the typography
Choose a body font stack, an optional heading stack, a base size and a modular ratio. The five preset sizes are computed and shown in px so you can sanity-check the smallest one.
- 03
Set the layout and spacing
Give
contentSizeandwideSizereal CSS lengths, choose how many spacing steps to generate, and set the root padding — aclamp()is the usual answer. - 04
Choose the editor controls, then export
Turn individual controls on or off, add the heading, button and template-part layers, clear the Checks tab, then copy the JSON or the CSS variables.
Worked example — palette, layout and root padding
The colour and layout portion of a generated file, with objects collapsed onto single lines for brevity. The full output also carries the typography scale, the spacing steps and every control flag.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"appearanceTools": true,
"useRootPaddingAwareAlignments": true,
"layout": { "contentSize": "720px", "wideSize": "1200px" },
"color": {
"defaultPalette": false,
"palette": [
{ "slug": "base", "name": "Base", "color": "#FAF9F7" },
{ "slug": "contrast", "name": "Contrast", "color": "#26221C" },
{ "slug": "primary", "name": "Primary", "color": "#3858E9" }
]
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--base)",
"text": "var(--wp--preset--color--contrast)"
},
"spacing": {
"padding": { "left": "clamp(1rem, 4vw, 2rem)", "right": "clamp(1rem, 4vw, 2rem)" }
}
}
}Those three palette entries become --wp--preset--color--base, --wp--preset--color--contrast and --wp--preset--color--primary, which the CSS vars tab lists in full so your stylesheet can use them directly.
theme.json — frequently asked questions
The questions people ask when a theme.json change does not show up where they expected.
Does a child theme.json replace the parent's?
No, it is merged over it. WordPress resolves core defaults, then the parent theme, then the child theme, then the user's own Site Editor changes, with each layer overriding the one before at the level of individual keys. So a child theme.json only needs the keys it is actually changing — copying the parent's whole file across is the mistake that makes every future parent update invisible.
Which theme.json version should I use?
Version 3 is current and is what this generator emits. The number is not decorative: it changes how some defaults are interpreted, notably the default font size and spacing presets, so bumping it on an existing theme can shift rendering. If your parent theme declares a different version, WordPress applies the behaviour of the higher one, which is another reason to check the parent before changing yours.
Why is my theme.json colour not showing on the front end?
Because settings.color.palette only offers the colour, it does not apply it. A palette entry becomes a swatch in the picker and a --wp--preset--color--{slug} custom property; something still has to use it. Set the value under styles — styles.color.background, styles.elements.link.color.text and so on — referencing the property with var(--wp--preset--color--slug).
Does my CSS override theme.json styles?
Yes, by default. Styles generated from theme.json are printed in the head before your theme's style.css, so an equal-specificity rule in your stylesheet wins. The exception is the user's own changes in the Site Editor: those are saved to a separate wp_global_styles post and printed later, so they beat both your theme.json and your stylesheet.
Can a classic theme use theme.json?
Yes. A classic (non-block) theme that ships a theme.json gets the palette, the font size presets, the spacing scale and the editor controls, and it stops WordPress applying its own default palette and sizes. You do not need block templates for any of that. It is the cleanest way to make the block editor match a classic theme's design.
What are the generated CSS custom properties called?
The naming is fully predictable: every preset becomes --wp--preset--{category}--{slug}, such as --wp--preset--color--primary or --wp--preset--font-size--large, and everything under settings.custom becomes --wp--custom--{path} with camelCase keys converted to dashes. Because those names are stable, theme.json works as a design-token file you can rely on from plain CSS.
Where do I paste the code from the theme.json generator?
theme.json goes in the root of your theme folder, not in functions.php. If you are editing a child theme, your theme.json is merged over the parent's — you only need to declare the keys you are changing.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other Design generators in the library, plus the WordPress tools most often used alongside this one.