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.

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.
- Add a
theme.jsonto the classic theme, for the palette, typography and layout. - 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 withblock_template_part(). - Create block templates, then add
templates/index.html, which turns the theme into a block theme. - Import widget areas, a Template Part block feature since WordPress 6.2.
- 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
- WordPress Developer Resources. What Is a Theme?
- WordPress Developer Resources. Theme Structure
- WordPress Developer Resources. Template Hierarchy
- WordPress Developer Resources. Template Parts
- WordPress Developer Resources (June 2026). Introduction to theme.json
- Block Editor Handbook (September 2026). Theme.json Version 3 Reference
- Block Editor Handbook. Migrating Theme.json to Newer Versions
- WordPress Developer Resources. Typography
- WordPress Developer Resources. Spacing
- WordPress Developer Resources. Style Variations
- WordPress.org. theme.json schema for WordPress 7.1
- Make WordPress Core (June 19, 2024). Theme.json version 3
- Make WordPress Core (June 21, 2024). WordPress 6.6 CSS Specificity
- Make WordPress Core (June 24, 2024). Section Styles
- GitHub, WordPress/gutenberg (July 10, 2024). Issue 63345, Rogues styles messing with my navigation styles
- GitHub, WordPress/gutenberg (July 18, 2024). Issue 63712
- Make WordPress Core (July 18, 2024). WordPress 6.6.1 RC1 is now available
- WordPress.org News (July 23, 2024). WordPress 6.6.1 Maintenance Release
- Make WordPress Core (September 4, 2024). WordPress 6.6.2 RC1 is now available
- WordPress.org News (September 10, 2024). WordPress 6.6.2 Maintenance Release
- Make WordPress Core (August 4, 2026). Miscellaneous Editor Changes in WordPress 7.1
- Make WordPress Core (August 5, 2026). Responsive block styles and configurable viewports in WordPress 7.1
- Make WordPress Core (March 15, 2026). Pattern Editing in WordPress 7.0
- GitHub, WordPress/gutenberg (June 12, 2026). Issue 79152, Template Part block controls since WP 7.0
- Block Editor Handbook. Block Locking API
- Block Editor Handbook. Block Filters
- WordPress Developer Blog, Troy Chaplin (December 3, 2024). Bridging the gap: Hybrid themes
- Make WordPress Core (October 4, 2022). Block-based template parts in traditional themes
- Learn WordPress. How to switch from a classic to a block theme
- WordPress.org Plugins. Create Block Theme
- WordPress.org Themes. Twenty Twenty-Five
- Learn WordPress (August 1, 2024). Converting a Classic theme to a Block theme
- Learn WordPress. Importing widget areas from a classic theme to a block theme
- WordPress Developer Blog, Nick Diego (January 29, 2024). How to disable specific blocks in WordPress
- Block Editor Handbook. Filters and hooks
- Block Editor Handbook. Disable Editor functionality
- WordPress Developer Resources. Blocks
- Make WordPress Themes (June 21, 2024). Theme.json version 3 frequently asked questions
- WordPress Developer Blog, Justin Tadlock (July 22, 2024). Mixing and matching styles, colors, and typography in WordPress 6.6
- Make WordPress Core (October 17, 2023). Miscellaneous Editor changes in WordPress 6.4
- WordPress.org News (July 16, 2024). WordPress 6.6 Dorsey
- WordPress.org News (April 15, 2025). WordPress 6.8 Cecil
- Make WordPress Core (May 14, 2026). WordPress 7.0 Field Guide
- Make WordPress Core (August 5, 2026). Pseudo and custom style states in WordPress 7.1
- Make WordPress Core (July 23, 2026). Text Shadow Support in Global Styles
- Make WordPress Core (July 26, 2026). New Block Support in WordPress 7.1, Background Gradient
- Make WordPress Core (July 1, 2026). WordPress 7.0.1 RC1 is now available
- GitHub, WordPress/gutenberg (July 22, 2026). Issue 80580, 7.1 Bug when using JSON filter for text colors in Responsive style states for blocks
LaFactory designs, builds and maintains WordPress and WooCommerce sites, and develops its own plugins. Talk to us about your WordPress project.
