theme.json WordPress Settings: Complete Reference Guide

theme.json is the configuration file at the center of WordPress Full Site Editing. Every block theme includes one, and the theme.json WordPress settings inside it control how the block editor behaves for your entire site: what colors appear in the palette, which font sizes are available, whether users can set custom spacing values, how the site’s layout width is defined, and what global CSS variables get generated. Understanding theme.json thoroughly is the most leveraged skill for block theme development because every hour spent mastering it applies across every theme you build.

This guide covers every significant global setting and style in theme.json, organized by section and explained with the practical context you need to use each one correctly. The goal is a complete reference, not a tour of highlights, but the full map, with attention to the settings developers most commonly get wrong or misunderstand.

The examples in this post target theme.json schema version 2 and 3, which are current as of WordPress 6.5+. Most of the settings documented here have been stable since WordPress 6.1. Where a setting was introduced in a specific version or has known version-specific behavior, that is noted. For a practical guide on applying these settings in a complete design system, the theme.json design system guide covers how to move from token definitions to a production-ready implementation.

Code editor showing theme.json WordPress settings configuration file for block theme development

File Structure Overview

theme.json lives in the root of your theme directory. Its top-level structure has seven main properties: $schema, version, settings, styles, customTemplates, templateParts, and patterns. The schema and version properties are metadata; the remaining five define your theme’s design system and content structure.

The $schema property is the URL for the JSON schema that provides editor validation and autocomplete in code editors that support JSON schema (VS Code does, with the built-in JSON language server). Set it to the WordPress-hosted schema URL for the version of WordPress you are targeting: https://schemas.wp.org/trunk/theme.json for the latest version. This property does not affect runtime behavior, it’s purely for developer tooling.

The version property is currently either 2 or 3, with version 3 introduced in WordPress 6.6 adding additional layout and fluid typography features. Use version 2 for maximum compatibility with WordPress 6.1+; use version 3 if you are targeting 6.6+ and want the newer fluid type controls. Do not omit this property, WordPress needs it to interpret the file correctly.


theme.json WordPress Settings: Quick Reference Guide

Before diving into each section, here is a reference overview of the seven top-level sections in theme.json and what each one controls. Use this as a map as you work through the full details below.

SectionWhat It ControlsCSS Output?
settingsDesign tokens: colors, fonts, spacing, layout widths, available controlsYes, CSS custom properties and utility classes
stylesApplies design tokens to elements and blocks as actual CSS rulesYes, scoped element and block CSS
customTemplatesRegisters page templates (equivalent to old Template Name comment)No
templatePartsRegisters header, footer, and sidebar parts for the Site EditorNo
patternsBundles WordPress.org Pattern Directory slugs with the themeNo
versionSchema version (2 or 3), tells WordPress how to parse the fileNo
$schemaJSON schema URL for editor validation and autocompleteNo

settings: Color

The settings.color section controls your theme’s color system. It’s one of the most-used and most-misunderstood sections because it has both UI controls (what appears in the editor) and CSS output (what gets generated for the front end).

Color Palette

The palette array defines your named color set. Each entry has a name (displayed in the editor), a slug (used in CSS class and variable names), and a color value (any valid CSS color). WordPress generates two CSS artifacts from each palette entry: a CSS custom property (--wp--preset--color--{slug}) and utility classes (has-{slug}-color and has-{slug}-background-color).

Theme.json’s palette merges with or replaces the default WordPress color palette, depending on whether you set settings.color.defaultPalette to false. The default palette includes WordPress’s built-in colors (black, cyan-bluish-gray, white, pale-pink, etc.). Most theme developers want only their own palette colors, if so, set "defaultPalette": false. If you don’t, your theme colors appear alongside the WordPress defaults in a potentially cluttered color picker.

Custom, Duotone, and Gradient Settings

Four boolean settings control what users can do with color beyond the preset palette: custom (whether users can pick any arbitrary color), customDuotone (whether users can create custom duotone filters), customGradient (whether users can create custom gradients), and link (whether individual blocks expose a link color control). Setting any of these to false removes the corresponding UI control from the editor, which is the mechanism for enforcing design system consistency, if users can only choose from your palette, they stay on-brand by default.

The duotone array, like palette, defines preset duotone filters. Each entry has a name, a slug, and a colors array with exactly two values, the shadows color and the highlights color. These appear in the duotone picker for image blocks. The defaultDuotone boolean controls whether the WordPress default duotone filters appear alongside yours.

The gradients array defines preset gradients in the same name/slug pattern, with a gradient property containing a valid CSS gradient value. These appear in the gradient picker for blocks that support gradients (Cover, Button, Group). Set "defaultGradients": false to remove WordPress’s built-in gradient presets from the picker.


settings: Typography

Typography settings in theme.json cover font families, font sizes, custom font sizes, line heights, letter spacing, text decoration, writing mode, and fluid typography. This is the largest settings section and the one with the most interplay between preset values and CSS output.

Font Families

The fontFamilies array registers font families for use in the editor. Each entry has a name, a slug, and a fontFamily CSS property value. For web fonts bundled with your theme, you also include a fontFace array that generates the @font-face declarations, this replaces the need to register fonts with wp_enqueue_style for fonts included in your theme’s assets. WordPress generates a CSS custom property for each registered font family: --wp--preset--font-family--{slug}.

The bundled font approach (defining fontFace in theme.json) is cleaner than PHP registration for block themes because the font loading is fully declared in the theme.json design system rather than split between PHP and JSON. WordPress handles the font-face generation automatically. Use relative paths in src values (file:./assets/fonts/my-font.woff2) and WordPress resolves them correctly relative to the theme directory.

Font Sizes

The fontSizes array defines your type scale. Each entry has a name, slug, and size value. WordPress generates CSS custom properties (--wp--preset--font-size--{slug}) and utility classes (has-{slug}-font-size) for each preset. The T-shirt size naming convention (small, medium, large, x-large, xx-large) is common in themes and is used in WordPress’s own default sizes, but you can name and slug your presets however your design system requires.

Fluid typography is set per font size using the fluid property. Setting "fluid": true lets WordPress calculate fluid values using clamp(); setting it to an object with min and max properties gives you explicit control over the range. The settings.typography.fluid boolean at the settings level enables the feature globally; individual font size entries can then opt in with their own fluid configuration. This system was introduced in WordPress 6.1 and refined in subsequent versions, if you are targeting older WordPress versions, test fluid typography behavior carefully.

Typography Boolean Controls

Several boolean settings control what typography UI appears in the editor: customFontSize (whether users can enter arbitrary font sizes), lineHeight (whether the line height control appears), letterSpacing (letter spacing control), textDecoration (underline/strikethrough), textTransform (uppercase/lowercase/capitalize), and writingMode (horizontal vs. vertical text). These are all false by default in WordPress and must be explicitly enabled in theme.json. Most theme developers enable lineHeight, letterSpacing, and textDecoration at minimum.


settings: Spacing

Spacing in theme.json covers margin, padding, block gap, and spacing scale. The spacing system underwent significant development between WordPress 5.9 and 6.2, and the current implementation is much more capable than earlier versions, but it has some non-obvious behavior worth understanding.

The spacingSizes array (available from WordPress 6.1) defines a spacing scale analogous to font sizes, named, slugged spacing values that generate CSS custom properties (--wp--preset--spacing--{slug}) and can be referenced in block spacing controls. The default WordPress spacing scale uses numeric slugs (10, 20, 30, 40, 50, 60, 70, 80) and T-shirt-size names. You can replace this with your own scale or supplement it.

The boolean controls for spacing are: margin (whether blocks expose margin controls), padding (whether blocks expose padding controls), blockGap (whether blocks expose gap controls for child elements), and customSpacingSize (whether users can enter arbitrary spacing values). These are all false by default. Enabling blockGap is particularly important for Columns, Group, and other container blocks where you want users to control the space between child blocks.

The units property under settings.spacing (available from WordPress 6.3) restricts what CSS units are available in the spacing pickers. By default, users can enter values in px, em, rem, %, vw, vh, and more. If your design system uses rem only, setting "units": ["px", "rem"] limits the unit choices accordingly.


settings: Layout

The settings.layout section defines your theme’s content width system. It has two key properties: contentSize (the default width for content blocks, text, images, etc.) and wideSize (the maximum width for “wide aligned” blocks). These values drive the CSS layout system that WordPress generates for all block themes.

When you set these values, WordPress generates CSS custom properties (--wp--style--global--content-size and --wp--style--global--wide-size) and uses them in its block layout CSS. Blocks using the “default” alignment are constrained to contentSize; blocks set to “wide” alignment can extend to wideSize; blocks set to “full width” extend to the container edge. This system is what makes block themes produce proper content width behavior without custom CSS.

The settings.layout.allowEditing boolean (available from WordPress 6.1) controls whether users can change layout settings for individual blocks in the editor. Setting this to false locks layout to what the theme defines, useful for designs where the content width must be strictly controlled. It is true by default.


settings: Blocks

The settings.blocks section lets you override global settings for specific blocks. Any setting that applies globally under settings can be scoped to a specific block using its block name as a key. This is one of the most powerful features in theme.json for design system control.

For example, if you want to enable custom color picking globally but disable it specifically for the Button block (to force buttons to use only palette colors): set settings.color.custom: true globally, then add settings.blocks["core/button"].color.custom: false to override that for buttons. The block-level setting takes precedence over the global setting for that specific block.

Common uses of per-block settings overrides include: disabling custom font sizes for headings while allowing them globally, restricting the color palette for specific decorative elements, enabling spacing controls only for certain container blocks, and setting different layout constraints for specific block types. This granularity allows theme developers to give users flexibility where it’s appropriate and lock down the design system where consistency matters most.


settings: Custom

The settings.custom section is an escape hatch for design tokens that don’t fit into the standard settings categories. Any values defined here generate CSS custom properties using a flattened naming convention: nested object keys become hyphen-separated segments in the CSS variable name, under the --wp--custom-- prefix.

For example, a settings.custom.button.borderRadius: "4px" value generates --wp--custom--button--border-radius: 4px. This allows you to define design tokens, border radius values, shadow definitions, animation durations, z-index scale, that are accessible as CSS variables throughout your stylesheets and template files. The custom section is useful for any theme that wants to maintain a single source of truth for design values beyond color and typography.

Note that custom properties generated from settings.custom do not appear in the block editor UI, they don’t show up in color pickers or font size dropdowns. They are CSS variables only. Use them to drive your theme’s stylesheets and template styles, not for editor-exposed controls.


styles: Global Styles

The styles section is where you apply your settings to produce actual CSS output. If settings defines your design tokens, styles applies them to elements. Global styles in styles apply to the body element and cascade down to all blocks unless overridden.

The styles.color object sets the global text color and background color. Use preset values (referencing your palette slugs) via the var syntax: "color": "var:preset|color|foreground". This syntax is how theme.json references preset values, it uses pipe-separated segments that WordPress resolves to the corresponding CSS custom property.

The styles.typography object sets global font family, size, line height, font style, font weight, letter spacing, and text decoration. Again, use preset references where possible. Setting the global font family to a preset registered in settings.typography.fontFamilies means the CSS custom property is used throughout the stylesheet rather than the raw font stack value.

The styles.spacing object can set global margin and padding, though these are less commonly used at the global level, it’s more common to set them per-element or per-block.


styles: Elements

The styles.elements section lets you style specific HTML elements site-wide: links, headings (h1 through h6), buttons, captions, and citations. This is the recommended place to define your theme’s link color, heading hierarchy, and button styles.

Links

The styles.elements.link object styles anchor tags globally. It supports color, typography, and spacing sub-objects with the same properties as global styles. More usefully, it supports pseudo-classes via :hover, :focus, :active, and :visited nested objects. This is how you define link hover colors in theme.json, an extremely common requirement that many developers miss because the pseudo-class support is not prominently documented:

Set styles.elements.link[":hover"].color.text to a color value (using preset syntax or a raw value) to define the link hover color. The same pattern applies to focus, active, and visited states. This pseudo-class support was added in WordPress 6.1 and is one of the most practically useful additions to the styles section.

Headings

Heading elements (h1 through h6) are styled in styles.elements.h1 through styles.elements.h6. Each supports the same typography and color sub-objects. Setting distinct typography values for each heading level is how you encode your heading hierarchy in theme.json, font size, weight, line height, and letter spacing for each level. This replaces the heading typography definitions that would otherwise require a stylesheet.

Buttons

Button element styles in styles.elements.button control the default appearance of all button elements on the site, including the Button block. You can set background color, text color, border (using the border sub-object which supports color, radius, style, and width), and padding. Pseudo-classes work here too for hover and focus states. Defining your button styles in theme.json’s elements section means your button appearance is encoded in the theme design system rather than in stylesheet-only CSS.


styles: Blocks

The styles.blocks section applies styles to specific block types, identified by their block name (e.g., core/paragraph, core/heading, core/button, core/group). This is the most granular level of styling in theme.json and covers the majority of block-specific style declarations.

The structure mirrors the global styles structure: each block entry supports color, typography, spacing, border, and dimensions sub-objects. You can define the default appearance of every core block, the Paragraph block’s font size, the Quote block’s border and padding, the Separator block’s color and height, the Code block’s background and font family.

Block styles can also set variations, named style variations for a block that appear in the editor as selectable styles. A button with variations for “Fill” and “Outline” presents those as distinct options in the block styles panel, with the CSS differences encoded in theme.json rather than in a stylesheet. This is the theme.json approach to what was previously done with PHP register_block_style() calls plus a CSS class.

One important nuance: block styles defined in theme.json use the block’s CSS selector as the scope. WordPress generates styles scoped to .wp-block-{block-name} for front-end output and unscoped (or editor-scoped) for the editor. If you need more specific CSS targeting, for example, styling a block only when it’s inside another specific block, you’ll need a stylesheet rather than theme.json, since theme.json does not support descendant or combinator selectors.


customTemplates

The customTemplates array registers custom page templates that your theme provides. Each entry has a name (the filename without .html, e.g., page-no-title), a title (displayed in the editor’s template dropdown), and a postTypes array (the post types this template applies to, typically ["page"] or ["post", "page"]).

Registering custom templates in theme.json makes them available in the editor’s Template dropdown for the specified post types. The corresponding template file (e.g., templates/page-no-title.html) must exist in your theme. This is the block theme equivalent of the old Template Name: ... comment in PHP template files.


templateParts

The templateParts array registers the template parts your theme provides, the reusable structural blocks like header, footer, and sidebar. Each entry has a name (the filename without .html), an area (one of header, footer, sidebar, or uncategorized), and optionally a title. The area classification affects how the part is displayed and can be filtered in the Site Editor.

Template parts registered in theme.json appear in the Site Editor’s template part library and can be inserted into templates via the Template Part block. The area classification also determines which interface in the Site Editor shows the part, header and footer parts appear in their respective navigation sections. For a detailed walkthrough of building header and footer template parts with flexible layout options, the template parts customization guide covers the implementation patterns in depth.


patterns

The patterns array (available from WordPress 6.0) lists WordPress.org pattern slugs that your theme wants to bundle. When a user installs your theme, the listed patterns are automatically fetched from the WordPress.org Pattern Directory and made available in their site’s pattern library. This is different from patterns bundled locally in your theme’s patterns/ directory, the patterns key in theme.json specifically references remote patterns by slug.

For themes that want to ship a curated set of patterns from the directory without bundling the pattern files themselves, this provides a lightweight way to extend the pattern library. For patterns you create yourself and want to bundle with your theme, place them in the patterns/ directory with the standard block pattern header comments, those are registered automatically and don’t require a theme.json entry.


Common Mistakes and How to Avoid Them

Working with theme.json extensively across multiple themes reveals a set of mistakes that appear consistently. Knowing them in advance saves debugging time.

  • Forgetting to set defaultPalette: false: WordPress’s built-in color palette appears alongside your theme’s palette by default, creating a cluttered, inconsistent color picker. Always explicitly set settings.color.defaultPalette: false unless you intentionally want both.
  • Using hex values directly in styles instead of preset references: Hardcoding #3A5BFF in a style value works but breaks the connection between your settings and your styles, if you update the palette color, the hardcoded style doesn’t change. Use the var:preset|color|{slug} syntax to reference presets.
  • Missing the version property: Omitting "version": 2 (or 3) causes WordPress to use fallback parsing that may not interpret your theme.json correctly. Always include the version.
  • Not scoping settings overrides correctly: The settings.blocks structure uses block names as keys, and they must be exact: core/button, not button or wp/button. Wrong block names silently fail, the setting is simply ignored.
  • Expecting theme.json to handle all CSS needs: Theme.json covers global settings and styles elegantly, but it doesn’t support every CSS use case. Pseudo-elements (::before, ::after), complex selectors, media queries, keyframe animations, and anything requiring CSS cascade manipulation beyond element/block scope still belongs in a stylesheet.
  • fontFace src paths pointing to wrong locations: Font file paths in settings.typography.fontFamilies[].fontFace[].src must use the file: prefix to indicate a theme-relative path. Omitting the prefix or using an absolute path causes font loading failures.

Child Theme Inheritance

Child themes in FSE inherit the parent theme’s theme.json but can override any part of it. The child theme’s theme.json is merged with the parent’s, with the child taking precedence for any key that exists in both. This inheritance model means you can create child themes that make minimal changes, override just the color palette, or just the font sizes, or just a few block styles, without duplicating the entire parent theme.json.

The merge is deep for nested objects: if the parent defines six palette colors and the child defines theme.json with a palette array, the child’s array replaces the parent’s array entirely, it does not append to it. If you want to add colors to the parent palette in a child theme, you need to include all of the parent’s colors plus your additions in the child’s palette array. This full-replacement behavior for arrays is the most common source of confusion when building child themes for block themes.

User customizations made through the Site Editor’s Global Styles panel are stored in the database as a global styles post, which takes precedence over theme.json. When a user sets a custom color or font size in the editor, that choice overrides the theme.json definition, which is the correct behavior from a user control perspective, but means that theme.json is not the final word on what users see in their fully customized sites. To reset to theme.json defaults, users can use the “Reset to defaults” option in the Global Styles panel.

Wrapping Up

Theme.json is a dense specification that rewards thorough study. The settings that seem redundant or obscure, like customFontSize being separate from whether font size presets exist, or the distinction between blockGap and margin, each address specific design system requirements that come up in real projects.

The practical approach to mastering theme.json is to work through building a theme from scratch using it as the primary design system configuration: define your color palette, register your fonts, establish your type scale, set your layout widths, and apply global styles, element styles, and block styles progressively. The moment you see how precisely the editor responds to each setting, palette colors appearing in the color picker, font families populating the typography panel, spacing controls appearing only where you’ve enabled them, the abstract specification becomes concrete and the logic of the system becomes clear.

For advanced block theme development, custom templates, template parts, block variations, and the block.json ecosystem that works alongside theme.json, the WordPress Developer Handbook and the Gutenberg repository’s theme.json documentation provide the authoritative reference. But for the vast majority of block theme design system configuration, this guide covers the full scope of what’s available and what each setting does.

Scroll to Top