Site Editor and Global Styles
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages. |
The Site Editor (Appearance > Editor) is where the owner of a site that runs a block theme designs the whole site with blocks: the header and footer, the layout of every kind of page, the menus, and the site-wide look (colours, fonts, spacing) that the Styles panel controls. It replaces the Customizer, the Menus screen and the Widgets screen of classic themes. This page describes it from the site owner’s side: what each screen does, what changed in recent releases, and, because it explains most surprises, where every change is stored and how it is reset or carried to another site.
This page was written against WordPress 7.1.3 (released 2026-10-06; 7.1.4, a security release of 2026-10-28, is the version to run in production), PHP 7.4 minimum and 8.3+ recommended, theme.json version 3, the Twenty Twenty-Five theme 1.5 bundled with 7.1, and WP-CLI 2.12.0, and checked on 2026-10-28. The Gutenberg plugin (24.2.0) previews editor changes before they reach core; nothing on this page requires it. WordPress 7.2 is in beta and planned for 2026-12-08; it does not change the storage model described here so far. Every stored value and result below comes from a fresh 7.1.3 installation with Twenty Twenty-Five active, edited through the same REST endpoints the Site Editor calls.
Writing a theme’s theme.json is a theme developer’s task, covered in
theme.json Settings and
theme.json Styles; template files are covered in
Templates and Template Parts. This page only shows
the JSON that the Site Editor writes on the owner’s behalf.
Who can use it and when it appears
The Site Editor needs a block theme: a theme with a templates/index.html file. Block themes and the Site Editor
arrived in WordPress 5.9 (January 2022); older books that place "full site editing" in 5.4 to 5.6 confuse it with the
Gutenberg plugin’s experiments. The Appearance menu changes with the active theme:
| Active theme | Appearance menu |
|---|---|
Block theme |
Themes, Editor ( |
Classic theme that declares |
Themes, Design (the Style Book and the Patterns screen), Fonts, Customize, Menus and Widgets when supported |
Other classic theme |
Themes, Patterns, Fonts, Customize, Menus and Widgets when supported |
All of these entries require the edit_theme_options capability, which only Administrators have by default (and on
Multisite, site Administrators too). Editing CSS additionally requires edit_css, which maps to unfiltered_html: it
is denied to everyone when DISALLOW_UNFILTERED_HTML is true, and to everyone but Super Admins on Multisite. Users
without it do not see the Additional CSS editors at all.
The editor itself is a single-page application over the REST API; every screen below saves through a /wp/v2/
endpoint, which is why the same changes can be scripted, as the examples on this page do.
The Site Editor screens
Opening Appearance > Editor shows a navigation sidebar and a preview of the home page (the page chosen in Settings > Reading). In 7.1 the sidebar lists six sections, and the WordPress admin bar now stays visible on every Site Editor screen (hidden only in Distraction Free mode).
| Section | What the site owner does there | Since |
|---|---|---|
Identity |
Site title, tagline, logo and icon in one panel, without editing blocks. They are the ordinary |
documented with 7.1 |
Styles |
Site-wide colours, typography, layout, background, shadows, block defaults, style variations, revisions, Additional CSS (all described below) |
5.9 |
Pages |
List, create and edit pages inside the Site Editor, with their template shown around the content |
6.3 |
Navigation |
List all menus, rename, duplicate or delete them, reorder items, open a menu in focus mode |
6.3 |
Patterns |
Synced and unsynced patterns, and the template parts (header, footer, general, navigation overlay) |
6.3 (template parts moved here from their own entry) |
Templates |
The theme’s templates and any custom ones; add a template for a page type, a post type or a single page |
5.9 |
The top bar of an open template or page has the block inserter, undo and redo, List View, the Command Palette (Ctrl+K or Cmd+K), Preview (device sizes, and in 7.1 the switch to edit responsive styles), the Settings and Styles toggles, Save, and the More options menu (top toolbar, spotlight mode, distraction free, visual and code editor, Tools > Export, keyboard shortcuts, Preferences). Save opens a list of every entity that changed (styles, templates, template parts, navigation menus, synced patterns), and each can be saved or left unsaved separately.
Zoom out (the toggle next to the Settings button, or Ctrl+Shift+0 / Shift+Cmd+0) shrinks the canvas to show whole sections. The editor switches into it when a pattern is inserted into a template, which makes it the natural view for rearranging a home page section by section. In the post editor it is available when "Show template" is on.
Two block-level helpers save time: Copy styles / Paste styles in a block’s options menu (6.2) copies one block’s design to another of the same type, and Advanced > Apply globally (6.2) turns one block’s local design into the site-wide default for its block type, that is, into Global Styles.
Styles
Styles (the half-filled circle icon, or the Styles section of the sidebar) sets the site-wide design. Every change is
a value in the user layer of global styles, which is merged on top of WordPress’s defaults and the theme’s
theme.json. Its panels are:
| Panel | Settings |
|---|---|
Typography |
Font family, size, appearance (weight and style), line height, letter spacing, decoration, case and orientation for Text, Links, Headings (all or H1 to H6), Captions and Buttons; the font size presets (S to XXL) can be edited, including fluid minimum and maximum values; Fonts opens the Font Library |
Colors |
The palette (theme colours, default colours if the theme allows them, and custom colours and gradients the owner adds), plus the colours of Background, Text, Links (with hover), Headings and Buttons; since 6.7 a set of preset palettes to start from |
Layout |
Content width and wide width, root padding (linked or per side, as values or spacing presets) and block spacing |
Background |
A site background image with its size, position and repeat |
Shadows |
The theme’s shadow presets (Natural, Deep, Sharp, Outlined, Crisp in Twenty Twenty-Five) and custom shadows applied to buttons and other blocks |
Blocks |
Defaults for every block type: colours, typography, spacing, border, radius, duotone, shadow, its registered block style variations, and Additional block CSS |
What the theme does not allow does not appear: a theme can switch off custom colours, custom font sizes or the
default palette in theme.json (settings.color.custom, settings.typography.customFontSize,
settings.color.defaultPalette), and those controls disappear from Styles.
A block that has its own colour or size set in the block sidebar keeps it; Styles changes only blocks that have no local value. This is the most common "Styles does not work" report, and Apply globally or clearing the block’s local value is the fix.
Style variations and presets
A style variation is a JSON file in the theme’s styles/ folder that replaces the theme’s whole look. Styles >
Browse styles lists them (since 6.0). Since 6.6 themes can also ship colour-only variations (styles/colors/*.json)
and typography-only variations (styles/typography/*.json, typesets), which the Colors and Typography panels list
as palettes and font pairings, so a site owner can combine one theme variation’s colours with another’s fonts.
Twenty Twenty-Five ships eight full variations (Evening, Noon, Dusk, Afternoon, Twilight, Morning, Sunrise, Midnight), eight matching colour palettes, seven typesets (for example "Literata & Ysabeau Office"), five section styles and four block styles. The list comes from the theme’s variations endpoint:
<?php
// wp eval-file list-variations.php --user=admin
$response = rest_do_request( new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/twentytwentyfive/variations' ) );
foreach ( $response->get_data() as $variation ) {
WP_CLI::log( $variation['title'] );
}
Source: REST API Handbook: Global Styles
Choosing a variation copies its values into the user layer; WordPress does not store "use variation Noon". Two
consequences follow. Further changes are made on top of the copy, and a theme update that changes styles/02-noon.json
does not reach a site that applied Noon before the update until the owner applies the variation again (or resets).
Building variations and section styles is covered in
Style Variations and Section Styles.
The Style Book
The Style Book (the eye icon in Styles, since 6.2) replaces the canvas with a catalogue of every block, grouped by
category, rendered with the current styles, plus a Colors and Typography overview. It is the place to check what a
change does to blocks that are not on the page being edited. Since 6.8 classic themes that declare editor-styles or
ship a theme.json reach it through Appearance > Design, so a hybrid theme can be reviewed the same way.
Responsive and state styles (7.1)
Before 7.1 a site-wide style applied at every screen width; changing the site title size on phones required custom CSS. WordPress 7.1 adds viewport states:
-
In Styles > Blocks > a block, a States dropdown with a Viewport group (Default, Tablet, Mobile). Values set for Tablet or Mobile override the default inside that range only.
-
On a single block in the editor, the same states are stored with that block instance. The View menu has a Responsive styles toggle to show or hide this editing.
-
The default breakpoints are
@mobileup to 480px and@tabletfrom 481px to 782px; there is no desktop state, the default value covers everything wider. A theme can move the breakpoints withsettings.viewport(px,emorremonly), which applies to the whole site, not per block.
7.1 also adds pseudo states (:hover, :focus, :focus-visible, :active) to the same States dropdown, for the
Button and Navigation Link blocks, and a theme-only -current state for the menu item of the current page. Both
features can be hidden from editors without removing saved styles, with the responsiveEditingEnabled and
blockStatesEditingEnabled editor settings. A site owner who wants a simpler editor for Lumen Press’s Editors can
hide the viewport controls with a must-use plugin:
<?php
/**
* Plugin Name: Lumen Site Editor guardrails
* Description: Hides the responsive (Tablet/Mobile) style controls in the editors.
*/
add_filter(
'block_editor_settings_all',
static function ( array $settings ): array {
$settings['responsiveEditingEnabled'] = false;
return $settings;
}
);
Saved as wp-content/mu-plugins/lumen-site-editor.php, it sets the setting to false for every editor; responsive
values already saved keep rendering. Theme-side syntax (@mobile, @tablet and :hover keys in theme.json) is on
theme.json Styles.
Custom CSS at three levels
| Level | Where | Stored in | Since |
|---|---|---|---|
Site-wide |
Styles > More (the ellipsis in the Styles header since 6.9) > Additional CSS |
|
6.2 |
Block type |
Styles > Blocks > a block > Additional block CSS; declarations only, |
|
6.2 |
One block instance |
Block sidebar > Advanced > Custom CSS; declarations only |
the block’s |
7.0 |
The instance level is new in 7.0. WordPress adds a generated wp-custom-css-* class to that block and prints the
rule in a stylesheet that loads after global styles, so it wins over the block-type default. It is skipped for blocks
that opt out with "customCSS": false (Classic, Custom HTML, More, Synced pattern), and CSS that contains anything
looking like an HTML tag is dropped, the same validation the global styles endpoint applies. Without edit_css, the
field is hidden and the attribute is stripped when the content is saved.
On a block theme the Customizer’s old Additional CSS (a custom_css post per theme) is still printed, merged into
the global styles stylesheet just before the Styles Additional CSS, so CSS added before a switch to a block theme keeps
working. The Customizer itself is reachable only by URL (customize.php) once a block theme is active, so moving that
CSS into Styles > Additional CSS is the cleaner path. Styles CSS survives theme updates but belongs to one theme: it
is not applied after switching to another theme, and comes back when switching back.
Custom CSS should be the last resort. Every value that Styles can express (a colour, a size, a breakpoint since 7.1) is better set there, because it lands in the preset system, the Style Book shows it, and theme updates and variations interact with it predictably.
Revisions, reset and per-panel reset
Each save of Styles creates a revision of the user global styles (since 6.3). Styles > More > Revisions shows a
timeline; selecting one previews it and Apply restores it as a new save. Reset to defaults (same menu) empties
the user layer, so the theme’s theme.json and active variation values take over again. Most panels also have their
own reset (Reset colors, Reset gradients, the reset of a Layout value), which removes only that key.
Templates and template parts have their own history: they are posts with revisions. A customized template shows Reset (or "Clear customizations") in its Actions menu, which deletes the stored copy so the theme file is used again.
The Fonts screen and the Font Library
The Font Library (6.5) manages font families separately from the theme. Since 7.0 it has its own admin page,
Appearance > Fonts (font-library.php), available with every theme, classic ones included; in a block theme it is
also reachable from Styles > Typography. It has three tabs:
-
Library: the fonts the theme provides (read-only) and the fonts installed by users, which can be activated, deactivated per variant or deleted.
-
Upload: install
.woff2,.woff,.ttfor.otffiles. -
Install fonts: the Google Fonts collection (
google-fonts, the only collection registered by default). After the site owner gives permission, WordPress downloads the chosen files once and serves them locally; the visitor’s browser never contacts Google, which is the privacy-friendly way to use those fonts.
Installed fonts are wp_font_family posts (the CSS family name and settings in JSON) with one wp_font_face child post
per style and weight, and the files go to wp-content/uploads/fonts (filterable with font_dir). They do not depend
on the theme and are not part of a theme export. A font that is installed but not activated in Styles > Typography
does not load on the front end.
Templates and template parts
Templates lists every template the theme provides (Twenty Twenty-Five ships index, home, single, page,
archive, search, 404 and page-no-title) and those added by the owner. Add new template offers the missing
ones of the template hierarchy (Front Page, a single post type such as "Single item: Book" for the Lumen Press lp_book
type, an archive for a taxonomy, an author, a date archive, or one specific page or post) and custom templates
that a page can select in its Template panel. How WordPress picks a template is explained in
Template Hierarchy.
Template parts (under Patterns) are the shared areas, assigned to an area: Header, Footer, General
(uncategorized), and Navigation Overlay (the block-based mobile menu overlay of 7.0). Editing the Header changes it
everywhere it is used; the Save list warns about it.
The list views show where a template or part comes from: from the theme, from a plugin, or customized by the
owner. A customized item is a copy in the database and from then on wins over the file. That is the expected behaviour
and the source of the most frequent support question: "I updated the theme, but my header did not change." The theme
update changed parts/header.html; the site still uses the customized copy until it is reset.
Each template is the serialized block markup that Templates and Template Parts describes; the Code editor (More options > Code editor) shows it.
Navigation
A block theme’s menus are Navigation blocks, and a menu’s items live in a wp_navigation post that the block
references by ID ({"ref":9}). Appearance > Editor > Navigation (6.3) lists those posts. Creating a menu there does
not place it anywhere; a Navigation block in a template part (normally the Header) must select it. Classic menus (nav_menu
terms) appear in the Navigation block’s menu selector and can be imported as a copy; the originals stay untouched.
The Navigation block also builds the mobile overlay (the "hamburger" menu); since 7.0 the overlay can be a block-based template part in the Navigation Overlay area, designed like any other part. Patterns, synced patterns and their overrides, which also appear in this sidebar, are covered in Patterns for Site Builders, and the developer side of menus in Navigation and Menus.
Where the edits are stored
Nothing the Site Editor saves goes into the theme folder. Every save is a post in wp_posts, tied to the active
theme by the wp_theme taxonomy (a term named after the theme’s directory), and resolved against the theme files at
request time:
| Edit | Post type and identity | Notes |
|---|---|---|
Styles, Additional CSS, block-type CSS |
|
JSON with |
Customized template |
|
Wins over |
Customized or new template part |
|
Also has a |
Menu |
|
Not tied to a theme: survives a theme switch; referenced by post ID |
Installed font |
|
Not tied to a theme; files under |
Identity |
options |
The same options Settings > General and the Customizer use |
Instance custom CSS, responsive and state values of one block |
inside the block’s attributes in the post, template or part |
Travels with that content |
Customizer Additional CSS |
|
Legacy; still printed for block themes |
The precedence for styles is core defaults, then the parent theme, then the child theme, then the user layer; a
child theme is merged into its parent before the user values. Since 6.6 global styles are printed with selectors such
as :root :where(.wp-block-site-title) capped at 0-1-0 specificity, so a theme’s or plugin’s ordinary class rules
can still override them.
Seeing it on a real site
The Compose file below starts MariaDB and a WP-CLI container, installs WordPress 7.1.3 (Twenty Twenty-Five is the
default theme) and mounts the scripts of this section. It needs no web server: the scripts call the REST API in
process with rest_do_request(), as the Site Editor does over HTTP. The passwords are placeholders for a local
machine.
# compose.yaml
name: lumen-site-editor
services:
db:
image: mariadb:11.8
environment:
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: ${WP_DB_PASSWORD:?set WP_DB_PASSWORD}
MARIADB_RANDOM_ROOT_PASSWORD: "1"
tmpfs: [/var/lib/mysql]
cli:
image: wordpress:cli-2.12.0-php8.4
depends_on: [db]
user: "33:33"
environment:
WP_DB_PASSWORD: ${WP_DB_PASSWORD}
WP_ADMIN_PASSWORD: ${WP_ADMIN_PASSWORD:?set WP_ADMIN_PASSWORD}
volumes:
- wp:/var/www/html
- ./scripts:/scripts:ro
entrypoint: ["sleep", "infinity"]
volumes:
wp: {}
export WP_DB_PASSWORD='<a-local-db-password>' WP_ADMIN_PASSWORD='<a-local-admin-password>'
docker compose up -d
docker compose exec cli sh -c 'php -d memory_limit=512M /usr/local/bin/wp core download --version=7.1.3 &&
wp config create --dbname=wordpress --dbuser=wordpress --dbpass="$WP_DB_PASSWORD" --dbhost=db &&
wp core install --url=http://lumenpress.test --title="Lumen Press" --admin_user=admin \
--admin_email=admin@lumenpress.test --admin_password="$WP_ADMIN_PASSWORD" --skip-email'
docker compose exec cli wp post list --post_type=wp_global_styles --post_status=any --fields=ID,post_name
Source: WP-CLI: wp post list
On a fresh site the last command prints only the header line: the user global styles post does not exist until something asks for it (opening Styles, or the first save).
Saving styles
scripts/save-styles.php sends the request the Styles panel sends on Save: a custom palette colour "Lumen ink", a
background from the theme palette, the theme’s Fira Code family as body font, the link colour, a larger site title
with a smaller value for phones, and a site-wide CSS rule for ISBNs.
<?php
/**
* Saves user-layer global styles for the active theme through the REST API,
* the same request the Styles panel sends when the site owner clicks Save.
* Run with: wp eval-file /scripts/save-styles.php --user=admin
*/
$id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
$request = new WP_REST_Request( 'POST', "/wp/v2/global-styles/$id" );
$request->set_body_params(
array(
'settings' => array(
'color' => array(
'palette' => array(
'custom' => array(
array( 'slug' => 'lumen-ink', 'name' => 'Lumen ink', 'color' => '#1d2a44' ),
),
),
),
),
'styles' => array(
'color' => array( 'background' => 'var:preset|color|accent-5' ),
'typography' => array( 'fontFamily' => 'var:preset|font-family|fira-code' ),
'elements' => array(
'link' => array( 'color' => array( 'text' => 'var:preset|color|lumen-ink' ) ),
),
'blocks' => array(
'core/site-title' => array(
'typography' => array( 'fontSize' => '2.5rem' ),
'@mobile' => array( 'typography' => array( 'fontSize' => '1.5rem' ) ),
),
),
'css' => '.lp-book-isbn { font-variant-numeric: tabular-nums; }',
),
)
);
$response = rest_do_request( $request );
WP_CLI::log( "HTTP {$response->get_status()}, wp_global_styles post $id" );
Source: REST API Handbook: Global Styles
The --user=admin flag matters. Without a user, get_user_global_styles_post_id() still creates the post, but
wp_insert_post() silently drops its wp_theme term (assigning terms needs a capability), the lookup by term then
fails, and every later call creates another orphan "Custom Styles" post (wp-global-styles-twentytwentyfive-2, and so
on). Scripts that touch global styles must run as an administrator.
docker compose exec cli wp eval-file /scripts/save-styles.php --user=admin
docker compose exec cli wp post list --post_type=wp_global_styles --post_status=any --fields=ID,post_name,post_title
docker compose exec cli wp post get 4 --field=post_content
docker compose exec cli wp post term list 4 wp_theme --fields=slug
Source: WP-CLI: wp post get
HTTP 200, wp_global_styles post 4
ID post_name post_title
4 wp-global-styles-twentytwentyfive Custom Styles
{"styles":{"color":{"background":"var:preset|color|accent-5"},"typography":{"fontFamily":"var:preset|font-family|fira-code"},"elements":{"link":{"color":{"text":"var:preset|color|lumen-ink"}}},"blocks":{"core/site-title":{"typography":{"fontSize":"2.5rem"},"@mobile":{"typography":{"fontSize":"1.5rem"}}}},"css":".lp-book-isbn { font-variant-numeric: tabular-nums; }"},"settings":{"color":{"palette":{"custom":[{"slug":"lumen-ink","name":"Lumen ink","color":"#1d2a44"}]}}},"isGlobalStylesUserThemeJSON":true,"version":3}
slug
twentytwentyfive
Source: WP-CLI: wp post term list
The stored document is a theme.json fragment: only the keys the owner changed, with preset references
(var:preset|color|accent-5) rather than hex values, so a palette change later propagates. The custom colour is
under settings.color.palette.custom, separate from the theme’s palette. The generated stylesheet shows the merge,
including the 7.1 media query for the @mobile value and the Additional CSS that is printed last:
docker compose exec cli sh -c 'wp eval "echo wp_get_global_stylesheet();" | grep -o "@media (width <= 480px){[^}]*}"'
docker compose exec cli wp eval 'echo wp_get_global_stylesheet( array( "custom-css" ) );'
@media (width <= 480px){:root :where(.wp-block-site-title){font-size: clamp(0.984rem, 0.984rem + ((1vw - 0.2rem) * 0.809), 1.5rem);}
.lp-book-isbn { font-variant-numeric: tabular-nums; }
The 1.5rem became a clamp() because Twenty Twenty-Five enables fluid typography: fixed sizes set in Styles are
made fluid too, with the entered value as the maximum.
Saving a menu, a template part and a template
scripts/site-edits.php reproduces three Site Editor saves: a new "Lumen header" menu, the Header part pointed at it,
and a customized home template.
<?php
/**
* Creates a navigation menu, points the header template part at it and
* customizes the home template, as saving in the Site Editor does.
* Run with: wp eval-file /scripts/site-edits.php --user=admin
*/
$theme = get_stylesheet();
$request = new WP_REST_Request( 'POST', '/wp/v2/navigation' );
$request->set_body_params(
array(
'title' => 'Lumen header',
'status' => 'publish',
'content' => '<!-- wp:navigation-link {"label":"Catalogue","url":"/books/","kind":"custom"} /-->'
. '<!-- wp:navigation-link {"label":"Submissions","url":"/submissions/","kind":"custom"} /-->',
)
);
$nav_id = rest_do_request( $request )->get_data()['id'];
WP_CLI::log( "wp_navigation post $nav_id" );
$header = rest_do_request( new WP_REST_Request( 'GET', "/wp/v2/template-parts/$theme//header" ) )->get_data();
$content = preg_replace_callback(
'/<!-- wp:navigation (\{.*?\} )?\/-->/s',
static function ( array $match ) use ( $nav_id ): string {
$attrs = ! empty( $match[1] ) ? json_decode( $match[1], true ) : array();
$attrs['ref'] = $nav_id;
return '<!-- wp:navigation ' . wp_json_encode( $attrs, JSON_UNESCAPED_SLASHES ) . ' /-->';
},
$header['content']['raw'],
1
);
$request = new WP_REST_Request( 'POST', "/wp/v2/template-parts/$theme//header" );
$request->set_body_params( array( 'content' => $content ) );
$saved = rest_do_request( $request )->get_data();
WP_CLI::log( "header: source {$header['source']} -> {$saved['source']}, wp_template_part post {$saved['wp_id']}" );
$request = new WP_REST_Request( 'POST', "/wp/v2/templates/$theme//home" );
$request->set_body_params(
array(
'content' => '<!-- wp:template-part {"slug":"header"} /-->'
. '<!-- wp:heading {"level":1} --><h1 class="wp-block-heading">New from Lumen Press</h1><!-- /wp:heading -->'
. '<!-- wp:template-part {"slug":"footer"} /-->',
)
);
$saved = rest_do_request( $request )->get_data();
WP_CLI::log( "home: source {$saved['source']}, wp_template post {$saved['wp_id']}" );
Template and part IDs in the REST API are <theme>//<slug>; the first save of a theme item turns its source from
theme into custom and creates the post.
docker compose exec cli wp eval-file /scripts/site-edits.php --user=admin
docker compose exec cli wp post list --post_type=wp_global_styles,wp_template,wp_template_part,wp_navigation \
--post_status=any --fields=ID,post_type,post_name,post_title
docker compose exec cli wp post term list 10 wp_theme wp_template_part_area --fields=taxonomy,slug
docker compose exec cli sh -c 'wp post get 10 --field=post_content | grep -o "<!-- wp:navigation [^>]*>"'
Source: WP-CLI: wp post list
wp_navigation post 9
header: source theme -> custom, wp_template_part post 10
home: source custom, wp_template post 11
ID post_type post_name post_title
9 wp_navigation lumen-header Lumen header
10 wp_template_part header Header
11 wp_template home Blog Home
4 wp_global_styles wp-global-styles-twentytwentyfive Custom Styles
taxonomy slug
wp_template_part_area header
wp_theme twentytwentyfive
<!-- wp:navigation {"overlayBackgroundColor":"base","overlayTextColor":"contrast","layout":{"type":"flex","justifyContent":"right","flexWrap":"wrap"},"ref":9} /-->
Source: REST API Handbook: Templates
The Header in the theme file is only a Pattern block (twentytwentyfive/header); the REST response and the saved copy
contain the pattern’s blocks expanded, so a customized part no longer follows later changes to that pattern either.
Revisions and resets
<?php
/**
* Lists the style revisions the Styles > Revisions panel shows.
* Run with: wp eval-file /scripts/list-revisions.php --user=admin
*/
$id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
$revisions = rest_do_request( new WP_REST_Request( 'GET', "/wp/v2/global-styles/$id/revisions" ) )->get_data();
foreach ( $revisions as $revision ) {
WP_CLI::log( sprintf( '%d %s %d top-level style keys', $revision['id'], $revision['date'], count( (array) ( $revision['styles'] ?? array() ) ) ) );
}
<?php
/**
* Empties the user layer, as Styles > Reset to defaults does.
* Run with: wp eval-file /scripts/reset-styles.php --user=admin
*/
$id = WP_Theme_JSON_Resolver::get_user_global_styles_post_id();
$request = new WP_REST_Request( 'POST', "/wp/v2/global-styles/$id" );
$request->set_body_params( array( 'settings' => new stdClass(), 'styles' => new stdClass() ) );
WP_CLI::log( 'HTTP ' . rest_do_request( $request )->get_status() );
Source: REST API Handbook: Global Styles
Resetting the home template means deleting its database copy, which is what the template’s Reset action does:
docker compose exec cli wp post delete 11 --force
docker compose exec cli wp eval 'echo get_block_template( get_stylesheet() . "//home" )->source, PHP_EOL;'
docker compose exec cli wp eval-file /scripts/reset-styles.php --user=admin
docker compose exec cli wp post get 4 --field=post_content
docker compose exec cli wp eval-file /scripts/list-revisions.php --user=admin
Success: Deleted post 11.
theme
HTTP 200
{"styles":[],"settings":[],"isGlobalStylesUserThemeJSON":true,"version":3}
12 2026-10-28T10:28:52 0 top-level style keys
5 2026-10-28T10:27:49 5 top-level style keys
Source: WP-CLI: wp post delete
A reset keeps the post and the history: the earlier state is still a revision, so a reset done by mistake is undone from Styles > Revisions. Deleting a template’s copy, in contrast, is final unless a backup exists.
Exporting and moving the design
Tools > Export in the Site Editor’s More options menu downloads a zip of the active theme with the owner’s
changes written into the files: user global styles merged into theme.json, customized templates and parts in
templates/ and parts/. The same zip is produced by wp_generate_block_templates_export_file(), which makes it easy
to check:
docker compose exec cli sh -c 'zip=$(wp eval "echo wp_generate_block_templates_export_file();" --user=admin)
unzip -l "$zip" | grep -E " (theme.json|templates/home.html|parts/header.html)$"
unzip -p "$zip" parts/header.html | grep -o "<!-- wp:navigation [^>]*>"
rm -f "$zip"'
1193 10-28-2026 10:28 parts/header.html
193 10-28-2026 10:28 templates/home.html
55175 10-28-2026 10:28 theme.json
<!-- wp:navigation {"overlayBackgroundColor":"base","overlayTextColor":"contrast","layout":{"type":"flex","justifyContent":"right","flexWrap":"wrap"},"ref":9} /-->
Source: REST API Handbook: Templates
The export has limits a site owner should know:
-
Menus are not exported. The header keeps
"ref":9, an ID that means nothing on another site; the Navigation block there falls back to another menu or a page list. Menus move with the content export (the dashboard’s Tools > Export includeswp_navigationposts), and the menu is then selected again in the Navigation block. -
Installed fonts are not exported, and neither are media referenced from styles (a background image URL keeps pointing at the old site’s uploads).
-
It is a copy of the theme with the same name and version. Installing it over the original on another site is fine; updating the original theme from WordPress.org later overwrites it. A design meant to last belongs in a child theme or the site’s own theme, which the Create Block Theme plugin produces from the Site Editor (save changes to the theme, create a child theme, or a new theme): see Create Block Theme and Version Control.
Once the changes are in the theme’s files, reset the database copies (styles and every customized template or part);
otherwise the database still wins and later file edits seem to have no effect. For Lumen Press the intended workflow
is: design in the Site Editor on staging, save to the lumen theme with Create Block Theme, commit, reset the
customizations, deploy the theme. Content moves are covered in
Importing, Exporting and Migrating.
What older books get wrong or leave out
| Older advice | Current state (7.1) |
|---|---|
Full site editing is "still maturing" or experimental, and design is best done in the Customizer with a commercial theme and a page builder (UWH) |
The Site Editor has been part of core since 5.9 (2022) and is the default design tool of every bundled theme since Twenty Twenty-Two. Responsive styles (7.1), per-instance CSS (7.0), the Fonts screen (7.0), colour and typography variations (6.6) and style revisions (6.3) fill the gaps that used to require a builder. Page builders remain an option, with the trade-offs in Page Builders and Commercial Themes. |
Site editing arrived in WordPress 5.4 to 5.6 (PRO) |
Block themes and the Site Editor arrived in 5.9; earlier versions only had Gutenberg plugin experiments. |
Customizer Additional CSS, Menus and Widgets as the way to adjust any theme (PRO, UWH) |
With a block theme they are replaced by Styles > Additional CSS, the Navigation screen and template parts; the Customizer is hidden, and its old CSS is still printed. |
"Reusable blocks" (UWH) |
Renamed synced patterns in 6.3, managed under Patterns. |
Site Editor walkthroughs written for 6.3 and 6.4 (AIO) |
The screens have moved since: template parts are listed under Patterns, Additional CSS is in the ellipsis menu of the Styles header (6.9), the Fonts screen is in Appearance (7.0), and 7.1 adds the Identity section, responsive styles and a persistent admin bar. |
Google Fonts loaded through a plugin or the theme from Google’s servers |
The Font Library downloads the files once and serves them locally, for every theme since 7.0. |
Updating the theme updates the site’s header and footer |
Only for parts and templates that were never customized; customized copies in the database win until reset. |
Common errors and pitfalls
| Symptom | Cause and fix |
|---|---|
Appearance > Editor is missing |
The active theme is classic (no |
A theme update changed nothing visible |
The header, footer or a template is customized in the database. Compare in the Templates or Patterns list (the "customized" source) and reset it, after exporting if the changes matter. |
Styles change some blocks but not others |
Those blocks have a local value set in the block sidebar. Clear it, or use Advanced > Apply globally. |
Additional CSS editors are missing |
|
Several "Custom Styles" posts for one theme, styles seem to reset at random |
A script or plugin created the user global styles post without a logged-in administrator, so the |
Styles vanish after switching themes |
Global styles, Additional CSS and customized templates belong to the theme that was active. They return when switching back; export or copy them before a permanent switch. |
The menu is empty after moving the site |
The Navigation block references a |
Mobile values do nothing |
The block is wider than the breakpoint, or the theme set |
A font chosen in Styles does not appear on the front end |
It is installed but not activated, or a block or the theme sets a different family more specifically. Check Appearance > Fonts and the block’s own typography. |
Security notes for site owners: everyone with edit_theme_options can change every template, so the Administrator
role should stay limited to people trusted with the whole site; the CSS editors are gated by edit_css because CSS can
leak data or deface pages; and a theme export zip can contain anything the database copies contain, so it should be
reviewed before being shared.
Related pages
Themes Overview, theme.json Settings, theme.json Styles, Typography and Fonts, Style Variations and Section Styles, Templates and Template Parts, Navigation and Menus, Patterns for Site Builders, Child Themes, Hybrid Themes and Migration to Block Themes, Database Schema and Accessibility (contrast of the colours chosen in Styles).
References
-
Make WordPress Core: Responsive block styles and configurable viewports in WordPress 7.1
-
Make WordPress Core: Pseudo and custom style states in WordPress 7.1
-
Make WordPress Core: Miscellaneous Editor Changes in WordPress 7.1
-
Make WordPress Core: Custom CSS for Individual Block Instances in WordPress 7.0
-
WordPress Developer Blog: What’s new for developers? (August 2026)
-
WordPress 7.1.3: wp-includes/class-wp-theme-json-resolver.php