WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.55k stars 4.22k forks source link

Explore options for standardizing and extending theme.json design tokens for colors #39372

Open glendaviesnz opened 2 years ago

glendaviesnz commented 2 years ago

What problem does this address?

Background discussion.

On the above issue, there was some consensus that there would be value in exploring the options for standardizing and extending theme.json design tokens in relation to colors.

This blog post provides a good introduction to some of the reasons for this.

N.B A lot of the discussion in relation to this has already taken place here , so please read that as background. The purpose of this issue is not to repeat all of that discussion, but rather to try and summarise what has already been discussed and to decide on some action points that could be used to prioritise/prototype some ideas to try and move this forward.

This is intended to be a top-level issue for some of the initial discussions, with a number of smaller issues likely needed to prototype/progress the work.

cbirdsong commented 2 years ago

As part of getting this discussion going, I'm going gather up and summary existing suggestions, then outline the semantic-ish system we've built for our non-theme.json agency themes.

In https://github.com/WordPress/gutenberg/issues/38998 and its accompanying blog post, @mrwweb suggested "foreground, background, primary, secondary, and accent", and @richtabor suggested nearly the same set, minus "accent".

I think background is fine, but I agree with @aristath on "foreground":

I agree with the suggestion above, background and foreground (or more appropriately text, since foreground can be perceived as a secondary background color which is more on the foreground) should be required.

"Text" is more clear.

@karmatosed gets at my main issue with "primary"/"secondary", and "accent" to a lesser degree:

What I find problematic actually is the use of primary/secondary and other words that tend to mean less - but that might be because I really want my theme.json right now to read like a recipe, not code. I don’t know if this is going to change for me with time and a personal feeling. Accent also feels strange to me as a term, accent to what? If we settle on some core fixed words, we have to think about translation, their comprehension, meaning and implications.

"Accent" at least conveys some usage intent, but primary and "secondary" are far too vague. Nothing about "primary" tells me if it's safe to use as the color of text in a link or heading, or what background color might contrast with it.

Commenters in https://github.com/WordPress/gutenberg/issues/29568 also suggested "light" and "dark", including @richtabor:

Should we have --light or --dark appended to slugs to indicate a primary--dark or primary--light color? This way, a pattern could declare that it should have a dark background with light text (via the theme's color declarations) — instead of working some of the time?

And this wouldn't happen: Screen Shot 2021-07-06 at 3 24 02 PM

The problem with this is that there's more to a dark theme than just light text. Links, for instance, will likely be illegible and inaccessible, and buttons might be the same color as the background color. I think this is better addressed by standardizing on requiring light/dark palettes. (more on that below)


To meaningfully standardize, the color names need to convey at least a broad intended use case, or a pattern that uses "primary" for the headings will look great in one theme while being illegible in another.

With that in mind, my baseline semantic colors are:

Additionally, these semantic colors are automatically computed if not explicitly supplied:

I also define these, which is where things get slightly more fuzzy but are more clear than "primary" or "secondary":

For these or any other more abstract terms, it will be important to establish expectations on how they will be used, even if many themes might even set them to be the same color as links or buttons.

I also generally add success, warning and error colors to use with form fields. They don't often change in different areas of the site, but it would be nice if themes shipped these so plugins could more easily fit in with the overall design.

View sample output: ```css --text-color: #000000; --text-shadow-color: rgba(255, 255, 255, 0.5); --background-color: #ffffff; --background-shadow-color: rgba(0, 0, 0, 0.25); --link-color: #0000cd; --link-color-shifted: #800080; --button-color: #87cefa; --button-color-shifted: #dda0dd; --button-color-inverse: #000000; --highlight-color: #ff1493; --accent-color: #008b8b; --error-color: #ff0000; --warning-color: #ffff00; --success-color: #008000; ```

This set of colors is then easy to use when developing other components, since you already broadly know how they are intended to be used. Examples:

Additionally, systemizing colors this way makes it easy to add additional palettes, most common of which are light and dark. Light/dark themes defined this way[^2] ensure other supporting elements still look good on the new background and also make it so users don't have to manually select text, link or button colors when using a non-default background color (though they'd still have the option to). (https://github.com/WordPress/gutenberg/issues/34717)

I think that expecting light/dark palettes would be a reasonable baseline requirement, since nearly every site has some bit of the design where the default color scheme is flipped. Beyond light/dark, I can easily imagine adding a "callout" palette with brighter, bolder colors, or an "aside" palette with muted colors for supplementary information. (Adding a UI for this concept is sort of what I was getting at in https://github.com/WordPress/gutenberg/issues/39028)

[^2]: These are output under two classes, .has-light-theme and .has-dark-theme. To integrate these with the editor I use @extend on them during the generation of the .has-<color>-background color classes, automatically choosing the correct one based on color contrast. The output would be a lot cleaner if the editor added a class indicating if the background color was light or dark, as was suggested here, though it would need to do a better job than the cover block.

andrewserong commented 2 years ago

Thanks for laying out all that context @cbirdsong!

To meaningfully standardize, the color names need to convey at least a broad intended use case

I really like the set of baseline semantic colors described there. I think when we're thinking of standardising naming, when the naming is as close to un-opinionated as possible (e.g. it's tied directly to a CSS property), that sounds like something where it feels like we're likely to come up with something that could be quite stable.

Like you've flagged and from the earlier discussions, for some of the more opinionated ideas (like accent or highlight color), I could imagine us changing our minds at some point, and then needing to make tweaks or design changes, particularly as trends in web design shift over time. Personally, I quite like the grouping possibilities of accent or highlight, and how it could be used across blocks, but my concern would be that since its imprecise language (in terms of which elements or CSS properties are actually targeted), it could be used in different ways (or require further tweaks) which means they might not be quite stable.

So, I was wondering if it'd be at all worth it to think of categorising the naming into "stable" and "unstable" names somehow? For example, we might say, "text" and "background" are naming conventions we're committed to treating as a stable API. The opinionated ones, e.g. "accent", "highlight", "light, "dark", might change, or are (at least slightly) open to interpretation by different themes. So, these might become an "unstable" set of colors, where folks who use them need to accept that they could change in the future?

I suppose my main thought is whether it'd be helpful to tease apart which ideas make sense right now for current design trends, but those trends could change, and which ideas appear to be more universal 🤔

cbirdsong commented 2 years ago

I don't think setting out to create an "unstable" set of colors is a good idea, and I also don't think that anything about this idea will necessarily be tangled up in current design trends. The fundamentals of choosing colors to ensure legibility have been the same since the days of "web-safe colors", and as long as the structure is grounded in basic utility and clarity for both users and developers it will be relatively future proof. Changes may eventually be necessary, but ideally those would take the form of new tokens, not radically redefining the use of an existing one.

The most important distinction for this kind of system is if the color is intended to be used as a text color or background color and needs to meet contrast requirements to ensure legibility, or is intended to be used as a supporting element and has no immediate accessibility/usability implications.

Ensuring clarity in use seems like a good way to determine how to name things and what's included, and from that perspective, text color and background color are obviously very clear, as is link color and any variant states.

Conversely, light and dark are clearly not a good inclusion because they don't carry any awareness of the overall palette being applied to the site/block. We wouldn't want a plugin to use "dark" for something intended to contrast with the background if it's already on a dark background and the surrounding text color is light.

The rest are a more mixed bag. In order of clarity, with notes on potential usefulness:

Highlight color would be pretty clear if it was renamed focus color. Adjusting that isn't currently surfaced in the site editor or block settings, but supplying a known valid focus color would be very helpful in ensuring plugins accessibly implement forms and other interactive controls, and maybe it could eventually be added to the editor.

Button color could be fairly clear, depending on the implementation details within buttons. For instance, I'd expect "button color" to correlate to the fill color of the button when applied to the default button, but on the "outline" style I'd expect that to be the border/text color. I'm not sure how easily a generic "button color" can be applied without reworking how the button CSS is structured overall, and it would also require a "button color inverse" to use for the text on top of the button when it isn't an outline button.

However, there is clear value in including button-related colors for use in forms. Maybe it would make sense to have separate tokens for "button text color" and "button background color"?

The use case of form success, warning and error colors is clear, but it would not be immediately obvious if they are intended as text/background colors or highlight/accent colors. Maybe the tokens could have modifiers like "success color text", "warning color background", "error color focus", etc?

Finally, accent color. I feel like "accent" conveys "intended to be used in support of text" much more than "primary" or "secondary", but the existence of accent-color in CSS muddies things up a bit. None of the CSS frameworks or design systems I've looked at have any better names for this sort of thing, and the only potential other options I've come up with are "feature" or possibly "highlight"?

Even with a good name, "accent" might not be useful without some indication if the color is closer to the text color or background color. It might be useful to split it into lighter/darker options in terms of text/background color, like "accent color text" or "accent color background"?

mrwweb commented 2 years ago

@cbirdsong Thanks for laying out these initial thoughts! They are a fabulous springboard for a conversation.

I think whatever system comes out of this must start from what you said here:

The most important distinction for this kind of system is if the color is intended to be used as a text color or background color and needs to meet contrast requirements to ensure legibility, or is intended to be used as a supporting element and has no immediate accessibility/usability implications.

With that in mind, I wonder if we can salvage the primary/secondary color names. Tell me if this is wrong, but I think most people think of primary and secondary as meaning "primary background" and "secondary background". Similarly, I suspect most people interpret "accent" or "highlight" as "foreground accent" or "foreground highlight".

I agree with some of the confusion around foreground/background, but I also wonder if that or light/dark can be the key to unlocking contrast-safe (and least most of the time) naming combinations. What would people think about background--primary and background--secondary or dark--primary and dark--secondary?

Button color could be fairly clear, depending on the implementation details within buttons.

I think button color is probably too specific for a standard set of color names, especially because there will also be theme.json styles for button elements. I generally think in terms of "the button uses the primary brand color", not there being a distinct "button color".

eric-michel commented 2 years ago

I've been thinking about this a lot lately. Color is so different from font sizes or margin/padding, which are fairly easy to standardize. I've worked on sites with 3 colors in the palette, and sites with 12 colors in the palette (and all of them used). There are also wildly different naming schemes (which are also heavily dependent on number of colors in the palette).

How to create standards for something that is so variable from theme-to-theme? And, when doing so, how to prevent those standards from being too prescriptive (so as to not discourage creativity)? I just don't know if it's possible.

Would some kind of remapping interface be more viable here?

For example, what if we defined a bare minimum of standard colors that would really be needed for all sites. Something like:

All other colors are free-defined in theme.json (and adjustable in the Global Styles interface). So as an example, let's say a user is currently using a theme that defines the 4 standard colors listed above, along with the following custom colors in theme.json:

The user then wants to swap to another theme. This theme uses a color palette that defines the 4 standard colors (text-color-dark, text-color-light, action-color-dark, action-color-light) along with the following custom options:

During the theme swap, all 4 of the standard colors obviously remap themselves 1:1. For the other colors, an interface appears that allows the user to map primary, secondary, and tertiary to either highlight or accent similar to the way you can remap authors to new users when importing posts into WP from another WP site.

Let's say our user decides all instances of primary and secondary should become highlight, and all instances of tertiary should become accent. After making those selections, the remapping system does a find/replace in the database that reassigns things like has-primary-background-color classes to has-highlight-background-color, etc.

There are obviously some gaps in the concept here, but figured I'd throw out the idea to see what y'all thought.

cbirdsong commented 2 years ago

Broadly, I think including "light" and "dark" in preset names will invite misuse from theme/plugin authors that use them assuming the content is appearing on a light background. They need to be more generic, and ideally tied into a system like this:

Tell me if this is wrong, but I think most people think of primary and secondary as meaning "primary background" and "secondary background". Similarly, I suspect most people interpret "accent" or "highlight" as "foreground accent" or "foreground highlight".

Primary/secondary/tertiary variants of more explicitly defined properties might be a good way to keep those words in the mix, but it would mean that if you wanted to use them in CSS you'd have to account for them being missing:

.my-cool-block {
  background-color: var(--wp-background-color-tertiary, var(--wp-background-color-secondary, var(--wp-background-color-primary)));
}

Alternately, I guess, the editor could generate redundant custom properties for colors themes don't define:

:root {
  --wp-background-color-primary: #191970;
  --wp-background-color-secondary: var(--wp-background-color-primary);
  --wp-background-color-tertiary: var(--wp-background-color-secondary);
}

Either way, tertiary is probably as far as that should go?

eric-michel commented 2 years ago

Been thinking more about colors! Specifically - how to handle properly providing sensible color contrast defaults. I had elsewhere advocated for adding .is-dark and .is-light classes in the editor (like the Cover block currently does) in order to accomplish this, but after some consideration, I think that's a bad idea.

If the editor handles assessing color lightness or darkness real-time (as it does with the Cover block), that will break two features:

  1. If a user updates a color in the Global Styles interface, and it changes from light to dark or dark to light, the .is-dark or .is-light classes inserted on any page that uses that color will be wrong, and the user would have to visit and update every page in order to have the editor reassess the color and correct that error.
  2. Similarly, I believe it's been proposed that eventually a theme author will be able to create multiple theme.json files that a user can swap between. If a swap from one theme.json file to another includes a color changing from light to dark or vice versa, editor-output classes would need to be re-evaulated again.

Instead, it probably makes sense for WP to assess each color in theme.json when parsed (or when parsing the selections in Global Styles) to output the correct CSS using the text-color-dark, text-color-light, action-color-dark and action-color-light standard colors I proposed above.

For example, let's say this is an example theme.json with the added structure for the standard text and action colors:

"settings": {
  "color": {
    "text-color-dark": "#4d4d4d",
    "text-color-light": "#fff",
    "action-color-dark": "#e64c38",
    "action-color-light": "#fdebdc",
    "palette": [
        {
            "slug": "primary",
            "color": "#e64c38",
            "name": "Primary",
            "default-text-color": "auto", //defaults to "auto" when not defined; when parsed into CSS, WP automatically chooses either "text-color-dark" or "text-color-light", whichever provides best contrast
            "default-action-color": "auto" //defaults to "auto" when not defined; when parsed into CSS, WP automatically chooses either "action-color-dark" or "action-color-light", whichever provides best contrast
        },
        {
            "slug": "secondary",
            "color": "#b0bc22",
            "name": "Secondary",
            "default-text-color": "light", //overrides auto selection and outputs "text-color-light" as default text color
            "default-action-color": "auto"
        }
    ],
  }
}

which would then output CSS that roughly looks like this:

:root {
  --text-color-dark: #4d4d4d;
  --text-color-light: #fff;
  --action-color-dark: #e64c38;
  --action-color-light: #fdebdc;
  --color-primary: #e64c38;
  --color-seconary: #b0bc22;
}

.has-primary-background-color {
  background-color: var( --color-primary );
  color: var( --text-color-light ); //automatically chosen as best contrast by WP during theme.json parse
}

.has-primary-background-color a {
  color: var( --action-color-light ); //automatically chosen as best contrast by WP during theme.json parse
}

.has-secondary-background-color {
  background-color: var( --color-secondary );
  color: var( --text-color-light ); //uses --text-color-light despite --text-color-dark providing better contrast because of theme.json setting
}

.has-secondary-background-color a {
  color: var( --action-color-dark );
}

.has-primary-color {
  color: var( --color-primary );
}

.has-secondary-color {
  color: var( --color-secondary );
}

.has-primary-link-color a {
  color: var( --color-primary );
}

.has-secondary-link-color a {
  color: var( --color-secondary );
}

This is another rough concept, but the idea would theoretically provide good default color contrast while preventing issues with color changes in Global Styles or theme.json.

eric-michel commented 2 years ago

@marceloaof that's an incredibly thorough list! Thanks for doing all that work!

Honestly, the broad array of color names used in what is (at the moment) a relatively small number of themes reinforces to me that trying to establish a standard naming scheme for colors isn't going to work and a color remapping system would be more viable.

cbirdsong commented 1 year ago

This is a really good blog post about structuring and naming variables used for colors: https://jwdallas.com/posts/namingcssvariables/