Repository navigation
Changes to theme.json #29891
Description
Part of #20331
@mtias @youknowriad @mcsf @nosolosw provided thoughts for this task.
As we approach the 5.8 cycle and the merge target for the theme.json API, it’s a good time to evaluate what is working and what needs some improvement. All in all, theme.json is responsible for providing configuration settings, global customization on things like text and links, and default style attributes for blocks. The first and last points seem to be fairly well established and their use fairly clear by themes, but the second point needs a bit more formalization.
The latest round of UI design iterations for the Global Styles sidebar puts a bit more emphasis on the style settings of cross-block elements. In light of this and the feedback gathered from themes so far there are a few aspects that can be improved if we clarify these three representations (settings, elements, blocks) a bit better.
To dive into this, let's explore an example of a theme.json that:
- (settings) provides a default color palette and a different color palette for the heading block
- (settings) disables the link color UI in the editor, except for the heading block
- (styles) provides a general link color and a specific link color for the heading block
- (styles) provides different font sizes for each heading configuration (h1-h6)
- (styles) provides a font size for the quote block
- (styles) provides background color and a different font size for links
With the current approach
{
"settings": {
"defaults": {
"color": {
"palette": [ /* default color palette */ ],
"link": false
}
},
"core/heading/h1": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
},
"core/heading/h2": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
},
"core/heading/h3": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
},
"core/heading/h4": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
},
"core/heading/h5": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
},
"core/heading/h6": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
}
},
"styles": {
"root": { // The "body" CSS selector.
"color": {
"link": "hotpink", // The "a" CSS selector within the body.
"background": "value"
}
},
"core/heading/h1": { // The "h1" selector.
"color": {
"link": "green",
"text": "value"
}
},
"core/heading/h2": { // The "h2" selector.
"color": {
"link": "green",
"text": "value"
}
},
"core/heading/h3": { // The "h3" selector.
"color": {
"link": "green",
"text": "value"
}
},
"core/heading/h4": { // The "h4" selector.
"color": {
"link": "green",
"text": "value"
}
},
"core/heading/h5": { // The "h5" selector.
"color": {
"link": "green",
"text": "value"
}
},
"core/heading/h6": { // The "h6" selector.
"color": {
"link": "green",
"text": "value"
}
},
"core/quote": { // The ".wp-block-quote" selector.
"typography": {
"fontSize": "value"
}
}
}
}
Some of the struggles of the current approach are:
-
We currently have two mechanisms to target DOM elements of a block:
core/heading/h1to targetH1elements andstyles.color.linkto targetaelements within the heading block. -
With the current approach theme authors can only set the color of the links but not the rest of its style properties (text-decoration, border, background-color, etc).
-
For blocks that use block selectors (
core/heading/h1, etc) there's no way to provide a style or setting for all the configurations of the same block (e.g.: presets forcore/headingor a particular color style for all headings). theme.json spec: use block names for settings & block selectors for styles #29773 -
The current API for settings & styles promotes block-level APIs while themes have expressed the desire of being able to set things across blocks. An example we've noticed is that current themes with theme.json support use
settings["defaults"]to target settings but notsettings["core/heading/h1"]― this is a hint about what are the primary and secondary use cases. See the latest round of mockups for the global styles sidebar as another case in which we want to provide an API to set styles across blocks. -
The current "root" and "defaults" selectors have been reported to be confusing. As we allow composing theme.json objects further (for example, to change the settings for a “page” template or a template part) the presence of a nested “root” object becomes more strange.
Being able to target blocks and elements
The proposal formalizes what we have been aiming to do for Links but extends it to other elements. This makes it more clear that, in addition to blocks, themes and the global styles UI can target specific cross-block elements. Instead of using core/heading/h1 we can use h1 directly (independently from the heading block) and instead of using styles.color.link we can target a elements directly.
The idea is that these elements are the same that were shared in the mockups for the global styles sidebar. Initially, these elements are set by core but plugins/themes (or even users through the style variations mechanisms) could be able to register custom ones in the future.
{
"settings": {
"color": {
"palette": [ /* default color palette */ ],
"link": false
},
"blocks": {
"core/heading": {
"color": {
"palette": [ /* heading color palette */ ],
"link": true
}
}
}
},
"styles": {
"color": { // Target the document (body).
"background": "value"
},
"elements": {
"link": { // This is the "a" css selector.
"color": {
"text": "green",
"background": "yellow" // NOT POSSIBLE BEFORE.
},
"typography": {
"fontSize": "value" // NOT POSSIBLE BEFORE.
}
},
"h1": { // This is the "h1" CSS selector.
"typography": {
"fontSize": "value"
}
},
"h2": { // This is the "h2" CSS selector.
"typography": {
"fontSize": "value"
}
},
"h3": { // This is the "h3" CSS selector.
"typography": {
"fontSize": "value"
}
}
// ... and so on
},
"blocks": {
"core/heading": { // This is the "h1, h2, h3, h4, h5, h6" CSS selector.
"elements": {
"link": {
"color": {
"text": "green"
}
}
},
"color": {
"text": "value"
}
},
"core/quote": { // This is the ".wp-block-quote" selector.
"typography": {
"fontSize": "value"
}
}
}
}
}
Note that:
- The distinction between
defaultsandrootis dropped: the settings & styles that lived within them are now top-level keys that target the document (settings => control the whole editor, styles => style the corresponding body/template part/etc). - Settings & styles can still address particular blocks, which are grouped under a
blockskey (so it's clear that's a secondary API). - There's a new
elementskey instylesto target the elements suggested by the global styles sidebar mockups, including links or H1-H6 DOM tags. elementscan be nested within a particular block but a particular block can't be nested within another block.- There’s a more clear separation within the style group between the configuration of blocks and the configuration of elements that occur across blocks.
Updating the theme.json shape
While the theme.json file is still experimental the plan is to make changes in a way that the plugin can support the two versions for a couple of weeks (a release or two, depending on the schedule) to both give time to adapt and to allow comparisons. Ultimately, only the version that lands in core will be supported going forward.
Activity
oandregal commentedon Mar 16, 2021
cc @carolinan @aristath @kjellr @scruffian @MaggieCabrera @jasmussen FYI.
aristath commentedon Mar 17, 2021
It feels like
styleswill now refer to selectors 🤔If that is the case, then this here is a bit confusing:
"link": { // This is the "a" css selector.Why not do it like
"a": { // This is the "a" css selector.?Would that not be more consistent with all the other things we have like
"h1": { // This is the "h1" CSS selector.?oandregal commentedon Mar 17, 2021
Ah, I see how some of those comments may be a bit misleading. Here's an attempt at clarification:
The serialization of an element to CSS is dependant on the context. This is to say that elements don't map to the same "thing" always: for example, the link element can be serialized as the
aCSS selector in one block and tobuttonin another block. It can also map to a CSS Custom Property--wp--style--link--color. It's going to depend on the context.Elements can be seen as "pieces" that form the blocks. Their goal is to give semantics to something that's very messy. Once blocks declare these selectors, the mockups are really powerful: users are not setting the color of an
aelement, they are setting the color of "links" which every block decides how it's serialized to the global styles stylesheet.The details about this need to be fleshed-out in the PR that refactors theme.json with support for the link element. I hope it becomes more clear over there.
oandregal commentedon Mar 18, 2021
I've started to work on this.
carolinan commentedon Mar 26, 2021
The separation of elements and blocks is clear. I do worry about the size of the file and how to keep it readable.
So it is possible to both target H1, heading block H1 and Site title
but not h1.sitetitle or a site title H1 block inside a header template area, or a post-author inside a query, correct?
oandregal commentedon Apr 28, 2021
#30541 to implement this is at the latest stages (polishing, fixing some bugs).
dogooddesignco commentedon May 2, 2022
This might be a different issue, I don't have much experience raising issues on github, but now when I use the "elements" object in theme.json, those settings are inlined in the editor styles, and then my custom editor styles are loaded after this, which overrides the settings in theme.json. For example, I have the following in my theme.json:
`"elements": {
However editor stylesheet begins with a reset which sets all heading levels to
font-size: inherit;So, in the editor my headings are all the base font size, since the reset overrides the styles from theme.json.
However on the frontend, the order is reversed and the styles from the theme.json file take precedence (which is my desired behavior).
There seems to be an inconsistency in the order in which these styles are loaded in the editor vs the frontend.
Side-question: is it possible to still use the old method of targeting "core/heading/h1" in the "styles" object? When I try it, it has no effect.