How to use custom CSS in theme.json

WordPress 6.2 added two new custom CSS options to the Styles sidebar in the Site Editor:

  • The site-wide Additional CSS option, which is the Site Editing equivalent of the Additional CSS Customizer option.
  • The per-block CSS option, where you add CSS to all copies of a single block type. This option uses the block’s unique CSS class as the pre-determined CSS selector.

This lesson focuses on the theme.json implementation. If you need a re-cap of how to use theme.json before you continue, please read the first lesson about Global styles & theme.json.

Estimated reading time: 3 minutes

Last updated

Introduction

The new CSS options have been added as replacements for Additional CSS option in the Customizer, which is hidden when you activate a block theme.

It is not a replacement for enqueueing CSS files if you need to add a lot of custom CSS.
You can use it to add a small amount of CSS to your theme when block options are not enough but you don’t want to add an extra stylesheet only to solve a small issue.

WordPress applies the CSS to the editors and the front of the website. On the front, the CSS is printed in the <head> in the global-styles-inline-css <style> tag in the following order:

  1. Global styles (CSS from other theme.json options and the Styles interface)
  2. CSS from the Additional CSS option in the Customizer
  3. Site-wide custom CSS
  4. Per-block custom CSS

Since you are working with a JSON file, the CSS needs to be written on one line, without line breaks. The CSS is enclosed in double quotes ", so you also need to escape any double quotes that you include in your CSS.

Site-wide custom CSS

You add the site-wide custom CSS as a string at the top level of the styles section in theme.json. The string can contain any valid CSS and the CSS selectors of your choice.

{
"version": 2,
"styles": {
"css": "..."
}
}

Per-block custom CSS

You add the per-block custom CSS as a string to styles.blocks.blockname.css. With this option, the CSS selector is already included for you and is either:

  • The block’s unique CSS class (wp-block-blockname).
  • The block’s experimental selector, as added in block.json.
  • Or, in case of blocks without default CSS classes, such as the paragraph block, the HTML element.

Below is a basic theme.json example for the table block, which has an experimental selector:

{
"version": 2,
"styles": {
"blocks": {
"core/table": {
"css": "color:#333"
}
}
}
}

Output:

.wp-block-table > table {
color:#333;
}

Extending custom CSS with the & selector

The new CSS options also introduce the possibility of using the & CSS selector to target nested elements and use pseudo selectors. For example, you could use it with :not, :before or :after.

In this basic example, I am adding the text color to the table and a background to the table header:

{
	"version": 2,
	"styles": {
		"blocks": {
			"core/table": {
				"css": "color:#333 & th{ background:#f5f5f5; }"
			}
		}
	}
}

WordPress then processes the values and outputs plain CSS:

.wp-block-table > table {
	color: #333;
}

.wp-block-table > table th {
	background: #f5f5f5;
}

When you want to target inner blocks, for example, the img element inside the figure in the image block, you can use a single space or & followed by a space:

{
"version": 2,
"styles": {
"blocks": {
"core/image": {
"css": " img{border:2px solid #ffffff;}"
}
}
}
}
{
"version": 2,
"styles": {
"blocks": {
"core/image": {
"css": "& img{border:2px solid #ffffff;}"
}
}
}
}

Output:

.wp-block-image img {
	border: 2px solid #ffffff;
}

A common example is setting the default width of the separator block:

{
"version": 2,
"styles": {
"blocks": {
"core/separator": {
"css": "&:not(.is-style-wide):not(.is-style-dots):not(.alignwide):not(.alignfull){width: 100px}"
}
}
}
}

Another is setting a border radius on the avatar:

{
"version": 2,
"styles": {
"blocks": {
"core/avatar": {
"css": "& img {border-radius: 999px}"
}
}
}
}

Resources

Thank you Enea (Overclokk) for the helping improve this article.