本文档介绍了影响块编辑器用户内容样式的主要概念,包括块样式、全局样式和布局样式。面向块作者和块编辑器项目开发者,提供相关参考指南和教程链接。
{
"name": "core/paragraph",
"...": "...",
"supports": {
"typography": {
"fontSize": true
}
}
}This document introduces the main concepts related to styles that affect the user content in the block editor. It points to the relevant reference guides and tutorials for readers to dig deeper into each one of the ideas presented. It’s aimed to block authors and people working in the block editor project.
By creating a post in the block editor the user is creating a number of artifacts: a HTML document plus a number of CSS stylesheets, either embedded in the document or external.
The final HTML document is the result of a few things:
The stylesheets loaded in the front end include:
wp-block-library-* ) or separate stylesheets per block in use (as in wp-block-group-*, wp-block-columns-*, etc). See this note for the full details.global-styles-inline-css.twentytwentytwo-style-css. In addition to having their own stylesheets they can now declare a theme.json file containing styles that will be part of the stylesheet generated by global styles.Since the introduction of the block editor in WordPress 5.0, there were tools for the users to “add styles” to specific blocks. By using these tools, the user would attach new classes or inline styles to the blocks, modifying their visual aspect.
By default, blocks come with a given HTML markup. Think of the paragraph block, for example:
<p></p>
In its simplest form, any style rule that targets the p selector will apply styles to this block, whether it comes from a block, a theme, etc.
The user may change the state of this block by applying different styles: a text alignment, a color, a font size, a line height, etc. These states are reflected in the HTML markup of the block in the form of HTML attributes, mainly through the class or style attributes, though it can be any other the block author sees fit.
After some user modifications to the block, the initial markup may become something like this:
<p class="has-color has-green-color has-font-size has-small-font-size my-custom-class"
style="line-height: 1em"></p>
This is what we refer to as “user-provided block styles”, also know as “local styles” or “serialized styles”. Essentially, each tool (font size, color, etc) ends up adding some classes and/or inline styles to the block markup. The CSS styling for these classes is part of the block, global, or theme stylesheets.
The ability to modify a block state coupled with the fact that a block can live within any other block (think of a paragraph within a group), creates a vast amount of potential states and style possibilities.
If you follow the block tutorial you can learn up about the different parts of the block API presented here in more detail and also build your own block. This is an introduction to the general concepts of how a block can let users edit its state.
To build an experience like the one described above a block author needs a few pieces:
In essence, these are the essential mechanics a block author needs to care about for their block to be able to be styled by the user. While this can be done completely manually, there’s an API that automates this process for common style needs: block supports.
Block Supports is an API that allows a block to declare what features it supports. By adding some info to their block.json file, the block tells the system what kind of actions a user can do to it.
For example:
{
"name": "core/paragraph",
"...": "...",
"supports": {
"typography": {
"fontSize": true
}
}
}
The paragraph declares support for font size in its block.json. This means the block will show a UI control for users to tweak its font size, unless it’s disabled by the theme (learn more about how themes can disable UI controls in the theme.json reference). The system will also take care of setting up the UI control data (the font size of the block if it has one already assigned, the list of available font sizes to show), and will serialize the block data into HTML markup upon user changes (attach classes and inline styles appropriately).
By using the block supports mechanism via block.json, the block author is able to create the same experience as before just by writing a couple of lines. Check the block supports api for adding block supports to static or dynamic blocks.
Besides the benefit of having to do less work to achieve the same results, there’s a few other advantages:
While the Block Supports API provides value, it also comes with some limitations a block author needs to be aware of. To better visualize what they are, let’s run with the following example of a table block:
<table>
<thead>
<tr>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr>
<th>First</th>
</tr>
<tr>
<th>Second</th>
</tr>
</tbody>
<tfoot>
<tr>
<th>Footer</th>
</tr>
</tfoot>
</table>
One of the limitations is that, from all the styles available, there can be only one instance of any them in use by the block. Following the example, the table block can only have a single font size. If the block author wanted to have three different font sizes (head, body, and footer) it can’t do it using the current block supports API. See this issue for more detailed info and ways forward.
The block supports API only serializes the font size value to the wrapper, resulting in the following HTML <table class="has-small-font-size"> . The current block supports API doesn’t serialize this value to a different node, for example, the <tbody>.
This is an active area of work you can follow in the tracking issue. The linked proposal is exploring a different way to serialize the user changes: instead of each block support serializing its own data (for example, classes such as has-small-font-size, has-green-color) the idea is the block would get a single class instead (for example, wp-style-UUID) and the CSS styling for that class will be generated in the server by WordPress.
While work continues in that proposal, there’s an escape hatch, an experimental option block authors can use. Any block support can skip the serialization to HTML markup by using __experimentalSkipSerialization. For example:
{
"name": "core/paragraph",
"...": "...",
"supports": {
"typography": {
"fontSize": true,
"__experimentalSkipSerialization": true
}
}
}
This means that the typography block support will do all of the things (create a UI control, bind the block attribute to the control, etc) except serializing the user values into the HTML markup. The classes and inline styles will not be automatically applied to the wrapper and it is the block author’s responsibility to implement this in the edit, save, and render_callback functions. See this issue for examples of how it was done for some blocks provided by WordPress.
Note that, if __experimentalSkipSerialization is enabled for a group (typography, color, spacing) it affects all block supports within this group. In the example above all the properties within the typography group will be affected (e.g. fontSize, lineHeight, fontFamily .etc).
To enable for a single property only, you may use an array to declare which properties are to be skipped. In the example below, only fontSize will skip serialization, leaving other items within the typography group (e.g. lineHeight, fontFamily .etc) unaffected.
{
"name": "core/paragraph",
"...": "...",
"supports": {
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalSkipSerialization": [ "fontSize" ]
}
}
}
Support for this feature was added in this PR.
Global Styles refers to a mechanism that generates site-wide styles. Unlike the block styles described in the previous section, these are not serialized into the post content and are not attached to the block HTML. Instead, the output of this system is a new stylesheet with id global-styles-inline-css.
This mechanism was introduced in WordPress 5.8. At the time, it only took data from WordPress and the active theme. WordPress 5.9 expanded the system to also take style data from users.
This is the general data flow:

The process of generating the stylesheet has, in essence, three steps:
theme.json file bundled with WordPress, the theme.json file of the active theme if it exists, and the user’s styles provided via the global styles UI in the site editor.The data can come from three different origins: WordPress defaults, the active theme, or the user. All three of them use the same theme.json format.
Data from WordPress and the active theme is retrieved from the corresponding theme.json file. Data from the user is pulled from the database, where it’s stored after the user saves the changes they did via the global styles sidebar in the site editor.
The goal of this phase is to build a consolidated structure.
There are two important processes going on in this phase. First, the system needs to normalize all the incoming data, as different origins may be using different versions of the theme.json format. For example, a theme may be using v1 while the WordPress base is using the latest version. Second, the system needs to decide how to merge the input into a single structure. This will be the focus of the following sections.
Different parts of the incoming theme.json structure are treated differently. The data present in the styles section is blended together following this logic: user data overrides theme data, and theme data overrides WordPress data.
For example, if we had the following three theme.json structures coming from WordPress, the theme, and the user respectively:
{
"styles": {
"color": {
"background": "<WordPress value>"
},
"typography": {
"fontSize": "<WordPress value>"
}
}
}
{
"styles": {
"typography": {
"fontSize": "<theme value>",
"lineHeight": "<theme value>"
}
}
}
{
"styles": {
"typography": {
"lineHeight": "<user value>"
}
}
}
The result after the consolidation would be:
{
"styles": {
"color": {
"background": "<WordPress value>"
},
"typography": {
"fontSize": "<theme value>",
"lineHeight": "<user value>"
}
}
}
The settings section works differently than styles. Most of the settings are only used to configure the editor and have no effect on the global styles. Only a few of them are part of the resulting stylesheet: the presets.
Presets are the predefined styles that are shown to the user in different parts of the UI: the color palette or the font sizes, for example. They comprise the following settings: color.duotone, color.gradients, color.palette, typography.fontFamilies, typography.fontSizes. Unlike styles, presets from an origin don’t override values from other origins. Instead, all of them are stored in the consolidated structure.
For example, if we have the following theme.json structures coming from WordPress, the theme, and the user respectively:
{
"settings": {
"color": {
"palette": [ "<WordPress values>" ],
"gradients": [ "<WordPress values>" ]
}
}
}
{
"settings": {
"color": {
"palette": [ "<theme values>" ]
},
"typography": {
"fontFamilies": [ "<theme values>" ]
}
}
}
{
"settings": {
"color": {
"palette": [ "<user values>" ]
}
}
}
The result after the consolidation would be:
{
"settings": {
"color": {
"palette": {
"default": [ "<WordPress values>" ],
"theme": [ "<theme values>" ],
"user": [ "<user values>" ]
},
"gradients": {
"default": [ "<WordPress values>" ]
}
},
"typography": {
"fontFamilies": {
"theme": [ "<theme values>" ]
}
}
}
}
The last phase of generating the stylesheet is converting the consolidated data into CSS style rules.
The styles section can be thought of as a structured representation of CSS rules, each chunk representing a CSS rule:
property: value).body selector.h1 or a).core/group becomes .wp-block-group) unless they explicitly set a different one using their block.json (core/paragraph becomes p). See the “Current limits” section for more about this.For example, the following theme.json structure:
{
"styles": {
"typography": {
"fontSize": "<top-level value>"
},
"elements": {
"h1": {
"typography": {
"fontSize": "<h1 value>"
}
}
},
"blocks": {
"core/paragraph": {
"color": {
"text": "<paragraph value>"
}
},
"core/group": {
"color": {
"text": "<group value>"
},
"elements": {
"h1": {
"color": {
"text": "<h1 within group value>"
}
}
}
}
}
}
}
is converted to the following CSS:
body {
font-size: <top-level value>;
}
h1 {
font-size: <h1 value>;
}
p {
color: <paragraph value>;
}
.wp-block-group {
color: <group value>;
}
.wp-block-group h1 {
color: <h1 within group value>;
}
From the settings section, all the values of any given presets will be converted to a CSS Custom Property that follows this naming structure: --wp--preset--<category>-<slug>. The selectors follow the same rules described in the styles section above.
For example, the following theme.json
{
"settings": {
"color": {
"palette": {
"default": [
{
"slug": "vivid-red",
"value": "#cf2e2e",
"name": "Vivid Red"
}
],
"theme": [
{
"slug": "foreground",
"value": "#000",
"name": "Foreground"
}
]
}
},
"blocks": {
"core/site-title": {
"color": {
"palette": {
"theme": [
{
"slug": "foreground",
"value": "#1a4548",
"name": "Foreground"
}
]
}
}
}
}
}
}
Will be converted to the following CSS style rule:
body {
--wp--preset--color--vivid-red: #cf2e2e;
--wp--preset--color--foreground: #000;
}
.wp-block-site-title {
--wp--preset--color--foreground: #1a4548;
}
In addition to the CSS Custom Properties, all presets but duotone generate CSS classes for each value. The example above will generate the following CSS classes as well:
/* vivid-red */
.has-vivid-red-color { color: var(--wp--preset--color--vivid-red) !important; }
.has-vivid-red-background-color { background-color: var(--wp--preset--color--vivid-red) !important; }
.has-vivid-red-border-color { border-color: var(--wp--preset--color--vivid-red) !important; }
/* foreground */
.has-foreground-color { color: var(--wp--preset--color--foreground) !important; }
.has-foreground-background-color { background-color: var(--wp--preset--color--foreground) !important; }
.has-foreground-border-color { border-color: var(--wp--preset--color--foreground) !important; }
/* foreground within site title*/
.wp-block-site-title .has-foreground-color { color: var(--wp--preset--color--foreground) !important; }
.wp-block-site-title .has-foreground-background-color { background-color: var(--wp--preset--color--foreground) !important; }
.wp-block-site-title .has-foreground-border-color { border-color: var(--wp--preset--color--foreground) !important; }
By default, the selector assigned to a block is .wp-block-<block-name>. However, blocks can change this should they need. They can provide a CSS selector via the __experimentalSelector property in its block.json.
If blocks do this, they need to be registered in the server using the block.json, otherwise, the global styles code doesn’t have access to that information and will use the default CSS selector for the block.
Every chunk of styles can only use a single selector.
This is particularly relevant if the block is using __experimentalSkipSerialization to serialize the different style properties to different nodes other than the wrapper. See “Current limitations of blocks supports” for more.
Similarly to block supports, there can be only one instance of any style in use by the block. For example, the block can only have a single font size. See related “Current limitations of block supports”.
The global styles UI in the site editor has a screen for per-block styles. The list of blocks is generated dynamically using the block supports from the block.json of blocks. If a block wants to be listed there, it needs to use the block supports mechanism.
In addition to styles at the individual block level and in global styles, there is the concept of layout styles that are output for both blocks-based and classic themes.
The layout block support outputs common layout styles that are shared between blocks used for creating layouts. Layout styles are useful for providing common styling for any block that is a container for other blocks. Examples of blocks that depend on these layout styles include Group, Row, Columns, Buttons, and Social Icons. The feature is enabled in core blocks via the layout setting under supports in a block’s block.json file.
There are two primary places where Layout styles are output:
Base layout styles are those styles that are common to all blocks that opt in to a particular layout type. Examples of common base layout styling include setting display: flex for blocks that use the Flex layout type (such as Buttons and Social Icons), and providing default max-width for constrained layouts.
Base layout styles are output from within the main PHP class that handles global styles, and form part of the global styles stylesheet. In order to provide support for core blocks in classic themes, these styles are always output, irrespective of whether the theme provides its own theme.json file.
Common layout definitions are stored in the core layout block support file.
When a block that opts in to layout support is rendered, two things are processed and added to the output via layout.php:
is-layout-flow is for blocks (such as Group) that use the default/flow layout, and is-content-justification-right is added when a user sets a block to use right justification.wp-container-$id where the $id is a unique number.There are currently four layout types in use:
block. The spacing between children is handled via vertical margins.contentSize and wideSize values set in settings.layout in the theme.json.gap CSS property.auto-fill approach to column generation but can also be set to a fixed number of columns. Spacing between children is handled via the gap CSS property.For controlling spacing between blocks, and enabling block spacing controls see: What is blockGap and how can I use it?.
The layout block support is designed to enable control over layout features from within the block and site editors. Where possible, try to use the features of the blocks to determine particular layout requirements rather than relying upon additional stylesheets.
For themes that wish to target container blocks in order to add or adjust particular styles, the block’s class name is often the best class name to use. Class names such as wp-block-group or wp-block-columns are usually reliable class names for targeting a particular block. In addition to block and layout classnames, there is also a classname composed of block and layout together: for example, for a Group block with a constrained layout it will be wp-block-group-is-layout-constrained.
For targeting a block that uses a particular layout type, avoid targeting wp-container- as container classes may not always be present in the rendered markup.
Work is currently underway to expand stable semantic classnames in Layout block support output. The task is being discussed in this issue.
The current semantic class names that can be output by the Layout block support are:
is-layout-flow: Blocks that use the Default/Flow layout type.is-layout-constrained: Blocks that use the Constrained layout type.is-layout-flex: Blocks that use the Flex layout type.is-layout-grid: Blocks that used the Grid layout type.wp-container-$id: Where $id is a semi-random number. A container class that only exists when the block contains non-default Layout values. This class should not be used directly for any CSS targeting as it may or may not be present.is-horizontal: When a block explicitly sets orientation to horizontal.is-vertical: When a block explicitly sets orientation to vertical.is-content-justification-left: When a block explicitly sets justifyContent to left.is-content-justification-center: When a block explicitly sets justifyContent to center.is-content-justification-right: When a block explicitly sets justifyContent to right.is-content-justification-space-between: When a block explicitly sets justifyContent to space-between.is-nowrap: When a block explicitly sets flexWrap to nowrap.Layout styles output is switched on by default because the styles are required by core structural blocks. However, themes can opt out of generated block layout styles while retaining semantic class name output by using the disable-layout-styles block support. Such themes will be responsible for providing all their own layout styles. See the entry under Theme Support.