The WordPress Block Editor can use your theme’s color system directly inside the editing experience, giving authors access to the same brand colors that are used on the frontend.
Instead of allowing editors to choose arbitrary colors for every paragraph, button or background, a theme can define a controlled palette such as:
- brand primary;
- brand secondary;
- accent;
- background;
- surface;
- text;
- muted text;
- contrast.
WordPress can expose those colors in supported block controls, generate reusable CSS custom properties and keep the editor much closer to the site’s actual design system.
For modern themes, the preferred way to configure this is theme.json.
A basic palette looks like this:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"palette": [
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
},
{
"name": "Background",
"slug": "background",
"color": "#ffffff"
},
{
"name": "Text",
"slug": "text",
"color": "#111827"
}
]
}
}
}
But changing the WordPress Block Editor theme color can mean several different things.
You may want to change the colors available in block controls, establish default text and background colors, remove WordPress’s default palette, prevent arbitrary custom colors, style individual blocks or change the editor canvas so it resembles the frontend.
Those tasks are related, but they are not identical.
This guide explains how WordPress’s modern color system works, how to customize it with theme.json, how classic theme support fits into the picture and how to keep editor colors consistent with the frontend.
What does “Block Editor theme color” actually mean?
Before changing anything, it helps to separate three different layers of the WordPress editing experience.
The content color palette
This is the set of colors authors can choose when editing blocks.
Depending on the block, WordPress may expose controls for:
- text color;
- background color;
- link color;
- gradients;
- duotone effects;
- other block-specific color properties.
This is usually what developers mean when they talk about customizing the Block Editor’s theme colors.
The visual appearance of the edited content
You may also want the content canvas itself to resemble the frontend.
For example, if your website uses a dark background with light text, editing the content against a white canvas can give authors a misleading preview.
That problem involves theme styles and editor styles in addition to the palette itself.
The architecture is covered in detail in WordPress Block Editor CSS, Explained.
The WordPress editor interface
The surrounding WordPress interface, including toolbars, sidebars, buttons and editor controls, is a separate layer.
Defining:
"primary": "#1d4ed8"
inside your theme palette does not automatically recolor the entire WordPress administration interface.
theme.json primarily describes the site’s design system and block editing capabilities. It should not be confused with arbitrary CSS customization of the WordPress admin UI.
Using theme.json to define a custom color palette
The modern WordPress theme system uses theme.json to configure global settings and styles.
The official WordPress introduction to theme.json explains that the file can define global settings, styles, color palettes, typography and other aspects of a theme’s design system.
It works with both block themes and classic themes, although it is particularly central to modern block-theme development.
Color configuration belongs under:
settings.color
The current WordPress Theme Handbook color documentation describes the available color settings, including palettes, gradients, duotone presets and controls for user-defined colors.
A practical palette could look like this:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"palette": [
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
},
{
"name": "Secondary",
"slug": "secondary",
"color": "#7c3aed"
},
{
"name": "Accent",
"slug": "accent",
"color": "#f59e0b"
},
{
"name": "Background",
"slug": "background",
"color": "#ffffff"
},
{
"name": "Surface",
"slug": "surface",
"color": "#f3f4f6"
},
{
"name": "Text",
"slug": "text",
"color": "#111827"
},
{
"name": "Muted",
"slug": "muted",
"color": "#6b7280"
}
]
}
}
}
WordPress can now expose these presets in the color controls of blocks that support them.
Each color needs a name, slug and value
A palette entry contains three important properties:
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
}
name is the human-readable label displayed to users.
slug is the stable machine-readable identifier WordPress uses when generating classes and CSS custom properties.
color contains the actual color value.
The slug is particularly important because content can reference the preset rather than storing only a raw hexadecimal value.
Use semantic slugs rather than literal color names
Consider these two approaches:
"slug": "blue"
and:
"slug": "primary"
The second is usually more maintainable.
If the brand later changes from blue to red, a preset called primary can simply receive a new color value.
A preset called blue that now contains red creates a semantic mess.
Useful design-system names include:
primary;secondary;accent;base;contrast;surface;muted.
The exact naming system matters less than keeping it stable and meaningful.
How WordPress turns palette colors into CSS
Registering a palette does more than populate a color picker.
WordPress also generates CSS custom properties for registered presets.
A palette entry with:
"slug": "primary"
generates a variable following the WordPress preset naming convention:
--wp--preset--color--primary
You can then use it in ordinary CSS:
.my-component {
color: var(--wp--preset--color--primary);
}
This is one of the major advantages of using the WordPress preset system instead of copying hexadecimal values throughout a stylesheet.
If the primary color changes, components using the preset variable can inherit the updated design token.
The official Global Settings and Styles documentation explains how presets defined through theme.json participate in generated styles and editor settings.
WordPress can also generate preset classes
When a block uses a registered color preset, WordPress can represent that choice with generated classes.
For a preset called primary, examples include:
.has-primary-color
for text and:
.has-primary-background-color
for backgrounds.
This means the content can reference the semantic preset rather than embedding the same raw color everywhere.
That distinction becomes valuable when maintaining a site over several years or changing brand colors without rebuilding every individual block.
Setting default theme colors, not just available colors
The settings section defines capabilities and presets. It does not necessarily mean those colors become the default appearance of the website.
For defaults, WordPress also provides the top-level styles section.
For example:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"palette": [
{
"name": "Background",
"slug": "background",
"color": "#ffffff"
},
{
"name": "Text",
"slug": "text",
"color": "#111827"
},
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
}
]
}
},
"styles": {
"color": {
"background": "var:preset|color|background",
"text": "var:preset|color|text"
}
}
}
This establishes default global background and text colors using registered presets.
The WordPress theme.json styles reference documents global style properties including background, text and gradient colors.
Settings and styles solve different problems
This distinction is worth remembering:
| Section | Purpose |
|---|---|
settings.color.palette |
Defines color presets available to the system and editor |
settings.color.custom |
Controls whether arbitrary custom colors can be selected |
settings.color.defaultPalette |
Controls access to WordPress’s default palette |
styles.color.text |
Defines the global default text color |
styles.color.background |
Defines the global default background color |
Confusing settings with styles is a common reason a developer adds a palette and then wonders why the site’s background did not suddenly change.
Removing WordPress default colors and restricting custom choices
WordPress provides its own default palette when the theme does not restrict it.
For a general-purpose theme, that flexibility can be useful.
For a client site with a strict brand system, allowing editors to choose from brand colors, WordPress defaults and arbitrary custom values can quickly undermine design consistency.
theme.json lets you control this.
Disable the default WordPress palette
Set:
"defaultPalette": false
inside settings.color.
For example:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"defaultPalette": false,
"palette": [
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
},
{
"name": "Background",
"slug": "background",
"color": "#ffffff"
},
{
"name": "Text",
"slug": "text",
"color": "#111827"
}
]
}
}
}
The official color settings documentation describes defaultPalette as the control for whether users can select colors from WordPress’s default palette.
Disable arbitrary custom colors
If users should select only from approved presets, set:
"custom": false
A stricter brand configuration could therefore be:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"custom": false,
"defaultPalette": false,
"palette": [
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
},
{
"name": "Secondary",
"slug": "secondary",
"color": "#7c3aed"
},
{
"name": "Accent",
"slug": "accent",
"color": "#f59e0b"
},
{
"name": "Background",
"slug": "background",
"color": "#ffffff"
},
{
"name": "Text",
"slug": "text",
"color": "#111827"
}
]
}
}
}
Editors can now work with the approved palette instead of inventing a slightly different blue every time a button is created.
Gradients and duotone have separate controls
Restricting custom colors does not automatically mean every other color-related feature is restricted.
WordPress also exposes settings such as:
customGradient;defaultGradients;customDuotone;defaultDuotone.
A tightly controlled configuration could include:
{
"settings": {
"color": {
"custom": false,
"customGradient": false,
"customDuotone": false,
"defaultPalette": false,
"defaultGradients": false,
"defaultDuotone": false
}
}
}
The current theme.json version 3 reference documents these color settings and their current defaults.
Controlling text, background and link colors
WordPress also lets themes control whether standard color tools are exposed.
Relevant settings include:
{
"settings": {
"color": {
"background": true,
"text": true,
"link": true
}
}
}
These controls determine whether compatible blocks can expose the corresponding design tools.
For example, if you do not want editors changing block backgrounds:
{
"settings": {
"color": {
"background": false,
"text": true
}
}
}
The important qualification is that block support still matters.
A block needs to support the relevant color feature for WordPress to expose and apply it. The official Block Supports documentation explains how blocks declare support for features such as text and background colors.
You can configure individual blocks separately
Global settings are not always enough.
A design system may allow flexible colors for Groups and Buttons while intentionally restricting colors on other blocks.
theme.json supports block-level configuration through:
settings.blocks
For example:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"custom": false
},
"blocks": {
"core/paragraph": {
"color": {
"background": false
}
}
}
}
}
This allows the global design system to remain flexible while applying tighter rules to particular blocks.
That is often preferable to disabling a useful feature everywhere simply because one block should not expose it.
Using the older editor-color-palette theme support
Before theme.json, themes commonly registered Block Editor colors through the editor-color-palette theme-support API.
The approach is still relevant for older theme architectures and compatibility scenarios.
The official WordPress Block Editor Theme Support documentation shows the traditional implementation:
function mytheme_setup() {
add_theme_support(
'editor-color-palette',
array(
array(
'name' => __( 'Primary', 'mytheme' ),
'slug' => 'primary',
'color' => '#1d4ed8',
),
array(
'name' => __( 'Secondary', 'mytheme' ),
'slug' => 'secondary',
'color' => '#7c3aed',
),
array(
'name' => __( 'Background', 'mytheme' ),
'slug' => 'background',
'color' => '#ffffff',
),
array(
'name' => __( 'Text', 'mytheme' ),
'slug' => 'text',
'color' => '#111827',
),
)
);
}
add_action(
'after_setup_theme',
'mytheme_setup'
);
WordPress then exposes those colors as the editor palette.
theme.json takes precedence
Modern WordPress maps several older add_theme_support() features into the Global Settings system for backward compatibility.
For example:
| Older theme support | theme.json equivalent |
|---|---|
editor-color-palette |
settings.color.palette |
disable-custom-colors |
settings.color.custom: false |
disable-custom-gradients |
settings.color.customGradient: false |
editor-gradient-presets |
settings.color.gradients |
link-color |
settings.color.link: true |
The official Global Settings and Styles documentation notes that when theme.json contains the relevant settings, those values take precedence over equivalent legacy theme-support declarations.
For new development, using theme.json generally creates a more coherent design system than splitting related configuration across PHP declarations and CSS files.
Making the editor match the frontend
A custom palette solves only part of the editing experience.
If the frontend uses your colors but the editor canvas does not resemble the actual page, authors can still receive a misleading preview.
There are two major tools for solving this:
theme.jsonglobal styles;- editor stylesheets.
Use theme.json for design-system defaults
If your site’s normal text and background colors belong to the theme’s global design system, define them through the styles section.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"palette": [
{
"name": "Background",
"slug": "background",
"color": "#0f172a"
},
{
"name": "Text",
"slug": "text",
"color": "#f8fafc"
},
{
"name": "Primary",
"slug": "primary",
"color": "#38bdf8"
}
]
}
},
"styles": {
"color": {
"background": "var:preset|color|background",
"text": "var:preset|color|text"
}
}
}
This is substantially more meaningful than registering the colors in the picker while leaving the global theme defaults unrelated.
Use editor styles when ordinary CSS is required
Some designs still require CSS that is not naturally represented through theme.json.
Classic and hybrid theme workflows can opt into editor styles:
function mytheme_editor_styles() {
add_theme_support( 'editor-styles' );
add_editor_style( 'editor-style.css' );
}
add_action(
'after_setup_theme',
'mytheme_editor_styles'
);
The editor stylesheet might then contain:
body {
background: #0f172a;
color: #f8fafc;
}
a {
color: #38bdf8;
}
However, modern Block Editor styling has an important architectural detail: editor content and the surrounding WordPress UI are not simply one styling context.
For the full distinction between add_editor_style(), enqueue_block_assets, enqueue_block_editor_assets, the editor canvas and editor interface, see WordPress Block Editor CSS, Explained.
Do not use admin CSS as a substitute for theme styles
A stylesheet loaded through general administration hooks is not automatically the correct mechanism for styling block content.
The public site, editor content canvas and surrounding administration interface are separate concerns.
If you are working with WordPress asset loading more broadly, wp_enqueue_scripts Explained covers handles, dependencies, stylesheet loading and the distinction between frontend and administration assets.
Global Styles, block themes and user customizations
With a block theme, theme.json participates in a larger Global Styles system.
The theme provides defaults, but users with appropriate access can also customize site-wide design through the WordPress Styles interface.
This is one reason it is useful to think of theme.json as a design-system configuration rather than merely a configuration file for the editor color picker.
For a broader explanation of block themes, Site Editing and Global Styles, see WordPress Full Site Editing and Block Widgets, Explained.
Theme defaults are not necessarily the final value
A developer may define:
"color": "#1d4ed8"
for a primary preset and then discover that a particular site does not visually match the expected theme defaults.
Possible reasons include:
- user-created Global Styles;
- block-specific styles;
- custom CSS;
- plugin styles;
- older block content containing explicit values;
- style variations;
- CSS specificity.
Do not assume that editing theme.json is equivalent to replacing every color value that has ever been stored or applied on the site.
Existing custom colors may remain custom
Suppose an editor previously chose:
#2463eb
as an arbitrary custom color.
You later register:
{
"name": "Primary",
"slug": "primary",
"color": "#2463eb"
}
The visual color may match, but that does not necessarily convert previously stored custom values into references to your new preset.
Likewise, disabling custom colors prevents future selection through the relevant editor control. It should not be treated as a database migration that rewrites all historical block markup.
Building a practical WordPress color system
A useful editor palette should represent the site’s design language rather than every color a designer has ever used.
A compact palette might contain:
{
"palette": [
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
},
{
"name": "Secondary",
"slug": "secondary",
"color": "#7c3aed"
},
{
"name": "Accent",
"slug": "accent",
"color": "#f59e0b"
},
{
"name": "Base",
"slug": "base",
"color": "#ffffff"
},
{
"name": "Contrast",
"slug": "contrast",
"color": "#111827"
},
{
"name": "Muted",
"slug": "muted",
"color": "#6b7280"
}
]
}
This gives editors meaningful choices without presenting thirty nearly identical shades.
Separate design tokens from component decisions
The palette should define reusable color roles.
Individual components can then use those roles.
For example:
.card {
background: var(--wp--preset--color--base);
color: var(--wp--preset--color--contrast);
}
.card__link {
color: var(--wp--preset--color--primary);
}
If the brand changes, updating the preset values can propagate through components that use those tokens.
This is considerably easier to maintain than scattering values such as:
#1d4ed8
through dozens of stylesheets.
Do not expose every design token to editors
Your internal CSS system may contain colors for:
- borders;
- focus states;
- disabled controls;
- success messages;
- errors;
- hover states;
- decorative effects.
That does not mean every token belongs in the Block Editor palette.
The editor palette should contain colors authors can safely use in content.
Implementation-specific tokens can remain internal to the theme.
Think about contrast, not only branding
A color being part of the brand does not mean every combination of brand colors produces readable text.
When defining text and background combinations, evaluate contrast and accessibility.
The W3C guidance for WCAG contrast minimum explains the contrast requirements that apply to normal and large text under WCAG 2.2.
Restricting editors to a curated palette can help maintain consistency, but it does not automatically prevent them from selecting two approved colors that have poor contrast when combined.
Common Block Editor color customization mistakes
Changing the palette and expecting the entire admin interface to change
settings.color.palette defines theme color presets. It does not redesign WordPress toolbars, panels and administration navigation.
Theme colors and admin UI colors are separate concerns.
Using only raw hexadecimal values everywhere
Hard-coded values make later brand changes unnecessarily expensive.
Use semantic presets and WordPress-generated CSS custom properties where appropriate.
Using color names as permanent slugs
A slug such as primary can remain meaningful after a redesign.
A slug such as bright-blue becomes misleading if the brand’s primary color later changes.
Defining a palette but forgetting global styles
A palette defines available presets. It does not automatically establish the site’s default background and text colors.
Use the styles section when you need global defaults.
Leaving unrestricted custom colors on a tightly controlled client site
If brand consistency matters, consider:
"custom": false
and:
"defaultPalette": false
so editors primarily work with approved presets.
Disabling every color tool globally
The opposite extreme can also be unnecessary.
If only Paragraph backgrounds are undesirable, configure that block rather than removing useful color tools from every compatible block.
Editing generated WordPress CSS
Do not modify generated preset CSS as if it were a source file.
Change the source configuration in theme.json or your theme styles instead.
Loading editor CSS into the wrong context
Modern WordPress separates editor content from surrounding editor UI.
If CSS appears to do nothing, verify that it is being loaded into the document you actually intend to style.
WordPress Block Editor CSS, Explained covers this architecture and the appropriate asset-loading methods.
Forgetting to test the frontend
The purpose of editor styling is not merely to make Gutenberg look attractive.
The editor and frontend should communicate the same design system.
After changing colors, test:
- Paragraph blocks;
- Headings;
- Buttons;
- Groups;
- Columns;
- Cover blocks;
- links;
- custom blocks;
- the editor canvas;
- the public frontend;
- existing content created before the palette changed.
A complete theme.json color example
The following example combines a custom palette, removes WordPress’s default palette, disables arbitrary colors and gradients, enables link colors and establishes global text and background defaults:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"color": {
"background": true,
"text": true,
"link": true,
"custom": false,
"customGradient": false,
"defaultPalette": false,
"defaultGradients": false,
"palette": [
{
"name": "Primary",
"slug": "primary",
"color": "#1d4ed8"
},
{
"name": "Secondary",
"slug": "secondary",
"color": "#7c3aed"
},
{
"name": "Accent",
"slug": "accent",
"color": "#f59e0b"
},
{
"name": "Base",
"slug": "base",
"color": "#ffffff"
},
{
"name": "Surface",
"slug": "surface",
"color": "#f3f4f6"
},
{
"name": "Contrast",
"slug": "contrast",
"color": "#111827"
},
{
"name": "Muted",
"slug": "muted",
"color": "#6b7280"
}
]
}
},
"styles": {
"color": {
"background": "var:preset|color|base",
"text": "var:preset|color|contrast"
},
"elements": {
"link": {
"color": {
"text": "var:preset|color|primary"
}
}
}
}
}
This creates a much more controlled editing environment than leaving every WordPress default and arbitrary color option available.
It also establishes semantic tokens that can be reused by blocks and ordinary CSS.
If you are building a full block theme rather than only customizing the Post Editor, WordPress Full Site Editing and Block Widgets, Explained provides the wider architectural context for theme.json, Global Styles, templates and the Site Editor.
Related guides
- WordPress Block Editor CSS, Explained
- WordPress Full Site Editing and Block Widgets, Explained
- Block Editor vs. Classic Editor: Which Fits Your Site?
- Does Switching WordPress Editors Affect Your Content?
- wp_enqueue_scripts Explained
- px vs. em vs. rem: CSS Units Explained
Final recommendation
For modern WordPress development, use theme.json as the primary source of truth for your theme’s Block Editor color system.
Define a small semantic palette rather than exposing dozens of arbitrary colors:
Primary
Secondary
Accent
Base
Surface
Contrast
Muted
Use stable slugs such as primary and contrast instead of names tied to literal colors.
If brand consistency matters, consider disabling WordPress’s default palette and arbitrary custom colors:
"custom": false,
"defaultPalette": false
Use settings.color.palette to define available presets and the styles section to establish actual theme defaults.
Then reuse WordPress-generated variables such as:
var(--wp--preset--color--primary)
in your CSS instead of duplicating raw color values throughout the project.
For older theme architectures, add_theme_support( 'editor-color-palette' ) remains a supported compatibility approach, but theme.json provides a more complete system for modern themes and takes precedence when equivalent settings are defined there.
Finally, remember that the Block Editor palette, the appearance of edited content and the surrounding WordPress interface are separate layers.
A well-designed WordPress color system aligns the editor with the frontend without confusing theme design tokens with administration-interface styling. The result is not merely a prettier color picker. It is an editing environment where authors can create content without accidentally drifting away from the site’s visual system.

