-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Proposal: Standardized block markup, theme.json design tokens, and CSS classes to improve interoperability #38998
Comments
Thanks for all your thinking and work on putting this together @mrwweb - I wonder if this would be better as a github discussion rather than an issue in order to allow some threading of ongoing discussion on the various points? |
@glendaviesnz I'm unclear about the best uses of Github discussions both in general and within the Gutenberg repository. I don't want it to get lost or buried—a couple really interesting discussions seem to have very little engagement. I'm open to whatever forum/format works best for generating robust discussion and moving these ideas toward adoption. |
Good call, the discussion side is newer and doesn't seem to get as much visibility at the moment. Let's just leave it here for now for visibility as you say, we can always fork off separate discussions for specific areas if needed. This is really useful feedback by the way, combined with the blog post. I have discussed it with the rest of my team this morning, and we are keen to come back to you with some feedback as it impacts some of the areas we are working on, but it may take us a day or two to think it all through properly. |
I'm super excited about this idea in general. The lack of this kind of standardization has been a huge pain point with Gutenberg and honestly had us looking at other CMS options. Some further thoughts from the perspective of an agency developer writing custom themes for clients: Themes should be able opt out of using some or all of these design tokens.A theme that is all in on bold typography should be able to remove lighter font weights from the UI, and should also be able remap those variables/classes to supported ones. This would ensure the site's existing content conforms to the new theme's design, along with any block patterns or plugins that use design token-based variables/classes. Themes should be able to opt out of using any custom color/size values, including within existing content.This should include any instance of the editor outputting inline styles, even stuff like column widths. The themes we create basically never use custom size values, because we want the ability to guarantee design consistency and the ability to revise these values later. It also makes the editing experience much more approachable, as @mrwweb mentions. The editor also needs to handle existing custom values much more elegantly. The way they work right now makes the block pattern directory essentially useless for themes that want to avoid custom values. For example, existing custom font size values in site content continue being used, even when a theme that disables custom font sizes is active. The only way to know they're present is inspecting the front end, and the only way to remove them is to use the code editor view to delete them from the block by hand. When the active theme has disabled custom values and the editor encounters an existing one it should either remove it, ignore it, or attempt to map it onto the closest preset value. Core blocks need much more stable and conservative HTML and CSS.Each core block's HTML and associated CSS need to be much more carefully considered for how it will fit into the larger theme ecosystem, and any change to markup or removal of CSS classes needs to be treated as a breaking change. The position outlined in The Block - Theme contract puts handcuffs on theme developers. HTML structure and class names need to be implemented with an eye for overall usability and to avoid future breaking changes. It's ridiculous that the cover block refactor shipped with the class name Similarly, core block CSS should use a single class as the selector as often as possible (as mentioned by the OP), and when that's not possible any changes to CSS should take existing specificity into account and ensure it doesn't increase. Right now, many core styles use extremely elaborate selectors which are often difficult for theme authors to override, and these selectors often become more specific when updated, breaking existing theme customization. This will probably require additional (and possibly duplicated) classes throughout each block, but that's preferable to writing nightmarish selectors like "Intrinsic" responsive design should be used instead of media queries.This has been discussed before: Traditional media queries have always been an awkward fit for blocks since they almost never span the width of the browser window, and thus can't actually be adjusted based on their width on the page. For example, it's basically impossible for the same set of media queries to elegantly handle these two columns blocks, which are internally identical. Container queries are the answer to this problem, but until they are widely supported enough core CSS should use "intrinsic" responsive design techniques as widely as possible. On my themes I've replaced the core column block's CSS with something inspired by Every Layout's Switcher, and it's generally much easier to deal with. Themes should be able to offer preset "packages" of block options.I dug into this in a separate issue, but the idea would also be appropriate for dimension values like spacing: This feature and the ability to set multiple block styles (#14598) would address so many of the issues I run into when using core blocks. Token additions and changes(aka, getting into the weeds)
|
Some more discussion about this here https://wptavern.com/the-case-for-a-shared-css-toolkit-in-wordpress |
I agree in principle to this and have been using a system similar to this for many years. It's essential to allow flexibility: using a key like |
So, I'm just going to throw in my system for semantic naming below. I created much of this model based on Tailwind and discussion with other theme developers about three years ago. I have been using it since then (obviously without the It is not necessarily better or worse than any other system. But, it has at least given me some comfort not having to figure out what to name things. I can just plug in values and move onto other design issues I need to take care of. The following is presented using CSS properties (e.g., Font SizesFor things where there is a sort of "middle" value that exists as the base, I prefer to use a T-shirt sizing system. This way, you can still semantically add more in either direction from the base.
Line HeightsI almost always disable this for users and tie my line heights directly to the font sizes. I also disable the ability to customize the font size, so users are always getting a paired font size and line height. However, if I am adding in specific presets, I would follow a stepped approach as explained the Margin/Padding/Gap section below. Font Weight and StyleIf adding custom classes for these, this should be simple to name. Just use follow what CSS does. Something along these lines:
Font FamiliesMost designs will have between 1 and 3 font families. More often than not, there is a specific font for headings, so I have separated it. Then, there is a primary/copy font for most other text on the site. And, in the case for secondary or less-used text, I have covered it too. I also create a set of always-existing types just in case a user would, for example, want to always have some text in sans, serif, monospace, etc.
Margin / Padding / GapI typically use a stepped-number approach here:
I usually calculate these off a base number, so it's something like:
I also usually add in a "global" spacing (sort of like blockGap but for everything):
One solution to this is allowing theme authors to declare the "spacing" values and WP automatically creating the Content WidthWe need a larger range of options than I'd definitely love to see a system with a range of sizes. I haven't really thought much on how to name these. But, I have turned to Tailwind's ColorsI think colors will be the toughest thing to agree on. There are so many different systems and methods. Plus, projects can vary so much in this area. It would be easier to create a standard naming scheme for other areas and not let colors become a blocker. I closely follow the Tailwind model with colors. I create a primary, secondary, and neutral set of colors with that range from light to dark (100 - 900). In cases where I use a smaller range, I might skip the evens (200, 400, etc.).
|
I definitely agree on the need for extra width values beyond For margin/spacing/padding, I’d suggest going with For font weight, however, I think using Color is definitely hardest. I see the merit in the Tailwind-style color model, but I’m not sure it’s the best fit for Wordpress/Gutenberg. Tailwind uses PurgeCSS to avoid shipping redundant unused CSS for colors that are never used, and there’s not really a good way for Wordpress to do that right now. Even discounting that, showing 9 color variations per color would get really overwhelming in the editor UI. I think it would be better to settle on a standard descriptive set of color names to use (ex.: text, background, highlight, accent, link/button/“action”, etc.), along with possible variations (ex: light/dark, maybe active/hover?). |
This is fantastic! I made a similar issue related specifically to spacing awhile back: #34210 (which I'm now going to close in favor of this one). Creating standard options for our users is really important, rather than just giving an open field with which they can adjust things like spacing using any value they like. I also agree that colors shouldn't be gated behind this toolkit - naming conventions are just going to be all over the place. |
Big thumbs up for this proposal!!! As a theme author I believe these kind of standardizations and improvements would make it much easier for all of us 😍 I would like to add that it would helpful to have added CSS classes for standard block supports like border styles, e.g. border-dashed, border-solid etc. as they are only added as inline styles at the moment. I hope it's ok to add this as a remark here. |
While we're at it, may I suggest using data attributes to style them accordingly? It might help solve some of our backward compatibility issues. We already have this Since it's a new API, there should be no (minimal) compatibility issues, and it also makes it easier to distinguish between public and internal APIs, while most class names should be considered internal. Or we can come up with a prefix for the class names to indicate clearly that they are public APIs, and anything else should remain internal for styling purposes only. I don't have any concrete idea for that yet unfortunately 😅 . |
Please don't use “HTML5 is designed with extensibility in mind for data that should be associated with a particular element but need not have any defined meaning.” “Custom data attributes are intended to store custom data private to the page or application, for which there are no more appropriate attributes or elements.“ |
As far as private vs public API I think it would make more sense to use the "style engine" to add generated classnames that could be used for the styling coming from core. But at the same time leave all the state describing class names as the public API. So each block would have markup that looks like this: <div class="wp-container-XXXXX wp-block-namespace has-background is-style-x ...">
...
</div> |
See also #36135 which mentions the serious issues caused by the overly-specific inline CSS markup. |
I wanted to note that this topic was discussed during today's Core Editor chat on Core WordPress Slack (requires registration). One thing to note - there is a potentially related effort on a Styles Engine project which seeks to improve how block styles are handled. |
I wonder if the CSS layers specification could be useful here: /* Create the layers, in the desired order */
@layer base, theme;
@layer base {
/* Append to 'base' layer */
h1.title {
font-size: 5rem;
}
}
@layer theme {
/* Append to 'theme' layer */
h1 {
font-size: 3rem;
}
} It isn't supported everywhere unfortunately, and some time might pass until it is. |
Introducing layers would really simplify the specificity wars going on in the editor at the moment. But this would only be an amazing addition, not a replacement for what has been proposed here. |
Thank you @mrwweb for putting the time and effort in gathering all these thoughts and looking through the history and past conversations. It's wonderful to see the attention and deep consideration here. My reply is going to be a bit long, so I apologize in advance. One thing that doesn't seem explicitly clear in the issue formulation is that it's primarily focused in providing a better experience for the aspects a theme wants to customize that is not part of the user interface (the theme contract, the 80/20) as referred to in this conversation.
Let me know if I'm over-interpreting there! This is important context since achieving the right balance — one that allows the editor as a creative tool to flourish and themes to have the space to be as expressive as they need to be — depends on conceptualizing that properly. That balance is quite tricky!
We should probably add "outside the UI" here since it aims to resolve the portion of style updates that is not handled by theme.json. In that regard we have to thread carefully with its implications because there are things currently not handled because they have not been implemented (for example, styling captions across blocks) and others not handled because they probably never will. The architecture of styles that are meant to be handled by theme.json have additional requirements to those that are meant to be modified by themes alone. For example, they cannot be expressed exclusively as CSS variables because we need them to be more agnostic in its declaration in order to support them across web editors, front-end, mobile apps, etc. This goes hand in hand with the consideration that changes a theme makes based on classes alone can be hard to transfer to other editing contexts. Even if in most cases editor styles can be supplied, mobile wouldn't know what to do with it. Furthermore, while editor styles can be supplied to editing contexts, it also implies the loading of CSS in anticipation of that CSS being used at all, which means sites would often be paying a performance penalty. If we go about this wrong, themes also might end up thinking classes are a first-class API for the things that are meant to be covered declaratively. Another way of looking at this group is as the subset of style properties that are meant to be interoperable between the theme and the user. This interoperability is crucial because it also gives site maintainers the leverage to disable or restrict whatever it deems appropriate. Custom theme styles driven purely by the use of classes will be opaque to the user and the system as a whole. This is not a problem in itself when characterized as the things a user won't have UI access to modify anyways. In that sense:
We need to separate what may be temporary states from fundamental design principles. The container specific classes are mostly there to ensure we have a system that will be capable of handling specific styles rendered when a given block is actually on the page. It's not an indictment of how generic container primitives should work, which can and should for the most part have a more sensible or semantic class representation (whatever that is). The list of issues captured in the longer post includes the removal of some relatively semantic classes that we should probably just treat as back-compat bugs. Classes, including utility ones, are also opaque to the system. They can be an alright artefact but cannot function as an interoperable API. Utility classes are also tricky because they rely on certain conventions to work properly and can be disrupted easily by misuse. Generally, plugins will have higher success and a more stable foundation interacting with the json properties of the theme and the elements APIs and that should be recognized. (For example, an Ads plugin that wants to ensure certain elements are styled to what the theme / site needs using elements like text and links, both generally and block-specific.) The situation with semantic classes is very nuanced as well. When we say “Large” or “+2” is more semantic than a discrete value we have to be careful around the user expectation. A user may not be choosing "Large" because of its semantic value but because it looks good to them. I think there is a distinction that may seem pedantic but is worth doing — there's some design tokens that are semantic and others that are merely encapsulations of discrete values. We need to be careful to not pass an encapsulation (which surely has value in terms of portability, etc, and we should still pursue) as a semantic element. The color palettes split between default colors and theme colors is a good example, which you also touch upon in the appendix when it comes to how many colors makes sense to have semantic value (primary, secondary), when that stops making sense (quaternary, etc), and when the colored-labeled classes can start making more sense (particularly for patterns!). Even though these are defined as classes ( This is also a layer orthogonal to the existence of Elements which are more clearly semantic (like — There's a side note here related to the discussion in #38918 that speaks to these considerations: the colors defined in a theme palette might not actually be used for semantic elements, so they may not actually be changing how the site looks to a user! That's why the previews render the site background, text, link colors instead, which may or may not be mapped to theme palette colors, depending on the needs of the site, user, and theme. I am in favor of establishing more sets of encapsulated values but I think it's important to not conflate Elements with these tokens / variables. There's also another distinction to be made between what a value resolves to and what the UI might be exposing. This is super relevant for responsive typography, where what seems like discrete values might be resolving to calc functions or token primitives. The interface could have discrete controls yet still resolve to a tokenized value in some circumstances. The two may often be paired but not necessarily. We also need to be careful with the proliferation of disconnected tokens. Often a "large" value for margin might not be actually adequate because it bears no declared relationship to other tokens that affect spacing. In some cases the token would need to be a composition of other primitives (like base-grid, or base-spacing unit, etc). This is obviously solvable, but we should be upfront about it. A nice concrete example is the Space Increment property in a tool like https://hihayk.github.io/shaper/ In that sense, I'm not sure whether properties like
This also touches upon what constitutes a user decision in practice. When a user picks a "green" color that is part of the theme "semantic" palette, are they picking it because they like the tone or because they have a semantic intention? Upon theme switching, would they expect it to change to the primary value of the new theme or be retained? This is in some ways largely unsolvable, but I think the separation of theme palette from color-specific palette has been a good step in providing users more clarity. We can still do better there.
I tend to fully agree with the latter but not necessarily with the former, particularly in the details of its implementation. For example, I think we should avoid serializing classes for inner elements as much as possible and we should do it only when there's consistent semantic value being provided through them. This is hard to make into a general rule and would need to be more nuanced for each block core offers. It's also something hard to assume for third party blocks. Obviously the coupling it creates on markup shape would become more difficult to maintain so we need to ensure we are not solidifying poor or temporary markup choices excessively. I think there's also something prior to that which is ensuring all blocks output I know the proposal means to exclude these elements but I think we should revisit some of them. At least lists have caused some issues in the past since they are a very flexible element that is used to construct many different things and can be leaky. In any case, the exception of There's a delicate balance between giving themes granular hooks and deteriorating the semantics of user content. I agree with the reason "keep selector specificity of core CSS low" in general.
This is alright, especially if it's not serialized!
I'd generally say yes to this, with the caveat that design tokens are not necessarily semantic and we should not conflate the two things. Elements might have a double representation as class names connected to a design token but both should be opaque to block authors. For example, "caption" should be stablished as an element that blocks can use as a component
Outputting utility classes as encapsulations for tokens is alright, but we should ensure we don't serialize them since they are just an implementation detail of block attributes and the styles engine. We should also be mindful that utility classes that have a representation in Generally, while it'd be fine to establish some tokens and classes as an API for themes to extend in that last portion of customization outside of theme.json, it's also crucial to be upfront that they are not idiomatic to the block API on their own. With that I mean that a block author can add one of those without going through the proper mechanisms, which can make them inoperable in some conditions for no visible reason. That's why utility classes need to be an artefact, an implementation detail of other more declarative tools in the block API toolset, otherwise it'd be really hard to ensure proper functioning across platforms (editors, web, mobile). Proliferation of utility classes can also become a burden if served as a monolithic stylesheet that is indifferent to the block style pipeline. Tools like Tailwind generally get around this problem through build steps that ensure only what's actually used gets enqueued. Classes that are generated by the style engine would have the same capability but a more naive approach that just gathered all of them in a Finally, Background and Foreground are not meant to be color palette tokens but proper Elements defined as part of the block API ( We can continue to discuss some of the holistic implication here but I think it'd be good to be extracting some actionable items already. |
Thank you @mtias for these detailed notes. There is a lot of great insight in there and I really appreciate the perspective. Ont thing that jumps out to me directly is that I would love to better understand what exactly you mean when you say:
I'm probably missing some context somewhere but I'm not sure I fully understand what you mean by |
Hey peeps 👋 there's a lot very thoughtful discussion here around CSS classes and naming in general, so I thought it might be a good place to seek some feedback on my experiment in #42763, where I'm moving the content width logic into its own layout type, to make it possible for blocks to apply content width to their children by default. The main thing I'm seeking feedback on is the name for this new layout type: I provisionally called it "column" but don't really like that as it can be confused with the Column block. I'm currently leaning towards "center", because what that layout type does is create a center-aligned column of content. It would be great to have some more thoughts on this! For reference, the layout type name is exposed as a block classname ( |
Good question @sascha-bleech . Currently, apart from 'default', we also have 'flex', which has horizontal and vertical variations, and a bunch of alignment options too. In the future we may have other layouts such as 'grid', and perhaps something that allows for absolute or fixed positions. What we're trying to do with 'center' is split out the content width logic that currently is part of 'default' into its own layout, so that it's possible for blocks to have content width enabled out of the box.
That's an interesting idea! The current work won't impact the possibility of implementing something like that in the future, though the name 'center' might not be the best if we were to enable right or left alignment for the content. It's hard to find a good single word description for this 😅
|
Yeah, "constrain" or (preferably) "constrained" sounds like a much more descriptive title to me... at least after you realize what it means, anyway. There's certainly a lot less room for confusion in the long-run than a name like "column". |
The other options/variations all sound very CSS like: flex, grid, fixed, absolute... "Constrain" sounds too negative/constrained to me ;-) |
Thanks for the feedback everyone! I decided to go with "constrained" as it best describes the specific layout type.
That wasn't on purpose; it would be preferable not to name them after their implementation details 😅 |
@tellthemachines I'm late to the party, but hopefully not too late!
I wonder whether the answer is simpler than that and more or less answered:
As many people have pushed hard for on #33374, I think there's a general feeling that the centered and constrained alignment should be the default alignment both for top-level blocks and container blocks like Group, Cover, and even Column blocks. So for a name, I would propose "default", and because of that I wouldn't expose that as a UI option if it can just be the default state of all block containers. Giving this a name (and a class that can be applied to block containers) is an awesome idea and so I'm glad you're doing that work! |
I proposed the same here: #42763 (comment). There was a bit more conversation beyond that you might find informative. It seems |
Cascading layers seem to be widely supported by now: https://caniuse.com/css-cascade-layers Encapsulating all WordPress/Gutenberg-generated CSS into layers would make custom theme development so much easier as we would not need to match the specificities all the time. Because layered CSS cannot overrule non-layered CSS developers can't benefit from cascade layers until the core uses them as well. |
@krokodok Cascade layers feels like a good new issue to open and would likely help with some of the goals mentioned in this thread. I'd encourage you to open that and bonus points for summarizing the conversation that's already happened on other issues and one discussion. |
With the spacing scale (#35306) now out in the wild, I wanted to show how it can make themes and plugins more interoperable today. I hope that plugins take advantage of techniques like this and folks will be inspired to further action on standardizing colors, font-sizes, etc. Background: I have a set of files I use to make The Events Calendar (TEC) plugin inherit styles from my themes. Because TEC uses custom properties, I can override their values to match my theme better. Instead of using the plugin default, I can remap the TEC spacing values to use the body {
/* Remap The Events Calendar spacing properties to theme.json presets */
/* custom property fallbacks are the values used in TEC */
/* Note: spacing scale is slightly collapsed to accommodate the number of default spacing presets */
--tec-spacer-0: var(--wp--preset--spacing--10, calc(var(--wp--preset--spacing--20) / 2), 4px);
--tec-spacer-1: var(--wp--preset--spacing--20, 8px);
--tec-spacer-2: var(--wp--preset--spacing--30, 12px);
--tec-spacer-3: var(--wp--preset--spacing--40, 16px);
--tec-spacer-4: var(--wp--preset--spacing--40, 20px);
--tec-spacer-5: var(--wp--preset--spacing--50, 24px);
--tec-spacer-6: var(--wp--preset--spacing--50, 28px);
--tec-spacer-7: var(--wp--preset--spacing--60, 32px);
--tec-spacer-8: var(--wp--preset--spacing--60, 40px);
--tec-spacer-9: var(--wp--preset--spacing--70, 48px);
--tec-spacer-10: var(--wp--preset--spacing--70, 56px);
--tec-spacer-11: calc((var(--wp--preset--spacing--70, 56px) + var(--wp--preset--spacing--80, 80px)) / 2);
--tec-spacer-12: var(--wp--preset--spacing--80, 80px);
--tec-spacer-13: var(--wp--preset--spacing--90, calc(var(--wp--preset--spacing--80, 80px) * 1.2));
--tec-spacer-14: var(--wp--preset--spacing--100, calc(var(--wp--preset--spacing--80, 80px) * 2));
} There are some specifics to the above code where I've had to do some things to map a smaller What's exciting to me is that there's no reason The Events Calendar couldn't just do this themselves and have their plugin styles automatically match the default spacing of a theme. Plugins can start doing this today as long as they provide fallbacks. Default WordPress comes with 20 - 80 defined on the spacing scale. Form plugins, events plugins, slideshow plugins, etc, could all really benefit from using this. TEC similarly uses an "accent" color which would be a prime candidate for grabbing directly from a palette of standardized color slugs. Interestingly, TEC would also benefit from the ability to easily reference additional values like Conclusion: I hope this shows off the value of standardizing "design tokens" via |
What problem does this address?
I have written a blog post describing the issues this proposal addresses in detail.
The overall goals are to:
The need for a clear direction and solution along these lines has come into focus recently:
The background and reasoning behind each decision along with examples are provided, so please read the full proposal if you want to leave a detailed comment.
This proposal brings together multiple different threads of conversations because I think it's important that each change is considered in the context of the others. When they come together, they illustrate a cohesive vision for a vibrant WordPress ecosystem benefiting all users and developers.
What is your proposed solution?
This is a four-part solution that builds on existing practices already in WordPress while committing to full block/setting coverage and backwards compatibility:
wp-block-media-text
,wp-block-media-text__content
,wp-block-media-text__media
,wp-block-media-text__image
) and use single-class selectors in core CSS when styling them so they are easy to overrideis-vertically-aligned-center
,has-background
,is-style-{name}
,alignwide
)theme.json
design tokens. For example, have all themes definebackground
,foreground
,primary
,secondary
, andaccent
colors andgap-1
,gap-2
,gap-3
,gap-4
, andgap-5
for gap values. Use these values as the standard options in block settings for the primary means of customization. I've written a proposed set of standard tokens to get this conversation going.wp-color-background
,wp-gap-4
,is-vertically-aligned-center
This proposal is illustrated with code snippets in the blog post and there's a functioning demo showing a partial implementation.
I believe if these four things were done consistently and with a commitment to backwards compatibility, all of the following become easier or newly possible:
theme.json
48px
gap is easier for users and would look good in any theme without modification)Proposed Standard Design Token Names in
theme.json
Themes and plugins could define additional tokens beyond these, but all of the following would be expected of new themes. WordPress would likely provide a fallback in cases where themes defined only some or none of the tokens.
foreground
,background
,primary
,secondary
,accent
font-size-1
,font-size-2
,font-size-3
,font-size-4
(default),font-size-5
,font-size-6
,font-size-7
font-weight-1
,font-weight-2
,font-weight-3
(default),font-weight-4
,font-weight-5
copy
,headings
,monospace
border-1
,border-2
(default),border-3
gap-1
,gap-2
,gap-3
(default),gap-4
,gap-5
margin-1
,margin-2
,margin-3
(default),margin-4
,margin-5
padding-1
,padding-2
,padding-3
(default),padding-4
,padding-5
two-columns
,three-columns
,desktop-menu
contentSize
(exists),wideSize
(exists),maxSize
Things not included in the proposal
This proposal doesn't need to interfere with or be at the expense of:
7px
border vs a small border)Related Issues
This is closely related to a number of existing issues, and tries to present a cohesive vision for a solution. Here's an incomplete list of current relevant issues:
style
vs. describingstate
#38694Credits / Attributions
None of the ideas in this proposal are new (a strength of the proposal). I think they are most compelling when packaged together. This builds on tons of existing thinking and work by other community members. Therefore, I think it's important to credit a many people here as I can.
The text was updated successfully, but these errors were encountered: