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

Theme package for generating admin themes #54541

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from
Open

Theme package for generating admin themes #54541

wants to merge 10 commits into from

Conversation

SaxonF
Copy link
Contributor

@SaxonF SaxonF commented Sep 18, 2023

color-change.mp4

What?

Introduces a theme package that aims to standardise how we name, generate and use the css variables that make up the foundation of an admin theme.

Why?

This is a big part of the admin redesign work as it lays the groundwork for tokens in future.

To reference #53612

The current Gutenberg colour system has been great for the editor, however we are starting to see the limitations while designing for a dark surface (e.g. site editor). If we want to lean heavily into customisation, and support themes including dark, we will need to extend the system and take a more programmatic approach. One of the biggest gaps in our current system is a limited bottom end of the scale. e.g. for component backgrounds, states etc

How?

A naming convention
image
You can see how this looks here

A theme object
The theme object is the central part of the theme and all variables are built from it. It should be familiar to anyone who has used other frameworks like Tailwind or Theme-UI or even theme.json of WP themes. It looks a little something like:

// theme object
export const defaultTheme = {
	shadows: {
		sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
		md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
		lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
	},
	spacing: {
		1: '8px',
		2: '12px',
		3: '16px',
		4: '24px',
		5: '32px',
		6: '48px',
	},
	borderRadius: {
		sm: '2px',
		md: '4px',
		lg: '6px',
	},
	fonts: {
		body: 'system-ui, sans-serif',
		heading: 'system-ui, sans-serif',
	},
	fontSizes: {
		xs: '12px',
		sm: '13px',
		md: '15px',
		lg: '18px',
		xl: '22px',
	},
	fontWeights: {
		normal: '400',
		medium: '500',
		bold: '700',
	},
	lineHeights: {
		normal: '1.5',
		heading: '1.25',
	},
	colors: generateColors( {} ),
};

Color generation
To make WordPress customisable in an accessible and aesthetically pleasing way we need to be clever with the way we generate colours. In this example we take a single color prop that acts as your accent along with whether you want darkMode enabled or not as well as a fun prop. Treating dark mode like a mode vs a theme is important as it allows platforms to white-label WordPress but still support light/dark. The fun prop just increases saturation in the neutral scale.

Colour generation in this PR offers us a simple starting point but we should eventually look at more complex ways of generating colours like what was explored in the try/theming-test branch that rotates hues and adjusts saturation as you go up/down the scale.

Default CSS
The package should also include an importable CSS file that includes defaults. Ideally this file is built automatically any time the color generator is changed (started that work here)

Theme provider
This also includes a very lightweight theme provider that is used to generate theme variables which are included in a CSS class added to document head.

Testing Instructions

  1. Open packages/edit-site/src/components/app/index.js and adjust theme values on line 46.
  2. Open site editor and see changes reflected
@SaxonF SaxonF changed the title Try/tokens mvp Sep 18, 2023
Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

I like the overall approach personally here. But I'm trying to guess from the code which is a bit hard, I know it's probably too early but would love docs (README) for the theme package.

The other thing I'd love to see is to align these variables with the existing variables we have --wp-admin-theme-color. So in that sense:

  • --wp-admin-theme-color can be seen as the initial implementation of this theme package
  • Would love if the new variables are also prefixed with --wp-admin-theme
  • Would love if we could somehow remove admin-scheme mixin and replace with this theme package.
@github-actions
Copy link

github-actions bot commented Sep 18, 2023

Flaky tests detected in e1afaab.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/6231011696
📝 Reported issues:

@SaxonF
Copy link
Contributor Author

SaxonF commented Sep 19, 2023

@youknowriad thank you for your early review! --wp-admin-theme makes sense to me.

I've added more context to the issue description which can be adjusted for docs when ready.

Two challenges we have:

  • The site editor has both light and dark components in the same area. For example light dropdowns/modals sitting on top of dark surface. Not exactly sure how to resolve this other than fully committing to dark/light modes.
  • Similar to the above, we need the editor to be light whilst sidebar is dark. The way layout file is currently structured makes this challenging.
@SaxonF SaxonF mentioned this pull request Sep 19, 2023
4 tasks
@Ren2049
Copy link

Ren2049 commented Sep 19, 2023

@SaxonF Color scales maintaining the wcag contrast in both directions can be a way for light/dark mixed themes.

The scales I've posted here work like this. E.g. color 800 has the same distance in contrast from 900 ( dark content bg) as 200 from 100 (bright content bg). It could be a starting point.

If you click the links it takes you to the Leonardo.io page with the scales (can take a bit to load). The saturation etc. can be customized through the sliders at the top, you can remove the color keys and add your own and reuse the scale (lightness stops).

@SaxonF SaxonF marked this pull request as ready for review September 19, 2023 07:40
@github-actions
Copy link

Warning: Type of PR label error

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Type-related labels to choose from: [Type] Accessibility (a11y), [Type] Automated Testing, [Type] Breaking Change, [Type] Bug, [Type] Build Tooling, [Type] Code Quality, [Type] Copy, [Type] Developer Documentation, [Type] Enhancement, [Type] Experimental, [Type] Feature, [Type] New API, [Type] Task, [Type] Performance, [Type] Project Management, [Type] Regression, [Type] Security, [Type] WP Core Ticket, Backport from WordPress Core.
  • Labels found: .

Read more about Type labels in Gutenberg.

@SaxonF
Copy link
Contributor Author

SaxonF commented Sep 19, 2023

The scales I've #53612 (comment) work like this. E.g. color 800 has the same distance in contrast from 900 ( dark content bg) as 200 from 100 (bright content bg). It could be a starting point.

Thanks @Ren2049 ! Worth exploring. Part of the challenge we have is that we arguably need a deeper bottom half of the scale (backgrounds, borders etc) vs top half. The approach explored here is similar to manually curated Radix scales. Any thoughts as to how saturation should shift as you progress through scale?

@Ren2049
Copy link

Ren2049 commented Sep 19, 2023

Part of the challenge we have is that we arguably need a deeper bottom half of the scale (backgrounds, borders etc) vs top half.

Gotcha, the bottom part (borders, bg) is of course much more sensible than the top (type) where the distances can vary more). I'd just mirror the bottom part steps and distances you need at the top. In use you could just avoid using the top part steps you don't need.

Any thoughts as to how saturation should shift as you progress through scale?

When I only use one scale I like to have the most saturation in the middle (3:00:1 to 7:00:1), for popping cta buttons and the like.

Most often I use a neutral scale based on the brand color with 3% to 7% saturation, and then a brand color scale with a % based on the vibe I'm going for, e.g. 40% for a classic vibe or 80% for mid century modern ones. Imo abundance doesn't hurt if you have a tight design system you know and have learned.

That's why the color system I've posted there has the complete color circle which is based on oklch hues. https://ok-color-picker.netlify.app/#127d8d

The colors of the circle represent 30 hues with a distance of 12 between each hue, at 100% saturation and 100% value using the hsv option of the ok-color picker, which served as the keys for the scales in Leonardo.

The advantage of oklab and a large enough color circle is that you don't need to rotate the hues because it's more consistent color space, especially for blues where rgb tends to shift into violet.

The only thing that maybe has to be adjusted manually are the colors that naturally peak at very low contrasts, like yellow or cyan. Their color-100 or 200 will also look more colorful because of that. Radix did a good job recognizing this and changed the usecase for those scales (inverting the button colors for example).

I think main problem is caused by the colors themselves and how we are used to them. What people define as Yellow is a specific spot within a scale at a low contrast, other colors like blue cover a larger spectrum.

@Ren2049
Copy link

Ren2049 commented Sep 19, 2023

An idea:

A complete color system with bi-directional scales for each hue at different saturations could be very interesting for WP themes as well. If you use bright and dark patterns in a theme you basically have to deal with the same problem. The advantage it would have over fully auto generated ones is that some color quirks can be accounted for in advance.

With a dense enough color system (enough hues) stored in a db, users who paste a hex color into WP could get a complete color system/scales (neutral, brand, info, error etc.) in return without noticing that it's not exactly based on the rgb hue they have pasted in but the nearest stored oklab hue.

The 30 hues of the circle map to 15 colors with cool and warm variants of each hue/color, and let's say a users hex code would fall in the cool category, info or error scales could also map to cool variants increasing the consistency even further. A set of scales using only cool variants will have of course a different vibe from a warm one (funny/warm vs. serious/cool for example)

These are the okhsv keys I'm using:

H90 - #ffcb00 - Warm Yellow
H78 - #ffb300 - Cool Amber
H66 - #ff9c00 - Warm Amber
H54 - #ff8200 - Cool Orange
H42 - #ff6100 - Warm Orange (warmest)
H30 - #ff1500 - Warm Red (warmest)
H18 - #ff004c - Cool Red
H6 - #ff0075 - Warm Rose
H354 - #ff009b - Cool Rose
H342 - #ff00c6 - Warm Magenta

H330 - #ff00f7 - Cool Magenta
H318 - #d200ff - Warm Purple
H306 - #a701ff - Cool Purple
H294 - #8001ff - Warm Violet
H282 - #5c00ff - Cool Violet
H270 - #3000ff - Warm Blue
H258 - #0077ff - Cool Blue
H246 - #00a1ff - Warm Azure
H234 - #00bbff - Cool Azure
H222 - #00d0ff - Warm Cyan

H210 - #00e4ff - Cool Cyan (coolest)
H198 - #00f9ff - Cool Teal (coolest)
H186 - #00ffed - Warm Teal
H174 - #00ffd3 - Cool Emerald
H162 - #00ffb1 - Warm Emerald
H150 - #00ff78 - Cool Green
H138 - #6cff00 - Warm Green
H126 - #beff00 - Cool Lime
H114 - #f0ff00 - Warm Lime
H102 - #ffe800 - Cool Yellow

Code could read out the hsl value from the pasted hex and map the h to the nearest oklab hue in the database. Users could then pick a vibe which would map to specific saturations for the scales.

With a complete system there's a lot of potential to help users nailing the design, and maybe use WP as the source of truth and place to bootstrap a design system they'd also use elsewhere in other design apps.

A complete color system built into WP would be incredible. With enough feedback and testing I think it could be much better than the Radix or Tailwind systems because those lack variety. They're build on top of RGB which skyrockets the complexity of manually rotating hues or a strange color distribution across circles like that.

@ciampo
Copy link
Contributor

ciampo commented Sep 25, 2023

Hey @SaxonF and @jameskoster, thank you for working on this!

Before diving into the actual code review, I'll leave some high-level observations.

Marking it as a private API

While this is still WIP work, we should definitely export all APIs as private, via the lock/unlock mechanism.

One thing that we won't really be able to keep private are CSS variables, though. So we'll have to be very explicit about the experimental nature of those variables, and potentially we could even look at implementing a custom linter rule as part of WP scripts (both in JS and CSS) to warn when such variables are used outside of some given package?

DOM context vs React context

Just noting that using CSS variables means that the inheritance will be based on the DOM, and not on React trees. This is particularly relevant in the editor when using slot/fills, and I personally think it's the correct choice.

Keeping it simple, especially at the beginning

While it's tempting to work on a very comprehensive theme provider, my advice would be to focus on a small subset of variables to start with:

  • it will make it easier to get something working quickly
  • it will allow us to test our assumptions and make changes early, before we've committed to a certain approach on a large scale
  • it will allow us to slowly introduce more variables only when really needed

I would therefore suggest focusing only on colors for the MVP / first iteration, and delaying the rest to a later time.

I see that this PR is already talking this approach, which is great!

Defining color inputs

Specifically to the color aspect of the theme, I agree that it's the theme provider's duty to provide a set of background/foreground (text) colors values that are guaranteed to be accessible.

Your proposed approach suggests taking, as inputs, 3 variables:

  • accent color
  • is the theme light or dark
  • fun (ie. how much saturation should the generated grayscale have)

With the above sets of inputs, consumers of the theme provider won't be able to set a custom background color. Is the assumption that background colors will always be white-ish for light mode and black-ish for dark mode? What if they the UI required the accent color to be used as background? What if there was ever a need for a different background color?

Naming variables

Having a comprehensive naming convention is important, and the one proposed above seems flexible enough to account for most usecases (although we'll have a better idea only after using the experimental theme for a bit).

One thing that is not clear to me is: how do I know which variable I should use for text, based on the background? Is the "neutral" set of variables supposed to provide the main foreground (text) colors? How do we know which pairings of bg / text color are safe?

Exploring different color spaces

Curated color and brightness scales seem like a great starting point. One alternative would be to use a color space like OKLCH which provide a more predictable lightness and are closer to human perception.

Keeping it simple (again)

Specifically when talking about colors, I don't think that the theme package should list full color presets.

The aim is not to create the next styling framework, but rather to empower Gutenberg with an easy and accessible way to partially customise its UI.

Any line of code that we add adds more complexity, increases bundle size, and will require maintenance at a later point. Let's be as lazy as possible, implementing only what's strictly necessary and adding more functionality only if we really need.

Adding docs and tests

As @youknowriad mentioned, we should work on having some DOCS, since they also help folks reviewing the PR to understand better the approach taken.

I personally think that we should also add unit tests to make sure that the theme variables are generated correctly, predictably, and respecting accessibility requirements (we could test for color contrast).

Runtime color generation

While generating colors at runtime is very likely a necessity, we'll need to be very careful of not introducing runtime performance regressions:

  • we should aim at keeping the amount of runtime generation as low as possible
  • we should optimize the runtime generation code to run the least amount of times possible
@@ -85,6 +85,7 @@
"@wordpress/shortcode": "file:packages/shortcode",
"@wordpress/style-engine": "file:packages/style-engine",
"@wordpress/sync": "file:packages/sync",
"@wordpress/theme": "file:packages/theme",
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably run npm i to generate updates to package-lock.json

@SaxonF
Copy link
Contributor Author

SaxonF commented Sep 25, 2023

Thanks for the feedback @ciampo ! Good points.

Specifically when talking about colors, I don't think that the theme package should list full color presets. The aim is not to create the next styling framework, but rather to empower Gutenberg with an easy and accessible way to partially customise its UI.

I'm not sure i understand this point, can you clarify? The aim is to provide a set of standardised tokens (starting with colour) that our component system and anyone else can make use of to enable theming + greater consistency in our UI. The theme provider is a way of generating these tokens. The way these tokens are applied is up to the consumer (eg we could also output a theme object to use inline) so I wouldn't consider this a styling framework.

If you have any extra thoughts on how to break this down further to a shippable v1 that please let know. I'd be happy to keep working on but also equally happy for someone else to run with it.

Re colour space. The previous branch is based off Superpal which uses Okhsl. It Would be nice to work towards that point when we start generating more colour scales for warnings etc and get deeper in to customisation. That's when consistent perceivable lightness across scales will be valuable to help with forcing contrast.

@ciampo
Copy link
Contributor

ciampo commented Sep 25, 2023

Specifically when talking about colors, I don't think that the theme package should list full color presets. The aim is not to create the next styling framework, but rather to empower Gutenberg with an easy and accessible way to partially customise its UI.

I'm not sure i understand this point, can you clarify?

Apologies if I wasn't clear. The theme provider should indeed provide tokens to be used to theme the UI, but the Theme package should not offer (IMO) a list of generic color swatches (like the ones listed in a message above)

@Ren2049
Copy link

Ren2049 commented Sep 25, 2023

@ciampo the ones listed in the message above are OKHSV based keys for Leonardocolor.io to generate hue contrast palettes for the entire color circle. This can replace a color picker with a set of bulletproof (accessible) color palettes, similar to other complete color systems like Munsell.

@scruffian
Copy link
Contributor

What do you think about using a theme.json file format?

@MaggieCabrera
Copy link
Contributor

What do you think about using a theme.json file format?

Oooooh, could a theme add it's own admin.json file so everything looks consistent then?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
7 participants