Theme.json color options

In this lesson, you will learn how to use all the theme.json color options, including:

  • Site-wide and block specific color palettes
  • Enable or disable custom colors (color picker)
  • Enable or disable gradients, duotone, link- and border colors
  • Adding default colors to blocks

Level: Beginner, developer

At the end of the lesson, you will also find answers to frequently asked questions.

Prerequisites: Global Styles & theme.json

Estimated reading time: 18 minutes

Introduction

What color options can you change with theme.json?

The WordPress block editor has many different types of color options:

  • Background color: Solid and gradient
  • Text color
  • Link text color
  • Border color
  • Inline text color
  • Duotone
  • Cover block overlay

With theme.json, you can:

  • Decide which colors, gradients, and duotones the user can choose from
  • Decide if the user should be able to select custom colors using the color picker
  • Disable and enable link color, border colors, gradients, and duotones
  • Use different color options per block
  • Apply default colors to many blocks

What colors can you not change with theme.json?

You may run into problems when you apply colors to complex blocks with multiple HTML elements. For example, you can not change the text color of a quote without changing the color of the citation.

What color values can you use in theme.json?

Except for duotones, you can use any valid CSS color values to add or apply colors. Such as Hex, rgb, rgba, color names like blue or red, keywords like transparent and currentColor, hsl, and hsla. You can also use a CSS custom property.JSON structure.

Duotone only works with hex and rgb color values.

JSON structure

All three color options, the palette, duotone, and gradients, use the same JSON structure with key and value pairs:

  • slug: The unique identifier, also used in the CSS custom property.
  • name: The visible name of the colors in the editor.
  • color/gradient/duotone: The CSS color value.

Color palettes

The default color palette

WordPress registers colors in its default color palette with CSS custom properties that are available for all themes. This is so the default colors doesn’t break if the user switches themes.

The palette is persistent: even if you add a new color palette, the CSS custom properties for these colors remain. You can not remove them, but you can assign new values to them.

View the default colors

–wp–preset–color–black: #000000;

–wp–preset–color–cyan-bluish-gray: #abb8c3;

–wp–preset–color–white: #ffffff;

–wp–preset–color–pale-pink: #f78da7;

–wp–preset–color–vivid-red: #cf2e2e;

–wp–preset–color–luminous-vivid-orange: #ff6900;

–wp–preset–color–luminous-vivid-amber: #fcb900;

–wp–preset–color–light-green-cyan: #7bdcb5;

–wp–preset–color–vivid-green-cyan: #00d084;

–wp–preset–color–pale-cyan-blue: #8ed1fc;

–wp–preset–color–vivid-cyan-blue: #0693e3;

–wp–preset–color–vivid-purple: #9b51e0;

How to assign new color values to the default color palette

The default color "pale pink" has the hex value #f78da7 and the custom property
var(--wp--preset--color--pale-pink).
You can change the value of pale pink by registering a new color using the same slug:

{
	"version": 1,
	"settings":{
		"color": {
			"palette": [
				{
					"slug": "pale-pink",
					"color": "#fdcee4",
					"name": "Pale pink"
				}
			]
		}
	}
}

How to add a new color palette to the block editor

You can create a new color palette inside the settings section of theme.json:

settings.color.palette

This palette replaces the default palette for all blocks.
You list the palette colors inside an array, encasing them with curly brackets and separating them with a comma:

{
	"version": 1,
	"settings": {
		"color": {
			"palette": [
				{
					"slug": "primary",
					"color": "#7c027c",
					"name": "Purple"
				},
				{
					"slug": "secondary",
					"color": "#420142",
					"name": "Dark purple"
				}
			]
		}
	}
}

WordPress parses the JSON and creates a CSS custom property for each color, and adds them to the body element:

body {
    --wp--preset--color--primary: #7c027c;
    --wp--preset--color--secondary: #420142;
}

These properties are available in addition to the custom properties for the default palette.

How to add a color palette for a block

You can create separate color palettes for blocks under the blocks section inside the settings section:

settings.blocks.blockname.color.palette

Example:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/paragraph": {
				"color": {
					"palette": [
						{
							"slug": "primary",
							"color": "#7c027c",
							"name": "Purple"
						},
						{
							"slug": "secondary",
							"color": "#420142",
							"name": "Dark purple"
						}
					]
				}
			}
		}
	}
}

The block specific CSS custom properties are not added to body, but to the block selector:

p {
    --wp--preset--color--primary: #7c027c;
    --wp--preset--color--secondary: #420142;
}

How to disable color palettes

There are a few different ways to disable color palettes and color palette options. You can remove the color palette for all blocks by leaving the array of colors empty:

{
	"version": 1,
	"settings": {
		"color": {
			"palette": []
		}
	}
}

Or you can remove it for a single block:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/paragraph": {
				"color": {
					"palette": []
				}
			}
		}
	}
}

How to disable custom colors

You can disable custom colors (the color picker) for all blocks by setting color.custom to false inside the settings section:

{
	"version": 1,
	"settings": {
		"color": {
			"custom": false
		}
	}
}

Here is how you would disable custom colors for a single block:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/paragraph": {
				"color": {
					"custom": false
				}
			}
		}
	}
}

You can use the following code to disable the color picker for all blocks except the paragraph:

{
	"version": 1,
	"settings": {
		"color": {
			"custom": false
		},
		"blocks": {
			"core/paragraph": {
				"color": {
					"custom": true
				}
			}
		}
	}
}

Only a few blocks support the border color and link color controls in the editor. You can still set default colors with theme.json, but the user can’t change the colors.

{
	"version": 1,
	"settings": {
		"border": {
			"customColor": true
		},
		"color": {
			"link": true
		}
	}
}

You can also enable this site-wide, but disable it for specific blocks.
Switch the true and false values to disable link color site-wide and enable them for the single block. The border color option works the same way.

{
	"version": 1,
	"settings": {
		"color": {
			"link": true
		},
		"blocks": {
			"core/list": {
				"color": {
					"link": false
				}
			}
		}
	}
}

Duotone

Duotone is a filter where you pair two colors to create effects for videos and images. It does not create CSS custom properties. Instead, the effect uses an inline SVG filter.

Read more about the introduction of duotones in WordPress:

WordPress.org: Coloring Your Images With Duotone Filters

WP Tavern: Duotone Filters: WordPress 5.8 Puts a Powerful Image-Editing Tool Into Users’ Hands

As one of the newest color filters, duotone does have some bugs.
The Gutenberg developers are exploring solutions for known issues with transparency for duotones, and duotones for fixed backgrounds.

Which WordPress blocks support duotone?

So far, only the cover block and the image block supports the duotone filter.
Gutenberg version 11.4.0 will add the filter to the featured image block,
and there is a suggestion to add the duotone filter to the site logo.

Default duotones

Dark grayscale

The WordPress default duotone applied to an image of wapuu.

Grayscale

Purple and yellow

Blue and red

Midnight (Black and blue)

Magenta and yellow

Purple and green

Blue and orange

How to add duotone colors

You can add new duotones inside the settings section of theme.json:

settings.color.duotone

In comparison to the color palette which takes one color value, the duotone takes an array with two color values. So the first color is the shadow, and the second color is the highlight.
Separate the values with a comma.

Important: Duotone only works with the hex and rgb color formats:

"colors": [ "#cc1871", "#f9449e" ]

If you add more than two colors, the effect will only use the first two colors.

Example of how to add a new duotone for both blocks (site-wide):

{
	"version": 1,
	"settings": {
		"color": {
			"duotone": [
				{
					"colors": [ "#cc1871", "#f9449e" ],
					"slug": "pink-and-light-pink",
					"name": "Pink and light pink"
				}
			]
		}
	}
}

And for a single block:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/cover": {
				"color": {
					"duotone": [
						{
					"colors": [ "#cc1871", "rgb(255,255,255)" ],
							"slug": "pink-and-white",
							"name": "Pink and white"
						}
					]
				}
			}
		}
	}
}

How to disable duotone

You need to add an empty duotone color array and set color.customDuotone to false to completely disable duotone:

{
	"version": 1,
	"settings": {
		"color": {
			"duotone": [],
                        "customDuotone": false
		}
	}
}

How to disable duotone for a single block:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/cover": {
				"color": {
					"duotone": [],
					"customDuotone": false
				}
			}
		}
	}
}

To only disable custom duotones (the color picker), you can set color.customDuotone to false:

{
	"version": 1,
	"settings": {
		"color": {
			"customDuotone": false
		}
	}
}

How to disable the custom duotone color picker for single blocks:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/image": {
				"color": {
					"customDuotone": false
				}
			}
		}
	}
}

Gradients

Which WordPress blocks support gradient backgrounds?

Button, column, columns, group, list, media & text, post author, post comments count, post comments form, post comments, post date, post excerpt, post terms, post title, preformatted, pullquote, next and previous pagination, archive title, post list (query), search form button, site tagline, site title, table, template part, and verse.

Default gradients

The default gradients are persistent as the CSS custom properties are output even if you disable gradients in your theme. This prevents the colors from breaking if the user has used the gradients and then changes themes.

–wp–preset–gradient–vivid-cyan-blue-to-vivid-purple:
linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);

–wp–preset–gradient–light-green-cyan-to-vivid-green-cyan:
linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);

–wp–preset–gradient–luminous-vivid-amber-to-luminous-vivid-orange:
linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);

–wp–preset–gradient–luminous-vivid-orange-to-vivid-red:
linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);

–wp–preset–gradient–very-light-gray-to-cyan-bluish-gray:
linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);

–wp–preset–gradient–cool-to-warm-spectrum:
linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,
rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);

–wp–preset–gradient–blush-light-purple:
linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);

–wp–preset–gradient–blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);

–wp–preset–gradient–luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);

–wp–preset–gradient–pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);

–wp–preset–gradient–electric-grass:
linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);

–wp–preset–gradient–midnight:
linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);

How to add new gradients to the block editor

Note: There is a known bug where you can’t edit custom gradients using the range slider in the editor.

You add new gradients under the settings section of theme.json:

settings.color.gradients

Use any valid CSS gradient: linear, radial, conic, or repeating, with any number of colors and opacity. Example:

{
	"version": 1,
	"settings": {
		"color": {
			"gradients": [
				{
					"slug": "pink-to-pale-purple",
					"gradient": "linear-gradient(to bottom right,rgba(255,30,142) 49.9%,rgb(229,6,229, 0.36) 50%)",
					"name": "Diagonal pink to pale purple"
				}
			]
		}
	}
}

You can also experiment with different shapes and hard stops.

CSS output:

body {
--wp--preset--gradient--pink-to-pale-purple: linear-gradient(to bottom right,rgba(255,30,142) 49.9%,rgb(229,6,229, 0.36) 50%);
}

How to add different gradients for different blocks:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/group": {
				"color": {
					"gradients": [
						{
							"slug": "pink-to-pale-purple",
							"gradient": "linear-gradient(to bottom right,rgba(255,30,142) 49.9%,rgb(229,6,229, 0.36) 50%)",
							"name": "Diagonal pink to pale purple"
						}
					]
				}
			},
			"core/search": {
				"color": {
					"gradients": [
						{
							"slug": "white-to-grey",
							"gradient": "linear-gradient(90deg, rgb(255,255,255) 0%, rgb(203,203,203) 49%, rgb(255,255,255) 98%)",
							"name": "White to grey"
						}
					]
				}
			}
		}
	}
}

Changing the color of the button in the search block is a new option in Gutenberg 11.2.0.

CSS output:

.wp-block-group {
    --wp--preset--gradient--pink-to-pale-purple: linear-gradient(to bottom right,rgba(255,30,142) 49.9%,rgb(229,6,229, 0.36) 50%);
}

.wp-block-search {
    --wp--preset--gradient--white-to-grey: linear-gradient(90deg, rgb(255,255,255) 0%, rgb(203,203,203) 49%, rgb(255,255,255) 98%);
}

How to disable gradients

You need to add an empty gradients array and set color.customGradient to false to disable gradients site-wide:

{
	"version": 1,
	"settings": {
		"color": {
			"gradients": [],
			"customGradient": false
		}
	}
}

Next is an example of how to disable gradients for a single block:

{
	"version": 1,
	"settings": {
		"blocks": {
			"core/cover": {
				"color": {
					"duotone": [],
					"customDuotone": false
				}
			}
		}
	}
}

Applying colors with theme.json

In the first part of this lesson, we went through how to add color options to the editor.
Next, I will show you some practical examples of applying colors to blocks in the styles section of theme.json.

One thing to keep in mind is that you are not limited to using presets that you created in the settings section. You can use any valid CSS color value.

Text color

Colors on the root level of the styles section in theme.json are applied to the HTML body.

styles.color.text
{
	"version": 1,
	"styles": {
		"color": {
			"text": "var(--wp--preset--color--black)"
		}
	}
}

CSS Output:

body {
    color: var(--wp--preset--color--black);
}

The next example shows how to add a default text color to a single block:

{
	"version": 1,
	"styles": {
		"blocks": {
			"core/verse": {
				"color": {
					"text": "var(--wp--preset--color--black)"
				}
			}
		}
	}
}

And the resulting CSS output for the verse block is:

.wp-block-verse {
    color: var(--wp--preset--color--black);
 }

Background color

You add the body background color the same way as you add the text color:

styles.color.background
{
	"version": 1,
	"styles": {
		"color": {
			"background": "var(--wp--preset--color--black)"
		}
	}
}

CSS output:

body {
    background-color: var(--wp--preset--color--black);
}

The code for adding a background for a single block is equally straightforward:

{
	"version": 1,
	"styles": {
		"blocks": {
			"core/search": {
				"color": {
					"background": "var(--wp--preset--color--black)"
				}
			}
		}
	}
}

Note: Adding a default background color for the search block in theme.json changes the background color of the form element, while changing the background color in the editor changes the form button.

Bug or feature? Sometimes it is hard to tell.

CSS output:

.wp-block-search {
    background-color: var(--wp--preset--color--black);
}

How to add a default gradient background to a block

You can use the CSS custom property for one of your gradients as a default background color, but you need to use some additional CSS.

The CSS output by the background color option uses background-color:value.
This is a problem because gradients only work with background:value. So you need to close background-color, and include background:

"initial; background: var(--wp--preset--gradient--cool-to-warm-spectrum)"

I have used initial, but you can also add a fallback color.

{
	"version": 1,
	"styles": {
		"blocks": {
			"core/heading": {
				"color": {
					"background": "initial; background: var(--wp--preset--gradient--cool-to-warm-spectrum)"
				}
			}
		}
	}
}

CSS output:

h1, h2, h3, h4, h5, h6 {
    background-color: initial;
    background: var(--wp--preset--gradient--cool-to-warm-spectrum);
}

How to add a default overlay color for the cover block

Theme.json does not support overlay colors out of the box, but you can use a trick that is similar to what I showed you for the gradients.

The cover block has a black overlay color by default, and the CSS is:

.wp-block-cover-image.has-background-dim:not([class*=-background-color]),
.wp-block-cover.has-background-dim:not([class*=-background-color]) {
    background-color: #000;
}

When we add colors using theme.json, we do not have the option to add has-background-dim:not. Instead, we can use !important to override the default:

{
	"version": 1,
	"styles": {
		"blocks": {
			"core/cover": {
				"color": {
					"background": "red !important"
				}
			}
		}
	}
}

CSS output:

.wp-block-cover {
    background-color: red !important;
}

And if the user selects a different overlay color, our default is overridden by the .has-* CSS class and !important:

.has-vivid-green-cyan-background-color {
    background-color: var(--wp--preset--color--vivid-green-cyan) !important;
}

The link color works differently from the text and body background color. Previously, you would add the link color as styles.color.link. Now if you are still using the old format, you need to update your theme.json file to use elements.

You use the elements object to apply styles to links and headings, independent of the block.
With elements, you can style all H2 headings, and then you can use block-level styles to override it. You can not use elements for any other HTML tags.

{
	"version": 1,
	"styles": {
		"elements": {
			"link": {
				"color": {
					"text": "var(--wp--preset--color--secondary)"
				}
			}
		}
	}
}

CSS output:

a {
    color: var(--wp--preset--color--secondary);
}

Use the heading levels H1 to H6 to add default colors to headings:

{
	"version": 1,
	"styles": {
		"elements": {
			"h1": {
				"color": {
					"text": "var(--wp--preset--color--secondary)"
				}
			}
		}
	}
}

CSS output:

h1 {
    color: var(--wp--preset--color--secondary);
}

You can also add background color:

{
	"version": 1,
	"styles": {
		"elements": {
			"link": {
				"color": {
					"text": "white",
					"background": "#222222"
				}
			}
		}
	}
}

One use case for the link background color is to style the pagination.
Remember that when you apply styling to blocks, the styles first need to be placed inside the blocks object:

styles.blocks.blockname.elements.link.color.text
styles.blocks.blockname.elements.link.color.background
{
	"version": 1,
	"styles": {
		"blocks": {
			"core/query-pagination": {
				"elements": {
					"link": {
						"color": {
							"text": "#fff",
							"background": "#222"
						}
					}
				}
			}
		}
	}
}

Another use case for adding a background color to links is to highlight post titles and read more links, like in this example on Tammie Lister’s website ephemeralthemes.com:

A blog post listing where the post title and read more links has a high contrast yellow background color and a black link text.

Styling the post excerpt read more text can seem tricky. -First the block needs to have the link option enabled and a custom read more text, otherwise, there is no link element to style.

In this example, I have included padding to make the background color more visible:

{
	"version": 1,
	"styles": {
		"blocks": {
			"core/post-excerpt": {
				"elements": {
					"link": {
						"color": {
				"background": "var(--wp--preset--color--pale-cyan-blue)"
						},
						"spacing": {
							"padding": {
								"top": "0.6rem",
								"right": "1rem",
								"bottom": "0.6rem",
								"left": "1rem"
							}
						}
					}
				}
			}
		}
	}
}

Border color

There is a known bug with WordPress 5.8 and a missing button block border setting, this will be solved in WordPress 5.8.1.

Which WordPress blocks support borders?

All blocks can have a default border added in theme.json.

In August 2021, with WordPress 5.8 and Gutenberg version 11.3.0, the group and pull quote blocks are the only blocks that have controls for both border color, style, width, and radius.

  • The button block and image block have controls for border radius.
  • The table block has controls for style, width, and color, but not radius.
  • Finally, the search block’s submit button has controls for border color and radius.

For the border controls to be visible in the editor, you need to enable the feature.

Button border controls in the block settings sidebar includes an input field for border width, a style selection option with solid, dashed and dotted.
Below is a color selection option and a border radius option with an input field and a range slider.

Border color examples

When you add a border color you also need to add a border style for the border to be visible:

{
	"version": 1,
	"styles": {
		"border": {
			"style": "solid",
			"color": "var(--wp--preset--color--pale-pink)"
		}
	}
}

One use case for adding borders to the body element could be for boxed layouts.

Button and pullquote code examples:

{
	"version": 1,
	"settings": {
		"border": {
			"customColor": true,
			"customRadius": true,
			"customStyle": true,
			"customWidth": true
		}
	},
	"styles": {
		"blocks": {
			"core/button": {
				"border": {
					"style": "solid",
					"color": "var(--wp--preset--color--pale-pink)"
				}
			},
			"core/pullquote":{
				"border": {
					"width": "4px",
					"radius": "10px",
					"style": "dotted",
					"color": "var(--wp--preset--color--cyan-bluish-gray)"
				}
			}
		}
	}
}
A button block with a solid pink border, followed by a pullquoute block with a dotted gray border and rounded corners.

Frequently asked questions

How should I name the colors?

It is recommended to use primary, secondary, tertiary and so on as the color slug, because the slug is the unique identifier.
But the visible name can be a color name or description.
Example: “slug”: “primary”, “color”: “#7c027c”, “name”: “Purple”

If more themes use this naming convention, it will be easier for the user to change themes and keep the color contrast and readability of their existing content.

You can follow the discussion about best practices and the naming convention in this overview issue on GitHub: Default Colors, Theme Colors, and Custom Colors.

How do I apply a default duotone on a block?

It is not possible to use theme.json to apply a duotone on a block yet.
There is a pull request for adding this, but it has not been cadded to Gutenberg yet (August 22 2021).

Can you use separate palettes for background and text color?

This is not possible with theme.json. There was a GitHub issue with a proposal to add contextual palettes, but the issues was closed with the comment that there are no plans to implement this.

Can you use a background color palette but no text color palette or vice versa?

This is not possible with theme.json, but you can achieve this by filtering the block support with JavaScript, setting the text color support to false.

Are there any plans to add gradient support to paragraphs?

This question have come up during different user testing sessions, and I have created an issue for adding gradients backgrounds to paragraph blocks.
There is also a discussion about possibly adding gradient text to headings.