Contents
Introduction
This article explains, in exhaustive detail, how to use theme.json in WordPress to manage styles and presets and how to interact with those presets from PHP and JavaScript. You will learn what theme.json contains, how WordPress exposes presets as CSS custom properties and editor settings, how to read and extend theme.json from PHP, how to generate dynamic block styles and editor presets, and how to read and update preset values in the block editor using JavaScript. Examples are included for theme.json, PHP and JS each code example is provided in the exact requested
wrapper and uses the corresponding language identifier.Concepts and terminology
- theme.json — a JSON file placed in a theme root that defines global styles, presets and settings for the site and block editor. Introduced in WordPress 5.8 and expanded in later releases.
- presets — collections of values for colors, font sizes, gradients, spacing, border radius, etc. They are typically defined under the top-level key settings gt presets or as shorthand keys such as settings.color.palette or styles.typography.fontSizes depending on version.
- styles — default CSS rules and block-level styles that a theme wants to apply globally.
- CSS custom properties — WordPress maps many theme.json presets into CSS variables with a consistent naming scheme like --wp--preset--color--{slug} so both front-end and editor can use the same values.
- runtime/editor settings — the block editor receives parsed theme.json data as settings JavaScript running in the editor can read these settings (and in some cases update them at runtime) via the data stores or by reading CSS variables.
theme.json anatomy — what to put in the file
A minimal theme.json includes top-level keys such as version, settings and styles. The settings area contains presets: color palette, font sizes, gradients, spacing scales, border radii, etc. The styles area contains default CSS-like values for elements and blocks. WordPress parses this into an internal representation and injects generated CSS (custom properties, output rules) into both front-end and editor.
Simple theme.json example
{ version: 2, settings: { color: { palette: [ { slug: primary, color: #0073aa, name: Primary }, { slug: secondary, color: #111827, name: Secondary }, { slug: accent, color: #ffb703, name: Accent } ] }, typography: { fontSizes: [ { slug: small, size: 13px, name: Small }, { slug: normal, size: 16px, name: Normal }, { slug: large, size: 24px, name: Large } ] }, spacing: { units: [ px, rem, % ], blockGap: 1.25rem } }, styles: { color: { background: var(--wp--preset--color--secondary), text: var(--wp--preset--color--primary) }, typography: { fontFamily: system-ui, -apple-system, Segoe UI, Roboto } } }
How WordPress exposes presets to CSS and the editor
- WordPress generates CSS custom properties for many kind of presets. For example, a color in the palette with slug primary becomes --wp--preset--color--primary.
- These variables are injected into the document root (html element) and are accessible in front-end styles and in the editor. That means you can reference them in your theme stylesheet (style.css) or block styles (editor.css), and also in block markup rendered to the front-end.
- The editor also receives parsed preset objects via its data stores, which plugins can read. Additionally you can programmatically update editor settings at runtime (useful for plugins, dynamic presets or user-driven customization interfaces).
Example: CSS variable usage
/ In your themes stylesheet or block styles / .wp-block-button .wp-block-button__link { background-color: var(--wp--preset--color--primary) color: var(--wp--preset--color--secondary) }
Accessing theme.json values from PHP
WordPress core automatically parses theme.json and applies many values without extra code. However, it is common to:
- Read theme.json from PHP to generate things dynamically (for example, register block style variations for each color in the palette).
- Provide backward-compatible support for older WordPress versions by calling add_theme_support where appropriate.
- Merge or extend theme.json values at runtime (for example, inject dynamic color values based on theme settings or user options) by reading the file and then using PHP APIs to register support or block styles.
Read and decode theme.json from PHP
The safest and most forwards-compatible way to access your themes theme.json in PHP is to read it directly using get_theme_file_path(). Then json_decode into an associative array and use the values you need.
lt?php // functions.php (or an included file) function mytheme_get_theme_json_array() { path = get_theme_file_path( /theme.json ) if ( ! file_exists( path ) ) { return array() } raw = file_get_contents( path ) data = json_decode( raw, true ) if ( json_last_error() !== JSON_ERROR_NONE ) { return array() } return data } ?gt
Generate an editor color palette or register block styles from theme.json
You can extract the palette from theme.json and then call legacy APIs like add_theme_support(editor-color-palette, palette) for classic compatibility — though when theme.json is present WP will prefer data in the file. A valuable use is to loop the palette and register block-level variations (block styles) so the editor shows a button style for each color.
lt?php // functions.php add_action( after_setup_theme, mytheme_register_dynamic_styles_from_theme_json ) function mytheme_register_dynamic_styles_from_theme_json() { data = mytheme_get_theme_json_array() // Defensive checks if ( empty( data ) empty( data[settings] ) ) { return } // Example: get palette palette = array() if ( ! empty( data[settings][color][palette] ) is_array( data[settings][color][palette] ) ) { palette = data[settings][color][palette] } // Option 1: register legacy editor color palette for older code or customizer compatibility if ( ! empty( palette ) ) { legacy_palette = array() foreach ( palette as color ) { if ( isset( color[slug], color[color], color[name] ) ) { legacy_palette[] = array( slug => color[slug], color => color[color], name => color[name], ) } } add_theme_support( editor-color-palette, legacy_palette ) } // Option 2: register a block style for the button block for each palette color if ( ! empty( palette ) ) { foreach ( palette as color ) { if ( empty( color[slug] ) ) { continue } slug = sanitize_key( color[slug] ) name = isset( color[name] ) ? color[name] : slug style_name = color-{slug} // Register style for core/button with a unique name and label. register_block_style( core/button, array( name => style_name, label => sprintf( %s (Theme color), name ), ) ) } } } ?gt
Notes about add_theme_support and theme.json
- If you use theme.json, WordPress core will apply many of the same settings automatically. Calling add_theme_support for the editor palette (or font sizes) is mainly for backward compatibility or for plugins/themes that expect legacy support arrays.
- register_block_style is an API to provide style variations for a block (makes them selectable in the editor). It can be used to create color-specific styles or outline styles derived from your presets.
Extending theme.json dynamically in PHP
If you need to compute presets dynamically (for example, generate a palette based on user-defined options or a theme setting), you can read the file, merge your computed values into the parsed array, and then register runtime items like block styles or call update_option for theme.json? Writing back to theme.json is not recommended. Instead, keep the dynamic logic in PHP and register or update editor settings dynamically at initialization.
lt?php // Example: create an extra palette color from a theme_mod and register a block style add_action( after_setup_theme, mytheme_dynamic_palette_from_theme_mod ) function mytheme_dynamic_palette_from_theme_mod() { data = mytheme_get_theme_json_array() palette = data[settings][color][palette] ?? array() // Suppose the site admin set a primary color in the customizer custom_primary = get_theme_mod( mytheme_primary_color, ) if ( custom_primary preg_match( /^#?([0-9a-f]{3}[0-9a-f]{6})/i, custom_primary ) ) { hex = ( strpos( custom_primary, # ) === 0 ) ? custom_primary : # . custom_primary // Add it with a special slug palette[] = array( slug => custom-primary, color => hex, name => Custom Primary ) // Register a block style for core/button for this custom color register_block_style( core/button, array( name => color-custom-primary, label => Custom Primary ) ) } } ?gt
Using theme.json presets from JavaScript (editor)
Inside the block editor you can access theme presets in several ways:
- Read CSS custom properties produced from theme.json with getComputedStyle(document.documentElement).getPropertyValue(--wp--preset--...). This works in editor and front-end and is robust.
- Read editor settings via the block editor data store (wp.data.select or the React hooks useSelect). The editor exposes settings objects with presets.
- Update editor settings at runtime via wp.data.dispatch(core/block-editor).updateSettings( { ... } ). This is useful for editor plugins that need to inject or modify presets on load.
Reading CSS variables in JS
This approach does not rely on editor internals: the generated CSS variables are available on the root element.
// Read the primary palette color CSS variable const root = document.documentElement const primary = getComputedStyle(root).getPropertyValue(--wp--preset--color--primary).trim() console.log(Primary color from theme.json via CSS var:, primary)
Reading editor settings via wp.data (React plugin example)
Inside an editor plugin, use useSelect to read settings, or use wp.data.select() directly. The returned settings object contains properties like colors and fontSizes when available.
/ Example for a Gutenberg plugin file (ESNext) This reads settings via the editor data store. / const { registerPlugin } = wp.plugins const { PluginSidebar } = wp.editPost const { useSelect } = wp.data const { TextControl } = wp.components const { Fragment } = wp.element function MySidebar() { const settings = useSelect( ( select ) => { // core/block-editor contains editor UI settings in many versions return select( core/block-editor )?.getSettings?.() select( core/editor )?.getSettings?.() }, [] ) // settings.colors may be an array of palette entries const colors = settings?.colors settings?.color?.palette [] return ( wp.element.createElement( Fragment, null, wp.element.createElement( PluginSidebar, { name: my-sidebar, title: Theme Presets }, colors.map( (c) => wp.element.createElement( TextControl, { key: c.slug c.name, label: c.name c.slug, value: c.color c.value } ) ) ) ) ) } registerPlugin( my-theme-presets-sidebar, { render: MySidebar } )
Updating editor settings at runtime
If you need to add presets after the editor has initialized (for example, plugin-injected palettes or dynamic presets from an API), you can call updateSettings on the editor store. This merges into the existing settings used by many block controls.
// Example: inject an additional color into the block editor settings at runtime const { dispatch } = wp.data const extraColor = { name: Injected, slug: injected, color: #bada55 } // Add to existing palette (this is not persisted, only for the current editor session) const settings = wp.data.select( core/block-editor )?.getSettings?.() {} const existingColors = settings.colors settings.color?.palette [] const newColors = existingColors.concat( extraColor ) // Update settings so controls that rely on settings read the new palette dispatch( core/block-editor ).updateSettings( { colors: newColors } )
Caveats about updateSettings
- updateSettings affects the current editor session only. It will not write changes back to theme.json or persist beyond the current load.
- Some blocks or controls may read their settings only on initialization, so changes made later might not reach older UI components without re-rendering or re-mounting them.
Practical end-to-end examples
Below are two complete practical examples: one where PHP generates block styles for each palette color, and one where a JS plugin reads CSS variables to show palette swatches in a sidebar.
lt?php // functions.php add_action( after_setup_theme, mytheme_register_button_styles_and_enqueue_editor_styles ) function mytheme_register_button_styles_and_enqueue_editor_styles() { // Register editor styles so the editor preview shows variable-based styles add_theme_support( editor-styles ) add_editor_style( assets/css/editor.css ) // ensures editor sees your CSS variables based rules data = mytheme_get_theme_json_array() palette = data[settings][color][palette] ?? array() if ( ! empty( palette ) ) { foreach ( palette as color ) { if ( empty( color[slug] ) ) { continue } // Name like color-primary style = color- . sanitize_key( color[slug] ) register_block_style( core/button, array( name => style, label => isset( color[name] ) ? color[name] : color[slug], ) ) } } } // Enqueue editor CSS that uses theme.json CSS variables add_action( enqueue_block_editor_assets, mytheme_enqueue_block_editor_assets ) function mytheme_enqueue_block_editor_assets() { wp_enqueue_style( mytheme-block-editor-styles, get_theme_file_uri( /assets/css/editor.css ), array(), filemtime( get_theme_file_path( /assets/css/editor.css ) ) ) } ?gt
In your editor.css you can use the variables that WordPress generated:
/ assets/css/editor.css / .wp-block-button.is-style-color-primary .wp-block-button__link { background-color: var(--wp--preset--color--primary) color: var(--wp--preset--color--secondary) } .wp-block-button.is-style-color-accent .wp-block-button__link { background-color: var(--wp--preset--color--accent) color: #fff }
/ File: my-plugin-presets.js Enqueued as an editor script for plugin assets. / ( function( wp ) { const { registerPlugin } = wp.plugins const { PluginSidebar } = wp.editPost const { useState, useEffect } = wp.element function ColorSwatchesSidebar() { const [ palette, setPalette ] = useState( [] ) useEffect( () => { const root = document.documentElement // If the palette keys are known, you can read them individually // Otherwise, you may keep a static list of expected slugs or read the settings store const slugs = [ primary, secondary, accent ] const items = slugs.map( (slug) => { const cssVar = --wp--preset--color-- slug const value = getComputedStyle( root ).getPropertyValue( cssVar ).trim() return { slug: slug, color: value } } ) setPalette( items ) }, [] ) return wp.element.createElement( PluginSidebar, { name: my-color-swatches, title: Theme Color Swatches }, wp.element.createElement( div, { style: { padding: 1rem } }, palette.map( (p) => wp.element.createElement( div, { key: p.slug, style: { marginBottom: 0.5rem } }, wp.element.createElement( div, { style: { width: 36px, height: 36px, background: p.color, display: inline-block, borderRadius: 4px, marginRight: 8px, verticalAlign: middle } } ), wp.element.createElement( span, null, p.slug : p.color ) ) ) ) ) } registerPlugin( my-color-swatches, { render: ColorSwatchesSidebar } ) } )( window.wp )
Best practices and recommendations
- Prefer theme.json for static theme presets: If your presets are static and known at build time, put them in theme.json and let WordPress manage variables and editor settings.
- Use CSS custom properties to style both front-end and editor: Refer to --wp--preset-- variables in your CSS to keep consistent branding between editor and site.
- Use PHP to generate dynamic or backward-compatible items: Read theme.json with get_theme_file_path and programmatically register block styles or call legacy APIs for older consumers.
- Use JS only for runtime injection or editor UI: updateSettings in wp.data can be used to affect the current editor session only. For persisted changes, update the source (theme.json or theme_mods) at PHP level or via a UI that writes options.
- Enqueue an editor stylesheet: add_theme_support(editor-styles) and add_editor_style(editor.css) so the block editor preview receives your theme’s CSS rules that use custom properties.
- Sanitize values when registering items: Use sanitize_key, esc_html or appropriate sanitization for slugs, names and colors to avoid invalid registrations.
Compatibility notes and version history
- theme.json was introduced in WordPress 5.8. The file format (structure and property names) has evolved the examples in this article assume the modern version (version: 2) which is common in WP 5.9 and 6.x.
- WordPress core will map many properties into CSS variables and editor settings however, some advanced or new preset types may require newer WordPress versions. Check core release notes if a specific preset does not appear to be output.
- Legacy functions like add_theme_support(editor-color-palette) still work but are superseded by theme.json. Use them only for compatibility with older plugins or code.
Troubleshooting
- Preset not available in editor? Ensure your theme.json is valid JSON and uses the correct schema version. Use jsonlint or PHP json_decode and check json_last_error().
- CSS variables missing on front-end? WordPress outputs variables when theme.json is parsed and enabled. Confirm the theme.json file is in the theme root and correctly formatted. Enqueue the block styles if needed, and clear any caching layer.
- Runtime updates not reflected? updateSettings affects only the editor session. Controls that cached settings at mount time might not re-read them. Consider initializing updates early or re-mounting components where possible.
- Block styles not visible? Ensure you call register_block_style before the editor UI is rendered (after_setup_theme is a good hook). Also make sure your editor stylesheet contains the actual CSS rules that the style refers to (matching the style class names).
Summary
theme.json is the canonical place for theme presets and global styles in modern WordPress. Use it for static values and consistent CSS variable generation. For dynamic scenarios:
- Read theme.json with PHP (get_theme_file_path json_decode) and register dynamic block styles or call legacy add_theme_support as needed.
- In the editor, read presets either by inspecting the CSS custom properties (robust) or by reading the editor settings in wp.data (useSelect or wp.data.select).
- Apply runtime changes using wp.data.dispatch(core/block-editor).updateSettings for editor-only injections, and persist changes on the server side if they must survive reloads.
Following the patterns shown here you can build themes and plugins that keep editor and front-end styling in sync, provide dynamic styling options, and extend the editor UX based on the same single source of truth: theme.json.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |