Themes & Block Editor: experimental theme.json Edit

These features are still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.

Documentation has been shared early to surface what’s being worked on and invite feedback from those experimenting with the APIs. Please, be welcome to share yours in the weekly #core-editor chats as well as async via the Github issues and Pull Requests.

This is documentation for the current direction and work in progress about how themes can hook into the various sub-systems that the Block Editor provides.

  • Rationale
    • Presets become CSS Custom Properties
    • Some block styles are managed
    • Settings can be controlled per block
  • Specification
    • Settings
    • Styles

Rationale Rationale

The Block Editor surface API has evolved at different velocities, and it’s now at a point where is showing some growing pains, specially in areas that affect themes. Examples of this are: the ability to control the editor programmatically, or a block style system that facilitates user, theme, and core style preferences.

This describes the current efforts to consolidate the various APIs into a single point – a experimental-theme.json file that should be located inside the root of the theme directory.

Presets become CSS Custom Properties Presets become CSS Custom Properties

Presets such as color palettes, font sizes, and gradients will be enqueued as CSS Custom Properties for themes to use.

These will be enqueued to the front-end and editor.

Top ↑

Some block styles are managed Some block styles are managed

By providing the block style properties in a structured way, the Block Editor can “manage” the CSS that comes from different origins (user, theme, and core CSS), reducing the amount of CSS loaded in the page and preventing specificity wars due to the competing needs of the components involved (themes, blocks, plugins).

Top ↑

Settings can be controlled per block Settings can be controlled per block

The Block Editor already allows the control of specific settings such as alignment, drop cap, whether it’s present in the inserter, etc at the block level. The goal is to surface these for themes to control.

Top ↑

Specification Specification

The experimental-theme.json file is divided into sections known as “contexts”, that represent a different CSS selector. For example, the core/paragraph context maps to p while core/group maps to .wp-block-group. In general, one block will map to a single context as in the cases mentioned. There are cases where one block can generate multiple contexts (different CSS selectors). For example, the heading block generates six different contexts (core/heading/h1, core/heading/h2, etc), one for each different selector (h1, h2, etc).

{
  "global": { ... },
  "core/paragraph": { ... },
  "core/group": { ... },
  "core/heading/h1": { ... },
  "core/heading/h2": { ... },
  "core/heading/h3": { ... },
  "core/heading/h4": { ... },
  "core/heading/h5": { ... },
  "core/heading/h6": { ... }
}

Every context has the same structure, divided in two sections: settings and styles. The settings are used to control the editor (enable/disable certain features, declare presets), taking over what was previously declared via add_theme_support. The styles will be used to create new style rules to be appended to a new stylesheet global-styles-inline-css enqueued in the front-end and post editor.

{
  "some/context": {
    "settings": {
      "color": [ ... ],
      "typography": [ ... ],
      "spacing": [ ... ]
    },
    "styles": {
      "color": { ... },
      "typography": { ... }
    }
  }
}

This structure is the same for the three different origins that exist: core, themes, and users. Themes can override core’s defaults by creating a file called experimental-theme.json. Users, via the site editor, will also be also to override theme’s or core’s preferences via an user interface that is being worked on.

Top ↑

Settings Settings

The settings section has the following structure and default values:

{
  "some/context": {
    "settings": {
      "color": {
        "custom": true, /* false to opt-out, as in add_theme_support('disable-custom-colors') */
        "customGradient": true, /* false to opt-out, as in add_theme_support('disable-custom-gradients') */
        "gradients": [ ... ], /* gradient presets, as in add_theme_support('editor-gradient-presets', ... ) */
        "link": false, /* true to opt-in, as in add_theme_support('experimental-link-color') */
        "palette": [ ... ], /* color presets, as in add_theme_support('editor-color-palette', ... ) */
      },
      "spacing": {
        "customPadding": false, /* true to opt-in, as in add_theme_support('experimental-custom-spacing') */
        "units": [ "px", "em", "rem", "vh", "vw" ], /* filter values, as in add_theme_support('custom-units', ... ) */
      },
      "typography": {
        "customFontSize": true, /* false to opt-out, as in add_theme_support( 'disable-custom-font-sizes' ) */
        "customLineHeight": false, /* true to opt-in, as in add_theme_support( 'custom-line-height' ) */
        "dropCap": true, /* false to opt-out */
        "fontSizes": [ ... ], /* font size presets, as in add_theme_support('editor-font-sizes', ... ) */
      }
    }
  }
}

To retain backward compatibility, add_theme_support declarations are considered as well. If a theme uses add_theme_support('disable-custom-colors'), it’ll be the same as set global.settings.color.custom to false. If the experimental-theme.json contains any settings, these will take precedence over the values declared via add_theme_support.

Settings can also be controlled by context, providing a more fine-grained control over what exists via add_theme_support. As an example, let’s say that a theme author wants to enable custom colors for the paragraph block exclusively. This is how it’d be done:

{
  "global": {
    "settings": {
      "color": {
        "custom": false
      }
    }
  },
  "core/paragraph": {
    "settings": {
      "color": {
        "custom": true
      }
    }
  }

Note, however, that not all settings are relevant for all contexts and the blocks they represent. The settings section provides an opt-in/opt-out mechanism for themes, but it’s the block’s responsibility to add support for the features that are relevant to it. For example, if a block doesn’t implement the dropCap feature, a theme can’t enable it for such a block through experimental-theme.json.

Top ↑

Presets Presets

Presets are part of the settings section. At the moment, they only work within the global context. Each preset value will generate a CSS Custom Property that will be added to the new stylesheet, which follow this naming schema: --wp--preset--{preset-category}--{preset-slug}.

For example, for this input:

{
  "global": {
    "settings": {
      "color": {
        "palette": [
          {
            "slug": "strong-magenta",
            "color": "#a156b4"
          },
          {
            "slug": "very-dark-grey",
            "color": "rgb(131, 12, 8)"
          }
        ],
        "gradients": [
          {
            "slug": "blush-bordeaux",
            "gradient": "linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%)"
          },
          {
            "slug": "blush-light-purple",
            "gradient": "linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%)"
          },
        ]
      },
      "typography": {
        "fontSizes": [
          {
            "slug": "normal",
            "size": 16
          },
          {
            "slug": "big",
            "size": 32
          }
        ]
      }
    }
  }
}

The output to be enqueued will be:

:root {
  --wp--preset--color--strong-magenta: #a156b4;
  --wp--preset--color--very-dark-gray: #444;
  --wp--preset--font-size--big: 32;
  --wp--preset--font-size--normal: 16;
  --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--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);
}

The goal is that presets can be defined using this format, although, right now, the name property (used in the editor) can’t be translated from this file. For that reason, and to maintain backward compatibility, the presets declared via add_theme_support will also generate the CSS Custom Properties. If the experimental-theme.json contains any presets, these will take precedence over the ones declared via add_theme_support.

Top ↑

Styles Styles

Each block declares which style properties it exposes. This has been coined as “implicit style attributes” of the block. These properties are then used to automatically generate the UI controls for the block in the editor, as well as being available through the experimental-theme.json file for themes to target.

{
  "some/context": {
    "styles": {
      "color": {
        "background": <value>,
        "gradient": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  }
}

For example, an input like this:

{
  "core/heading/h1": {
    "styles": {
      "color": {
        "text": "var(--wp--preset--color--primary)"
      },
      "typography": {
        "fontSize": "calc(1px * var(--wp--preset--font-size--huge))"
      }
    }
  },
  "core/heading/h4": {
    "styles": {
      "color": {
        "text": "var(--wp--preset--color--secondary)"
      },
      "typography": {
        "fontSize": "var(--wp--preset--font-size--normal)"
      }
    }
  }
}

will append the following style rules to the stylesheet:

h1 {
  color: var(--wp--preset--color--primary);
  font-size: calc(1px * var(--wp--preset--font-size--huge));
}
h4 {
  color: var(--wp--preset--color--secondary);
  font-size: calc(1px * var(--wp--preset--font-size--normal));
}

Color Properties Color Properties

These are the current color properties supported by blocks:

Context Background Gradient Link Text
Global Yes Yes Yes Yes
Columns Yes Yes Yes Yes
Group Yes Yes Yes Yes
Heading [1] Yes Yes Yes
Media & text Yes Yes Yes Yes
Paragraph Yes Yes Yes
Post Author Yes Yes Yes Yes
Post Comments Yes Yes Yes Yes
Post Comments Count Yes Yes Yes
Post Comments Form Yes Yes Yes Yes
Post Date Yes Yes Yes
Post Excerpt Yes Yes Yes Yes
Post Tags Yes Yes Yes Yes
Post Title Yes Yes Yes
Site Tagline Yes Yes Yes
Site Title Yes Yes Yes

[1] The heading block represents 6 distinct HTML elements: H1-H6. It comes with selectors to target each individual element (ex: core/heading/h1 for H1, etc).

Top ↑

Typography Properties Typography Properties

These are the current typography properties supported by blocks:

Context Font Size Line Height
Global Yes
Columns
Group
Heading [1] Yes Yes
Media & text
Paragraph Yes Yes
Post Author Yes Yes
Post Comments Yes Yes
Post Comments Count Yes Yes
Post Comments Form Yes Yes
Post Date Yes Yes
Post Excerpt Yes Yes
Post Tags Yes Yes
Post Title Yes Yes
Site Tagline Yes Yes
Site Title Yes Yes

[1] The heading block represents 6 distinct HTML elements: H1-H6. It comes with selectors to target each individual element (ex: core/heading/h1 for H1, etc).