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
  • Specification
  • Current Status

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 ↑

Individual features can be controlled per block Individual features can be controlled per block

The Block Editor already allows the control of specific features 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 specification for the experimental-theme.json follows the three main functions described in the section above: presets, styles, and features.

{
  "presets": {
    "color": [ ... ],
    "font-size": [ ... ],
    "gradient": [ ... ],
  },
  "styles": { ... },
  "features": {... }
}

The file is divided into sections that represent different contexts: individual blocks, as well as the global environment.

{
  "global": {
    "presets": { ... },
    "styles": { ... },
    "features": { ... }
  },
  "core/paragraph": {
    "presets": { ... },
    "styles": { ... },
    "features": { ... }
  },
  "core/group": {
    "presets": { ... },
    "styles": { ... },
    "features": { ... }
  }
}

Some of the functions are context-dependant. Take, as an example, the drop cap:

{
  "global": {
    "features": {
      "typography": {
        "dropCap": false
      }
    }
  },
  "core/paragraph": {
    "features": {
      "typography": {
        "dropCap": true
      }
    }
  },
  "core/image": {
    "features": {
      "typography": {
        "dropCap": true
      }
    }
  }
}

In the example above, we aim to encapsulate that the drop cap should be disabled globally but enabled in the paragraph context. Based on the current implementation, the drop cap in the Image block context wouldn’t make sense so it would be ignored (but it could be used by plugins that extend its functionality).

Top ↑

Current Status Current Status

Top ↑

Presets Presets

So far, this function is only enabled for the global section.

The generated CSS Custom Properties follow this naming schema: --wp--preset--{preset-category}--{preset-slug}.

For this input:

"global": {
  "presets": {
    "color": [
      {
        "slug": "strong-magenta",
        "value": "#a156b4"
      },
      {
        "slug": "very-dark-grey",
        "value": "#444"
      }
    ]
  }
}

The following stylesheet will be enqueued to the front-end and editor:

:root {
  --wp--preset--color--strong-magenta: #a156b4;
  --wp--preset--color--very-dark-gray: #444;
}

The goal is that presets can be defined using this format, although, right now, the name property (used to be shown 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. The equivalent example to above is:

add_theme_support( 'editor-color-palette', array(
  array(
    'name' => __( 'strong magenta', 'themeLangDomain' ),
    'slug' => 'strong-magenta',
    'color' => '#a156b4',
  ),
  array(
    'name' => __( 'very dark gray', 'themeLangDomain' ),
    'slug' => 'very-dark-gray',
    'color' => '#444',
  ),
) );

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 will declare 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.

Color Properties Color Properties

Context Background Gradient Link Text
Global Yes
Paragraph Yes Yes Yes
Heading [1] Yes Yes Yes
Group Yes Yes Yes Yes
Columns Yes Yes Yes Yes
Media & text Yes 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

Context Font Size Line Height
Global
Paragraph Yes Yes
Heading [1] Yes Yes
Group
Columns
Media & text

[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 ↑

Features Features

Via the features key we allow controlling some editor and block features according to the following rules:
– The features within global override the default editor settings, which plugins can modify by hooking into the block_editor_settings filter on the server.
– The features within the context of each block override the default block settings, which plugins can modify by hooking into the blocks.registerBlockType filter on the client.
– Block settings take precedence over global settings.

For example, if a feature is enabled for a block by default, although the theme disables it at the global level, the block keeps it enabled. If the theme wants to override a block’s default, it has to target that particular block.

So far, this function is enabled only for the global section in lib/experimental-default-theme.json.

{
  "global": {
    "features": {
      "typography": {
        "dropCap": false
      }
    }
  }
}

Then each block can decide to override how they handle block editor features during their registration process (register_block_type or registerBlockType calls) using supports object in block.json file:

{
  "supports": {
    "__experimentalFeatures": {
      "typography": {
        "dropCap": true
      }
    }
  }
}

Moving forward, we plan to integrate overrides targeting individual blocks defined inside a theme specific file (experimental-theme.json) that would be applied on top of features defined by block authors in supports property.

The list of features that are currently supported are:
– Paragraph: drop cap.

Top ↑

Recap of current available functions Recap of current available functions

{
  "global": {
    "presets": {
      "color": [
        {
          "slug": <preset slug>,
          "value": <preset value>
        },
        { <more colors> }
      ],
      "font-size": [
        {
          "slug": <preset slug>,
          "value": <preset value>
        },
        { <more font sizes> }
      ],
      "gradient": [
        {
          "slug": <preset slug>,
          "value": <preset value>
        },
        { <more gradients> }
      ]
    },
    "styles: {
      "color: {
        "background": <value>
      }
    }
  },
  "core/paragraph": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/heading/h1": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/heading/h2": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/heading/h3": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/heading/h4": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/heading/h5": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/heading/h6": {
    "styles": {
      "color": {
        "background": <value>,
        "link": <value>,
        "text": <value>
      },
      "typography": {
        "fontSize": <value>,
        "lineHeight": <value>
      }
    }
  },
  "core/columns": {
    "styles": {
      "color": {
        "background": <value>,
        "gradient": <value>,
        "link": <value>,
        "text": <value>
      }
    }
  },
  "core/group": {
    "styles": {
        "color": {
          "background": <value>,
          "gradient": <value>,
          "link": <value>,
          "text": <value>
        }
      }
  },
  "core/media-text": {
    "styles": {
        "color": {
          "background": <value>,
          "gradient": <value>,
          "link": <value>,
          "text": <value>
        }
      }
  }
}