Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document experimental theme.json #22518

Merged
merged 8 commits into from
May 22, 2020
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions docs/designers-developers/developers/themes/theme-json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Themes & Block Editor

Copy link
Contributor

@youknowriad youknowriad May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we should add a sentence here to clarify the experimental state of the APIs and that they're subject to change at any moment (like the block-based theme doc)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about 7f82de8 ? Mostly copied verbatim from the block-themes page.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that the link you intended to share? It points to this same thread so I'm not sure I follow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's the comment bout the FSE requirement :)

This is intended to document 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

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](https://make.wordpress.org/core/2020/01/23/controlling-the-block-editor/), or [a block style system](https://github.com/WordPress/gutenberg/issues/9534) 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.

When this file is present and the Full Site Editing experiment is enabled, a few Block Editor mechanisms that are activated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to limit the theme.json to FSE?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so there are two things here:

  • The FSE experiment needs to be enabled for this to work. I liked that as it follows what the other experiments are doing. However, one alternative would be to enable this if there's an experimental-theme.json, independently of the FSE experiment status. Would that make more sense? I guess if a theme provides that file it knows what's doing, so it should be fine. I don't have a strong opinion on this.

  • The stylesheet is only enqueued for front end & site editor. I wondered if we could open this to the other editors as well (post & widgets).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, one alternative would be to enable this if there's an experimental-theme.json, independently of the FSE experiment status. Would that make more sense?

Yes, that was my thinking, I see value for this for regular themes too.

The stylesheet is only enqueued for front end & site editor. I wondered if we could open this to the other editors as well (post & widgets).

Definitely, especially "post". What's the reasoning for loading this in the site editor page only?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Draft PR to open this up #22520

What's the reasoning for loading this in the site editor page only?

Just being extra-cautious about which contexts had this loaded. With the current direction (enable features, etc), it makes more sense to open it up.


### Presets become CSS Custom Properties

Presets such as [color palettes](https://developer.wordpress.org/block-editor/developers/themes/theme-support/#block-color-palettes), [font sizes](https://developer.wordpress.org/block-editor/developers/themes/theme-support/#block-font-sizes), and [gradients](https://developer.wordpress.org/block-editor/developers/themes/theme-support/#block-gradient-presets) will be enqueued as CSS Custom Properties for themes to use.

These are enqueued for both in the front-end and the site editor.

### 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).

### 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. The goal is that these are also available for themes to control.

## 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: { ... }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have a small concern about the "presets" and "features".

For instance to disable colors entirely you'd have to do:

presets : {
   color: []
},
features: {
   customColors: false,
}

So two separate locations to "control" the editor.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree, that'd be not ideal. This is not implemented yet but the way I imagine this could work is by compressing it to:

presets : {
   color: false
}

Copy link
Contributor

@youknowriad youknowriad May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's still two different top level keys to disable a feature though. it's not the value itself that bothers me.

}
```

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: {
dropCap: false
}
},
core/paragraph: {
features: {
dropCap: true,
}
},
core/image: {
features: {
dropCap: true
}
}
}
```

In the example above, we aim to encapsulate that the drop cap should be disabled globally but enabled in the paragraph context. The drop cap in the image context wouldn't make sense so should be ignored.

## Current Status

### Presets

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:

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

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

```css
: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 so far 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:

```php
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`.

### 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.

The list of properties that are currently exposed via this method are:

- Paragraph and Heading: line-height, font-size, color.
- Group, Columns, and MediaText: color.

### Features

This is being implemented, so it's not currently available.

### Recap of current available functions

```
{
global: {
presets: {
color: [
{
slug: <preset slug>,
value: <preset value>,
},
{ ... },
],
font-size: [
{
slug: <preset slug>,
value: <preset value>,
},
{ ... },
],
gradient: [
{
slug: <preset slug>,
value: <preset value>,
},
{ ... },
]
}
},
core/paragraph: {
styles: {
line-height: <value>,
font-size: <value>,
color: <value>,
}
},
/* core/heading/h1, core/heading/h2, etc */
core/heading/h*: {
Comment on lines +199 to +200
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be exceptional behaviour in that it breaks the global|<blockName> model as defined earlier in the document. Can we clarify how this will work, how and whether block types can define these exceptions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this is something that needs untangling. I see a few things here. They are related, although can be separate discussions as well. Here's the current direction and some thoughts about it:

  1. There are blocks that represent multiple HTML elements.

The heading block represents 6 HTML elements (h1-h6). That variation is captured as a block attribute, levels. I don't know any other block that does the same, although I can see potential for more cases. For example, a block that wanted to use <a> or <button> depending on some attribute.

There's another aspect here that I'm unsure how/whether plays with this: block variations. As far as I've seen, the block variations in use don't represent structural changes, but, they could if the block author uses them for that.

  1. How blocks (that represent multiple HTML elements) should register their selectors.

How any block register its selector is not documented. An old iteration of the managed CSS PR proposed using a new key selector within the block.json. At the moment, this data is hardcoded until we're able to register blocks in the server and consolidate the mechanism.

This is how the proposal looked like for the paragraph block:

// block.json
{
  "name": "core/paragraph",
  "selector": "p"
}
// theme.json
{
  'core/paragraph': {
      styles: ...
  }
}

The global styles mechanism knew how to match both pieces of data, the theme.json block name to the block.json selector.

This is fine for most blocks, but then we face blocks that represent multiple HTML elements, such as heading. This is what I proposed at the time:

// block.json
{
  "name": "core/heading",
  "selector": {
    "h1": "h1",
    "h2": "h2",
    "h3": "h3",
    "h4": "h4",
    "h5": "h5",
    "h6": "h6"
  }
}
// theme.json
{
  'core/heading/h1': {
      styles: ...
  },
  'core/heading/h2': {
      styles: ...
  },
  ...
}

Essentially, each block.json selector key creates a new block name in theme.json: core/heading/h1, core/heading/h2, etc. From the block.json perspective, I don't see many alternatives that fit with the "selector metaphor" nicely. There's something to say about the theme.json structure, though.

  1. How theme authors target blocks that represent multiple HTML elements in theme.json

The current proposal is:

// theme.json
{
  'core/heading/h1': {
      styles: ...
  },
  'core/heading/h2': {
      styles: ...
  },
  ...
}

However, we could do differently. Some alternatives that I've considered are:

  • using a new key
// theme.json
{
  'core/heading': {
    tag: 'h1',
    styles: ...
  },
  'core/heading': {
    tag: 'h2',
    styles: ...
  }
}
  • using nesting levels
{
  'core/heading': {
    h1: {
        styles: ...
    }
    h2: {
        styles: ...
    },
    ...
  }
}

The current proposal seemed clearer to me, both from the human and computer perspective.

Does this create any new thoughts/heps to advance the conversation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I've seen, the block variations in use don't represent structural changes, but, they could if the block author uses them for that

It's not exposed through any visual option yet, but the Group block has an attribute for changing which tag it uses. (So you could use it for <section>s or <aside>s.)

I think it's worth noting that, at least at some point (I don't know if it's still the case), it was thought that the block editor could be used in contexts where the output isn't HTML. I wonder if maybe some of the proposed ideas may be too HTML-specific. I suppose heavy reliance on HTML also causes problems for the mobile app.

As for the Heading block, I really don't think it makes sense to target h1-h6 elements through it. h1-h6 elements can and often will occur outside of Heading blocks. Theme authors ought to target h1-h6 elements as they normally would.

It's worth noting that there are variations in how blocks should be styled that depend, not on style variations, but an attribute that toggles whether a certain CSS class is added or not. The Latest Posts block has a grid and a list mode. The two should be styled very differently. But you can't use target either using style variations, because they aren't style variations. But the two modes also don't use different markup, if I recall correctly. The only difference is what CSS classes are applied.

But for a CSS class that is unique to a particular block, I don't think we should be targeting that class name in theme.json. I think instead, it would make more sense to target the block attribute. (This would have the benefit of not being CSS-specific, and it would allow us to change the specific class name used, since the theme.json wouldn't be referencing the class directly.)

I'm not sure exactly how/if this would work/look in practice. Just an idea I thought I'd share.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the group nudge, Zebulan. It led me to find another block that represents multiple HTML elements: the list block can use ul or ol.

For most blocks we have more specific selectors through the named class wp-block-{blockname}. However, there are a few for which we don't (paragraph, heading, list, and perhaps more) so we have to target the element. This is a source of headaches, but we also don't have another way that's back-compatible (unless we re-write block markup upon rendering the post).

As for not optimizing for HTML: I'd say that expressing the block attributes as a pair of property/value that is decoupled from the output (CSS) runs with that idea and may be helpful for other environments as well. Granted, right now, there's still a coupling between the block style attributes and CSS properties --- it doesn't need to be this way, though.

I'd put the latest post example in the same category of style variations. It seems the direction we're going towards is that style variations should be different sets of values for block style attributes (and not classes). Implementation-wise, we're far from that idea, but I have it in my background as something to explore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some PRs that have landed, I was able to put together #22698 which takes the block data (block selectors + block implicit attributes) from the block registry instead of being hardcoded.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned above, I've looked at the intersection of block variations and block selectors at #22805 (comment) TLDR: those two things seem to be independent.

styles: {
line-height: <value>,
font-size: <value>,
color: <value>,
}
},
core/columns: {
styles: {
color: <value>,
}
},
core/group: {
styles: {
color: <value>,
}
},
core/media-text: {
styles: {
color: <value>,
}
},
}
```