Full Site Editing and theme.json: The Complete Guide

by Francis Rozange | Oct 2, 2026 | Gutenberg

A WordPress block theme needs only two files to exist: a stylesheet that declares it, and a templates/index.html template. Everything else, colors, typography, spacing, widths, can live in a third file, theme.json, which drives both the editor and the site.

That file is not just for block themes. WordPress loads its own default theme.json on every site, including those running a classic theme. In the summer of 2024, a change to those global styles underlined every navigation link on sites that had never touched the Site Editor.

This guide covers Full Site Editing as it stands in WordPress 7.1: block, classic and hybrid themes, templates and template parts, the structure of theme.json version 3, fluid typography, style variations, what 7.0 and 7.1 added, locking the editor for a client and migrating a classic theme.

Block, classic, hybrid: three ways to build a theme

What makes a block theme

WordPress officially recognizes two types of themes: block themes and classic themes. A block theme is made of HTML templates written in block markup, editable in the Site Editor, and of global styles defined in theme.json. A classic theme relies on the PHP template hierarchy and the Customizer, both still supported.

The line comes down to one file: WordPress treats a theme as a block theme as soon as it contains templates/index.html. A theme.json alone is not enough. Kadence and Blocksy ship one and remain classic themes.

The admin menu reflects the difference. With a block theme, Appearance opens the Site Editor, and the Customizer disappears unless a plugin or the theme hooks into it.

With a classic theme, the Site Editor only exists in a reduced form, without templates or global styles. Since WordPress 6.8, the Appearance menu offers Design, with the Style Book and patterns, when the theme has a theme.json or editor styles, and simply Patterns otherwise.

Hybrid themes, an unofficial middle ground

The documentation makes it clear: hybrid themes are not an official type. They are classic themes that adopt some block features, such as a theme.json, block template parts, block styles or patterns. The WordPress Developer Blog presents them as a bridge: they support the block editor, not the Site Editor.

It is often the most realistic way to evolve an existing theme without rewriting everything. Our selection of free WordPress block themes shows where ready-made themes stand.

Templates and template parts

The block template hierarchy

A block theme’s templates go in the /templates folder, template parts in /parts, patterns in /patterns and style variations in /styles. Only style.css and templates/index.html are required.

To display a page, WordPress first looks for a template created by the user and saved in the database, then in the child theme, then in the parent theme. For a post, for example, it tries a template specific to the content type, then single.html, then singular.html, and always ends with index.html. The front page uses front-page.html if it exists, whatever the reading setting.

Here is a minimal index.html template, in block markup.

<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
	<!-- wp:query {"query":{"inherit":true}} -->
	<div class="wp-block-query">
		<!-- wp:post-template -->
			<!-- wp:post-title {"isLink":true} /-->
			<!-- wp:post-content {"layout":{"type":"constrained"}} /-->
		<!-- /wp:post-template -->
		<!-- wp:query-pagination -->
			<!-- wp:query-pagination-previous /-->
			<!-- wp:query-pagination-next /-->
		<!-- /wp:query-pagination -->
	</div>
	<!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

WordPress generates the html, head and body tags around this content itself.

Template parts and areas

Template parts are reusable fragments, such as the header or footer. They sit flat in the /parts folder, since WordPress does not support subfolders; the legacy folder name block-template-parts is still read for backward compatibility. Each can be attached to an area: header, footer, general, and since WordPress 7.0, the navigation overlay.

What users change, and where it is saved

Changes made in the Site Editor do not touch the theme files. They are saved in the database, in content types dedicated to templates, template parts and global styles. They take precedence over the files and survive theme updates.

A practical consequence: a fix the theme author makes to a template does not reach a site where the user has already modified that template. To bring those changes back into files, the Site Editor offers a theme export, and the Create Block Theme plugin can save them back into the active theme.

Block child themes

A block theme accepts a child theme, just like a classic theme. WordPress then looks for each template in the child theme before the parent, and the child’s theme.json complements the parent’s instead of replacing it. Since WordPress 6.2, the child theme also inherits the parent’s style variations, and a variation saved under the same file name in the child replaces the parent’s.

That is the right way to adapt a directory theme without losing its updates. Create Block Theme can also create a child theme from the active theme.

theme.json, the file that drives the design

Structure and schema version

The theme.json file arrived with WordPress 5.8, its version 2 with WordPress 5.9 and its version 3 with WordPress 6.6, in July 2024. In WordPress 7.1, the current version is still 3: there is no version 4, neither in core nor in the official schemas.

Be wary of the Theme Handbook: its introduction page, updated as recently as June 2026, still says version 2, and its examples use it. The up-to-date reference is the one in the Block Editor Handbook, which, however, also documents keys from the development branch.

To validate your file, the Themes team recommends pointing the $schema key to the schema for the minimum WordPress version your theme requires, for example 7.1’s for a theme that requires 7.1, rather than the development branch schema, which already contains keys missing from core.

Settings and styles

The file splits into two main sections. Settings, settings, determine which options the editor offers and which presets exist: color palette, font sizes, spacing, shadows. Styles, styles, define the CSS actually applied, to the whole site, to elements such as links and headings, or to specific blocks.

Each preset generates a CSS variable, such as --wp--preset--color--accent, and colors, gradients, font sizes and font families also generate a class, such as .has-accent-color. In the file, you reference it with a dedicated syntax, for example var:preset|color|accent. The appearanceTools option turns on a whole set of design tools at once: borders, link colors, margins, minimum heights, sticky positioning.

A starter example

This file, validated against the WordPress 7.1 schema, sets a three-color palette, fluid font sizes, a spacing scale and link styles.

{
	"$schema": "https://schemas.wp.org/wp/7.1/theme.json",
	"version": 3,
	"settings": {
		"appearanceTools": true,
		"layout": { "contentSize": "720px", "wideSize": "1200px" },
		"color": {
			"defaultPalette": false,
			"palette": [
				{ "slug": "base", "color": "#ffffff", "name": "Base" },
				{ "slug": "contrast", "color": "#111111", "name": "Contrast" },
				{ "slug": "accent", "color": "#667eea", "name": "Accent" }
			]
		},
		"typography": {
			"fluid": true,
			"defaultFontSizes": false,
			"fontSizes": [
				{ "slug": "small", "size": "1rem", "name": "Small", "fluid": false },
				{ "slug": "large", "size": "2rem", "name": "Large", "fluid": { "min": "1.5rem", "max": "2.5rem" } }
			]
		},
		"spacing": {
			"defaultSpacingSizes": false,
			"spacingScale": { "operator": "*", "increment": 1.5, "steps": 7, "mediumStep": 1.5, "unit": "rem" }
		}
	},
	"styles": {
		"color": { "background": "var:preset|color|base", "text": "var:preset|color|contrast" },
		"elements": {
			"link": {
				"color": { "text": "var:preset|color|accent" },
				":hover": { "color": { "text": "var:preset|color|contrast" } }
			}
		}
	}
}

Per-block settings and elements

A global setting can be overridden for a specific block in settings.blocks, and a style in styles.blocks. Watch out for a typo in the Theme Handbook, which writes “settings.block” in the singular: the correct key is plural. Elements, such as links, buttons or headings, are set in styles.elements, with states such as hover or focus for links and buttons only.

Fluid typography and spacing

Fluid typography makes text size vary between a minimum and a maximum depending on screen width, with the CSS clamp() function. It is off by default in core: you turn it on with typography.fluid. Each size can then set its own bounds, or opt out of fluidity.

Without further detail, WordPress calculates fluidity between screen widths of 320 and 1,600 pixels, or up to the theme’s wide width if one is defined, with a 14-pixel floor for small sizes.

For spacing, two approaches coexist. The scale, spacingScale, generates a series of values from an operation and a step. The list, spacingSizes, lets you name each value freely, and it is the only way to get fluid spacing. Twenty Twenty-Five, the default theme in WordPress 7.1, uses this second approach, with its larger spacing steps being fluid.

The customSpacingSize option, set to false, forces users to choose among the presets. Here again, the Theme Handbook contains a typo, “customSpacingSizes” in the plural, which the schema rejects.

Stacked glass sheets where light changes color at each layer

From version 2 to version 3: the two settings that changed

Version 3 changes only two behaviors, tied to core presets. The defaultFontSizes and defaultSpacingSizes options now default to true, so core font sizes and spacing appear in the editor, and a theme can no longer reuse their names, such as “small” or “large”, without turning those options off.

The official migration recipe has two steps: set the version to 3, then configure those two options. If the theme defines its own sizes, set defaultFontSizes to false. The old trick that hid core’s spacing scale with zero steps is replaced by defaultSpacingSizes set to false.

Nothing forces you to migrate: older versions remain supported. Version 3 simply requires WordPress 6.6 at minimum.

Style variations, palettes and section styles

Style variations are JSON files placed in the /styles folder. They can contain any theme.json key and appear in the Site Editor’s styles. Since WordPress 6.6, a file that contains only colors becomes a palette offered on its own, and a file that contains only typography becomes a typography set.

WordPress 6.6 also introduced section styles: a file that declares a list of block types, with the blockTypes key, becomes a style variation applicable to those blocks, for example a group on a colored background. Twenty Twenty-Five offers several.

Here is a minimal dark variation, placed in /styles/dark.json. Since it contains only colors, WordPress offers it as a palette.

{
	"$schema": "https://schemas.wp.org/wp/7.1/theme.json",
	"version": 3,
	"title": "Dark",
	"settings": {
		"color": {
			"palette": [
				{ "slug": "base", "color": "#0a0a0f", "name": "Base" },
				{ "slug": "contrast", "color": "#f5f5f7", "name": "Contrast" },
				{ "slug": "accent", "color": "#764ba2", "name": "Accent" }
			]
		}
	}
}

The color slugs match those of the main palette: that is what lets the variation change the look of the whole site without touching content. One detail matters for maintenance: when a user picks a variation, its data is copied into the database as a customization. A later update to the variation in the theme therefore does not reach the site until the user selects it again.

What WordPress 7.0 and 7.1 added

WordPress 7.0 made it possible to define states such as hover for the Button block directly in theme.json, and added width and height dimensions and the navigation overlay. Above all, it made unsynced patterns and template parts content-only by default, which hides their design tools.

WordPress 7.1 brings responsive styles: @mobile and @tablet keys in styles, and configurable breakpoints in settings.viewport, 480 and 782 pixels by default. There is no desktop key.

The same release gives states such as hover an interface, for the Button and Custom Link blocks. It also adds text shadow in theme.json, with no interface or presets for now, and background gradients. Our overview of what changed in WordPress 7 puts these additions in the context of both releases.

{
	"$schema": "https://schemas.wp.org/wp/7.1/theme.json",
	"version": 3,
	"settings": { "viewport": { "mobile": "30rem", "tablet": "45rem" } },
	"styles": {
		"blocks": {
			"core/group": {
				"spacing": { "padding": { "top": "3rem", "bottom": "3rem" } },
				"@mobile": { "spacing": { "padding": { "top": "1rem", "bottom": "1rem" } } }
			}
		}
	}
}

The real case: the summer WordPress underlined every link

The story of WordPress 6.6, released on July 16, 2024, shows better than any example that a change in global styles affects every site, classic themes included. It is fully documented in the developer notes, the official announcements and the public Gutenberg issues.

Specificity, leveled

To make core styles easier for theme authors to override, and to prepare section styles, editor contributors decided to make the CSS specificity of global styles and block styles uniform. The June 21, 2024 developer note announced it: existing selectors would be wrapped in :root :where(...), and theme authors were asked to double-check their designs if they relied on CSS with complex selectors.

Six days before release

On July 10, 2024, a developer, Ciprian Popescu, opened an issue: the links in his navigation, hand-coded in his own theme, were suddenly underlined. The default link style from core’s theme.json, which every theme inherits, now outranked his CSS rule. He added that the four hundred sites he manages were in danger of breaking when WordPress 6.6 came out.

A fix was merged into Gutenberg on July 12, with no guarantee of making it into 6.6.0. The release shipped on July 16 with the problem. The next day, Divi users reported in the same issue the same underlining on all their clients’ sites. The workarounds offered went as far as rewriting the global styles CSS on the fly.

Two point releases

WordPress 6.6.1 shipped on July 23, with the underlined-links fix at the top of the list. Meanwhile, another developer had reported that his Underscores-based child theme had lost its font, font size and line height because of the same mechanism applied to the body selector. That problem was only fixed in core with WordPress 6.6.2.

Other regressions followed, on buttons and layouts. The 6.6.2 release candidate, on September 4, acknowledged that several fixes were about CSS specificity, which had left sites not looking as intended, and invited sites that had reverted to 6.5 to test. WordPress 6.6.2 shipped on September 10, nearly two months after the initial release.

The 2026 epilogue

In July 2026, during WordPress 7.1 testing, a tester whose theme limited text palettes found that a color set for desktop beat the mobile color. Block-level preset classes were then wrapped in :where() in turn, before release, and the change was documented with its before and after selectors.

What to take away

Core’s theme.json applies to every site: a global styles change is a risk for classic themes too. The schema version only protects what it versions: version 3 governed sizes and spacing, but the specificity change hit version 2 and version 3 themes alike.

Test major releases on a copy of the site during the release candidate phase: the problem had been reported before release. Put the design in theme.json presets and styles rather than in element CSS selectors, the most fragile layer. And report regressions: every fix in 6.6.1 and 6.6.2 started from a public issue.

Locking the editor for a client

Start with theme.json

The simplest way to limit a client’s choices goes through theme.json, where custom colors, gradients, free font sizes and drop caps can be turned off, and a palette can be imposed on a specific block.

{
	"$schema": "https://schemas.wp.org/wp/7.1/theme.json",
	"version": 3,
	"settings": {
		"color": { "custom": false, "customGradient": false, "defaultPalette": false },
		"typography": { "customFontSize": false, "dropCap": false },
		"spacing": { "customSpacingSize": false }
	}
}

Allowed blocks and locking

The allowed_block_types_all filter restricts the available blocks, the canLockBlocks setting reserves locking for accounts that manage the design, and two lines remove core patterns and those from the online directory.

add_filter( 'allowed_block_types_all', function ( $allowed, $editor_context ) {
	if ( ! empty( $editor_context->post ) ) {
		return array( 'core/paragraph', 'core/heading', 'core/image', 'core/list', 'core/list-item' );
	}
	return $allowed;
}, 10, 2 );

add_filter( 'block_editor_settings_all', function ( $settings ) {
	$settings['canLockBlocks'] = current_user_can( 'edit_theme_options' );
	return $settings;
} );

add_action( 'after_setup_theme', function () {
	remove_theme_support( 'core-block-patterns' );
} );
add_filter( 'should_load_remote_block_patterns', '__return_false' );

At the template or group level, contentOnly locking hides the design tools and only lets users edit content, and the lock attribute prevents moving or removing a block. On a group, this mode simplifies the interface without closing everything: a toolbar button, “Edit pattern” in WordPress 7.1, reopens the design tools, and the documentation states that it cannot be removed with code.

By default, any user who can edit can also unlock: hence the value of canLockBlocks. To go further with extra blocks, our selection of Gutenberg block plugins helps you choose without overloading the editor.

Different settings by role

Since WordPress 6.1, four filters let you change theme.json data on the fly, by origin: core, blocks, theme or user. You can thus offer a full palette to administrators and a reduced one to writers, by checking the account’s capabilities in the theme filter. The documentation points out that injected data must carry a theme.json version number.

When content-only gets in the way

In June 2026, Oliver Juhas, author of the Zooey block theme and a newcomer from classic themes, found that with WordPress 7.0 the Template Part block no longer displayed the minimum height, sticky position and margin controls his theme added to it, which worked perfectly in 6.9. The cause: the new content-only default. A maintainer acknowledged an unfortunate side effect, and the fix went into WordPress 7.0.1.

WordPress 7.1 then added a documented setting, disableContentOnlyForTemplateParts, alongside disableContentOnlyForUnsyncedPatterns introduced in 7.0. If your theme extends core blocks, test it during release candidates.

Migrating a classic theme, step by step

The Theme Handbook no longer offers a conversion page: its old address returns an error. Learn WordPress, however, publishes a lesson, “Converting a Classic theme to a Block theme”, and the path is completed with several other official resources.

  1. Add a theme.json to the classic theme, for the palette, typography and layout.
  2. Switch to block template parts, available in classic themes since WordPress 6.1 once the theme declares add_theme_support( 'block-template-parts' ), then called in PHP with block_template_part().
  3. Create block templates, then add templates/index.html, which turns the theme into a block theme.
  4. Import widget areas, a Template Part block feature since WordPress 6.2.
  5. Save to files the changes made in the Site Editor, with Create Block Theme, whose latest release dates from June 2026.

On the admin side, the change is visible. With a block theme, the Customizer disappears, widgets give way to template parts, and the theme file editor moves under Tools. Warn the people who run the site day to day before the switch, not after.

Test each step on a copy of the site, with an up-to-date backup. Our guide to migrating a WordPress site without downtime details how to set up a test copy and switch over.

Summary table

Concept Where it lives Since What to watch
Block theme templates/index.html WordPress 5.9 theme.json alone is not enough
theme.json version 3 Theme root WordPress 6.6 No version 4, Theme Handbook behind
Site Editor changes Database WordPress 5.9 Override theme files
Style variations /styles folder WordPress 6.0 Copied to the database once chosen
Section styles /styles with blockTypes WordPress 6.6 Limited to the declared blocks
Content-only by default Patterns, template parts WordPress 7.0 Documented opt-out settings
Responsive styles @mobile, @tablet, settings.viewport WordPress 7.1 Rejected by the 7.0 schema

Frequently asked questions

Is there a theme.json version 4?

No. WordPress 7.1 still uses version 3, introduced with WordPress 6.6. The development branch schema contains upcoming keys, but no new version.

Can a classic theme use theme.json?

Yes. The file works with both types of themes. It lets a classic theme define its palette, typography and layout in the block editor, without giving it access to the Site Editor’s templates or global styles.

Why don’t my Site Editor changes show up after a theme update?

It is the other way around: your changes, saved in the database, take precedence over the theme files. It is the theme’s new features that do not show up on the templates you modified. Resetting a template in the Site Editor brings it back to the theme’s version.

How do I stop a client from picking custom colors?

In theme.json, set color.custom and color.customGradient to false, and replace the core palette with your own. The client can then only choose among your colors.

Do I need the Create Block Theme plugin?

Not to use a block theme. It becomes useful to create a child theme or a style variation, or to save Site Editor changes back into files. Its latest release dates from June 2026, and its directory page only lists testing up to WordPress 6.9, so try it on a copy of the site first.

Conclusion

Full Site Editing rests on a simple idea: one file, theme.json, describes the design, and the Site Editor lets users change it without touching code. In practice, you need to know the current version, be wary of outdated documentation pages, and understand that user changes live in the database.

The summer of 2024 was a reminder that this file is not just for block themes. Whether your theme is block, hybrid or classic, test every major WordPress release on a copy, and put your design in theme.json presets rather than in fragile CSS overrides.

Sources


LaFactory designs, builds and maintains WordPress and WooCommerce sites, and develops its own plugins. Talk to us about your WordPress project.

Francis Rozange

Former section editor at Libération, he runs LaFactory, an international web agency since 1996.

Cart