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

🖍Global Style System #19611

Closed
ItsJonQ opened this issue Jan 13, 2020 · 46 comments
Closed

🖍Global Style System #19611

ItsJonQ opened this issue Jan 13, 2020 · 46 comments
Labels
Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json Needs Technical Feedback Needs testing from a developer perspective.

Comments

@ItsJonQ
Copy link

ItsJonQ commented Jan 13, 2020

👋 Hello everyone!

First off, Happy New Year at all! I hope you all had the happiest of holidays.

I'd like to expand upon the original "Block Style System" issue @mtias started at #9534.

I originally proposed a concept last year in November. Even before then, the thread has evolved a lot, with a lot of exciting ideas and feedback.

This Github issue is also very much related to #19255. Serving as the technical complementary piece to the Global Styles / FSE design work that is happening!

Recap

The idea is to provide a unified system for Gutenberg blocks (core + 3rd party) and themes to work well with each other. They also have to understand and respect user overrides. These defined styles can be applied globally throughout the user's site (e.g. Declaring font scale, or colors, or how all Buttons look).

At the moment, Gutenberg only supports style customization at the per-block level. So updating a Button in one post, will not retroactively update Buttons on other pages.

Terminology

"Builders" - People who create things to help create content. Examples: Block creators, themers, plugin authors.
"Users" - People who create content.
"Backend" - Gutenberg's Editor. What the User uses to create content.
"Frontend" - Rendered site. What the User sees.


Demo

Screen Capture on 2020-01-13 at 16-33-31

I think a good way to start this one would be an interactive demo!
The demo can be found here:

👉https://9w53w.csb.app/

CodeSandbox (for code/preview):
https://codesandbox.io/s/github/itsjonq/wp-gs

Github Repo for source code:
https://github.com/itsjonq/wp-gs

In the demo, click on "Toggle Inspector" to see the style configs (outlined below)

To simulate the "loading" of a theme, go to:
https://codesandbox.io/s/github/itsjonq/wp-gs

Uncomment the following line:

//import "./load-theme";

And click the refresh icon in the browser preview (right)


Note: The code I have is mostly prototype code. Code to discover and realize the various moving pieces, and how they all fit together. I wrote it outside the context of Gutenberg to make iterations/builds faster. It also enabled me to publish it onto CodeSandbox for live code/preview purposes.


The Parts

01-parts

For my proposal, global styles is a system of setting and rendering a (giant) config. These mechanisms can be represented by 5 parts (above).

  1. Default theme styles
  2. Theme styles (e.g. Twenty Twenty)
  3. User styles
  4. Merged styles
  5. CSS Variable rendering

1. Default Theme Styles

These determine the default properties how the sites/blocks will render. These are the bare-bone essentials for rendering site's front-end. They are not opinionless, but very close to being so.

They also help establish structure when it comes to adding/updating values. For example, colors and fontSizes will always exist.

The current structure follows the semantics outlined by Theme UI spec, which is a (mostly React) based theming convention that's gaining in popularity. Of course, we don't have to use this schema :).

What qualifies as default styles?

Some core colours and typography would be a great start.

Gutenberg's core blocks have style opinions. They kind of have to. For certain blocks to be functional, they need some styling in order to work correctly on the front-end. These would qualify for default styles as well.

2. Theme styles

In the future, once Global Styles x Gutenberg is in place, one way a Gutenberg supported theme may want to customize blocks would be to include a theme.json file. The key/value pairs follow the same schema.

The .json file works similarly to the current add_theme_support() php function, but I feel like it's easier to declare.

These will add to/override the values from default styles.

3. User styles

The ability for the user to customize these values would be presented in the Global Styles editor/tool interface. The concept is very similar to the customizer.

The experience of this can be seen in my demo.

Global Styles x Blocks

In addition to updating colors or typography, (in the future) this interface can update (style based) attributes for blocks. Using a registration system (like how core/custom Gutenberg blocks are registered), block style attributes can appear in this global editing experience.

Hierarchy

User styles overrides theme styles.
Theme styles overrides defaults.

Leading us to...

4. "Merged" styles

This is the consolidation of User > Theme > Default styles. These are consolidated and enhanced to prepare the values to be rendered.

Enhanced with "plugins"

03-transforms

A mechanism I built into the system allows for Builders to modify/enhance values from the style config. For example, in the above screenshot, the original single text value has become an array of various color shades. This is applied to any color value under the colors key.

With my prototype, this is how a plugin may look like:
https://github.com/ItsJonQ/wp-gs/blob/master/src/global-styles/plugins/color-scheme-plugin.js

By interpolating/enhancing values, we can improve and simplify the values to be rendered with CSS

5. CSS Variable rendering

Finally, the defined and enhanced styles are rendered via CSS variables. The last stage of the system flattens and transforms the data into CSS variables.


Global Styles x Blocks

For blocks to start using global styles, their CSS will need to be refactored to use the CSS variables outputted by system.

Example:

.wp-block-button {
	box-sizing: border-box;
	border: 1px solid var(--wp-gs-color-primary-dark20);
	display: inline-flex;
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
		"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
		"Helvetica Neue", sans-serif;
	line-height: 1.2;
	font-weight: bold;
	text-decoration: none;
	cursor: pointer;

	background-color: var(--wp-gs-button-backgroundColor);
	border-radius: var(--wp-gs-button-borderRadius);
	border-width: var(--wp-gs-button-borderSize);
	box-shadow: var(--wp-gs-button-dropShadow);
	color: var(--wp-gs-button-textColor);
	padding: var(--wp-gs-button-padding);
}

Global Styles x Themes

With this system, the overhead of styling blocks outputted from Gutenberg has been tremendously reduced. By leveraging a theme.json file, the Themer has a lot of controls over the visuals of the WordPress x Gutenberg. Better yet, they will be able to confidently adjust the visuals of custom blocks as well (assuming they're using global styles).


Rendering on the Front End

Similar to how Gutenberg blocks are saved/rendered, we can save and store the outputted HTML from Global Styles. This HTML contains the generated variables, which can be injected into the head via PHP, meaning there's not variable/style consolidation required on load. It's already been done.

Any change made to global styles will trigger a save, following the mechanism that Gutenberg has for its blocks.

This style generation mechanism needs to be triggered regardless if Gutenberg is loaded or not. For example:

A user installs and opens WordPress for the very first time. They download and activate a custom theme (with global styles support). They view their site, without having written a page/post yet. The site should render correctly.


Customizing rendering of "sections"

Since UI on the front-end will be rendered with CSS Variables, we will be able to customize how blocks render in "sections" in a robust manner.

For example, your site is white with black text, but your sidebar is black with white text.

Certain blocks that get added to your sidebar need to adjust their colors to be readable. From a technical standpoint, this can now be done by inline styling a CSS variable, example:

<div class="wp-sidebar-block" style="--wp-gs-colors-text: white;">
  ...
</div>

For those who made it to the end of this very long post, thank you!

🙏 🙏 🙏 🙏

There are a lot of moving parts for this idea. We're still very early days. I'm sure there are a lot of nuances and edge cases that I haven't thought of yet. Which is why I'd love to hear from everyone!

Thoughts and feedback are 💯 welcome.

Thank you so much for your time!

@ItsJonQ ItsJonQ added Needs Technical Feedback Needs testing from a developer perspective. [Type] RFC and removed [a11y] Color Contrast labels Jan 13, 2020
@tellthemachines
Copy link
Contributor

Thanks for posting this! Very excited for the possibilities 😍

These defined styles can be applied globally throughout the user's site (e.g. Declaring font scale, or colors, or how all Buttons look).

At the moment, Gutenberg only supports style customization at the per-block level. So updating a Button in one post, will not retroactively update Buttons on other pages.

To clarify, will users be able to update the color on all the buttons, or the font-size on all the paragraphs, through the editor UI? And if so, will they still be able to make adjustments on a per-block basis?

Similar to how Gutenberg blocks are saved/rendered, we can save and store the outputted HTML from Global Styles. This HTML contains the generated variables, which can be injected into the head via PHP, meaning there's not variable/style consolidation required on load. It's already been done.

Would there be a good alternative to using CSS variables on the front end? The concern here is that any website using WP would stop working on IE. Even if WP as a project decides to stop supporting IE (which is likely at some point in the future), if users can no longer build IE-compatible websites with it that could be a huge problem for any government or other official institution that uses WP.

@SchneiderSam
Copy link

So cool and can't wait for this feature!

@nerrad
Copy link
Contributor

nerrad commented Jan 14, 2020

Would there be a good alternative to using CSS variables on the front end? The concern here is that any website using WP would stop working on IE. Even if WP as a project decides to stop supporting IE (which is likely at some point in the future), if users can no longer build IE-compatible websites with it that could be a huge problem for any government or other official institution that uses WP.

This is an important point and unfortunate reality. It'd be nice if we could get some hard data on exactly how much of an impact this would have though.

With that said, I wonder if something like https://www.npmjs.com/package/postcss-css-variables would be something that could be used in the build process to assist with the final css. Maybe there could be a plugin for WP that would do on the fly processing of the css output to transpile (using that package) to a IE friendly output that could then be used by those sites needing it?

@ItsJonQ
Copy link
Author

ItsJonQ commented Jan 15, 2020

@tellthemachines + @nerrad Thanks for your feedback! I agree, the concern with CSS Variables and the lack of (older) browser support is very real.

Maybe there could be a plugin for WP that would do on the fly processing of the css output to transpile (using that package) to a IE friendly output that could then be used by those sites needing it?

That would be interesting! Something server-side to reduce the need for a JS-based polyfill fill client side.

I'm really hoping we can use CSS Variables for the styling mechanism for this project. Either using a technique like what @nerrad suggested, and pony/polyfill, or something else.


With that said, I don't think CSS variables the piece that makes/breaks global styles.

I think the most critical piece, would be creating the system that allows Themes, Blocks, and user Customizations (globals + one-offs) to play nicely with each other. A system that reduces complexity and overhead for Block Developers and Themers. This will then provide a more cohesive experience for users. Perhaps a user who customizes things often or tries using different custom blocks from different block authors.

Focusing on that user experience is, I think, ultimately a big part of the Gutenberg experience :). I'm reminded of that by a comment @nerrad wrote (link below), where he eloquently emphasized the importance of user experience within WordPress.

#16384 (comment)

@ItsJonQ
Copy link
Author

ItsJonQ commented Jan 15, 2020

👋Hallooo all!

I have an update. I've been working with @karmatosed to figure out the flows for how global styles will work in the context of Full Site editing, as well as with themes and blocks.

I've created a prototype that demonstrates the flow + hierarchy of how global styles will work with blocks and themes

I recorded a screencast that goes over prototype + concepts:

📹 Video
https://www.loom.com/share/73f721797f524dfeb2c3a222e9e24561

🙌 Demo
https://yvz8y.csb.app/#/v2/post


Too Long, Didn't Watch

No worries! I'll try to summarize <3

The style hierarchy can be represented by this pyramid:

Screen Shot 2020-01-15 at 4 38 43 PM

Core: Base styles that come with Gutenberg/global styling system
Theme: Custom styles defined by a theme
Global: Custom styles applied by the user, intended for the entire site
Document: Custom styles applied by the user, intended for certain pages/template parts (FSE)
Block: Custom styles applied by the user, for individual blocks

In the above pyramid, we can see that the Core styles (black text), has persisted it's way up, as no styles were defined on any other layer. Resulting in something that looks like this:

Screen Shot 2020-01-15 at 4 43 43 PM

If we switch the theme to Blue, the blue theme's custom styles (in this case, blue text), will override core's styles. It will persist upwards as no other custom styles are applied

Screen Shot 2020-01-15 at 4 38 48 PM

Resulting in a site that looks like this:

Screen Shot 2020-01-15 at 4 44 53 PM

If we apply a global style, it will override the theme's:

Screen Shot 2020-01-15 at 4 39 01 PM

If we apply a document style, it will override global's (if applicable):

Screen Shot 2020-01-15 at 4 39 12 PM

Lastly, if we customize a specific block, it will override the other layers:

Screen Shot 2020-01-15 at 4 39 21 PM

Resulting in:
Screen Shot 2020-01-15 at 4 39 33 PM


We will also have the ability to "reset" or remove custom settings. In this example, let's say the user removes their global styles setting. However, the document style remains, which renders like so:

Screen Shot 2020-01-15 at 4 39 51 PM


Theme Switching

Global + Document styles are coupled with the theme. You can think of them as user theme configs. If the user switches to another theme, it will apply that themes' global + document styles.

Let's say the user has customized their existing theme ("blue") a bunch.
Let's say the user installs a brand new theme ("red"). A theme they just downloaded.

Once they switch, the global + document styles will be reset.

The reason is because they've never customized anything on the "red" theme yet.

Switching back to "blue", will restore their previous global/document styles.

Screen Capture on 2020-01-15 at 16-51-17


TLDR (Too Long, Didn't read)

  • There's a style hierarchy - Core, Theme, Global, Document, Block
  • Global, Document, Block styles are customized by the user
  • Global + Document styles are associated with a theme
  • This allows for theme switching with minimized side effects

Thank you so much for your time! Thoughts + feedback welcome 🙌

@aristath
Copy link
Member

Excellent breakdown, thanks @ItsJonQ!
Isn't Theme & Global pretty much the same thing though?
I mean thinking of what the future of themes might look like, most themes will probably switch from using the customizer to the global-styles system. Settings that now exist in the customizer will be migrated to this system... They can be thought of as the same thing. We can call them "theme" if the theme uses the customizer and "global" if the theme is using the global-styles system, but it's one or the other, no theme will have the same settings both in the customizer & the global-styles editor because there's no reason to do so, there's no benefit in having both.

Regarding the css-vars: I wouldn't worry about IE support... Parsing the content and replacing css-vars with their values won't be difficult to do in PHP.
IMO we can work with css-vars and a simple filter that does search-and-replace server-side on the PHP side of things can work perfectly fine.

@oandregal
Copy link
Member

Would there be a good alternative to using CSS variables on the front end? The concern here is that any website using WP would stop working on IE.

This is solvable by creating a fallback such as:

p {
    font-size: 16px; // Fallback for IE.
    font-size: var( --font-size-paragraph );
}

It can be automated using https://www.npmjs.com/package/postcss-custom-properties Another approach would be a SASS mixin, although the CSS gets muddy (you have to write @include font-size-mixin( 16px ); which is a bit unnatural. There are options to overcome older web citizens, so CSS custom properties are fine to use.

@kraftner
Copy link

I've just read through all of #9534 and this issue and I'd like to bring up a perspective that hasn't been mentioned at all yet as far as I've read.

Users Styling vs. Builders Styling

All of the above is mostly concerned with maximum flexibility and control for "Users", offering them a solid base in the form of core and theme styling but giving them the final say and a broad array of capabilities to alter the styling of a website.

This is a valid and quite surely the most common use case, but I am coming from a totally different (not to say opposite) direction: Sites where there are multiple users as well as a clear separation between designers, developers and editors. In that setting users specifically should have no or at least very little control over the styling of the site. They create semantic content using the (heavily moderated) set of blocks leaving the styling in the hands of designers and developers. Think separation of concerns between content and layout/styling for the sake of consistency.

In that situation the "Style Hierarchy" mentioned above is not what one needs or wants since styling should solely be defined by the theme. Changing anything on the Global, Document and Block Levels shouldn't be possible. And even the Core Styling might be too opinionated. Instead the Theme Level should be the first, last and only one to decide how everything is styled.

Shouldn't we just embrace users being in control?

Generally yes, but maybe not here. Let's see why.

If a website is based on a corporate design, a styleguide or any other well thought through system a button looks like a button and there is no need for a user (think content editor) to ever even be able to micro-manage things like the background color of a button. Maintaining global consistency is impossible with individual user control.

(Even more so I personally think taking away some choices like the ones relating to styling actually helps users be content editors by freeing them from needing to think about styling. This actually makes them feel more in control even if they might not technically be. "Decisions, not Options")

Core styles are always opinionated.

I understand that there has to be some baseline, but even that often needs to be changed drastically if not even removed. Simple example: Currently Column and Gallery Blocks use CSS Flexbox for layout, making any Core Block CSS totally obsolete if not even hindering if having your whole site layout based on CSS Grid.

So basically here the whole concept of multi-layer overwrites is somewhere between irrelevant to harmful beyond the fact that one can disable, overwrite or take as much control over it as possible. And even if some things may be allowed to be styleable by users I'd see the pyramid shuffled such as that the theme layer is on top having the final say and only "letting through" certain styling choices by a user.

So if this whole system gives even more styling control to users than we already have right now please make sure that any setting that is being introduced as user-controllable has a way to be disabled, restricted or overwritten. This is so that it isn't as difficult to stay in control as it currently is with things like custom colors/gradients, fonts sizes, number of columns, drop caps,...

Final thoughts

To close things up I am aware that the general direction Gutenberg is moving towards is a very visual approach to content editing. WordPress has always been quite oblivious to the separation of content and layout, but currently I see the risk that this might become the final nail in the coffin so that it isn't only difficult but becomes outright impossible the further down we go the route of giving unbound styling power to users.

@pagelab
Copy link
Contributor

pagelab commented Jan 16, 2020

In that setting users specifically should have no or at least very little control over the styling of the site.

@kraftner This is really spot on and I just want to point out that this level of control already happens “out there”. There's currently no shortage of plugins that empower the user to micromanage it's own website layout – even to it's own detriment.

By implementing this new system in core, that practice would be finally condoned by WordPress itself, which IMO is not in the best interest of that specific kind of user that have no clue about what design systems are, and therefore, their relevance and impact. And I would even say that this kind of user represents the majority of the user base.

This is something important to tackle before moving on.

@mtias
Copy link
Member

mtias commented Jan 16, 2020

Thanks for your detailed thoughts, @kraftner, these are good points.

All of the above is mostly concerned with maximum flexibility and control for "Users", offering them a solid base in the form of core and theme styling but giving them the final say and a broad array of capabilities to alter the styling of a website.

I don't think this is necessarily the case here. The system proposes a way to structure the style capabilities of a block in a way that can be better controlled, but it doesn't say much about who will bear this control. It's entirely possible that most of this would just be exposed as theme builder tools, to admins and designers, not necessarily to end users.

Think of it more as the evolution of the "theme editor" than necessarily giving control of all the style details to users. The latter would also not be great to most users, since it would be quite overwhelming, but themes need better and easier control over blocks (particularly blocks they don't know even exist!).

Sites where there are multiple users as well as a clear separation between designers, developers and editors. In that setting users specifically should have no or at least very little control over the styling of the site.

This is perfectly reasonable, and it should go without saying that a site would have control over whether these tools are exposed, to which roles, and which capacity — the same way your regular users don't have access to the PHP theme editor.

And even the Core Styling might be too opinionated. Instead the Theme Level should be the first, last and only one to decide how everything is styled.

It should be in your control to setup things this way — don't load default block styles (outside of the structural ones, maybe) and restrict the ability to edit global and local block style options. I think what you outline is a perfectly valid use case that should be easy to accommodate and improve upon.

So if this whole system gives even more styling control to users than we already have right now please make sure that any setting that is being introduced as user-controllable has a way to be disabled, restricted or overwritten.

Definitely. Again, I see this foundation as giving more deliberate control to the theme over blocks. A side effect is that we end up with a system that could also be exposed to users if they want to have that level of control using a GUI rather than writing CSS and modifying PHP templates by hand, but this is not going to be a one-size-fits-all-use-cases, as the world of WordPress is incredibly diverse and we need tools that accommodate diverse use cases.

If there are more examples of things that you feel are relinquishing that control from you, please, report them as issues so that it could be discussed and addressed. I do agree that currently it can take effort to go through all the things you might want to disable. Part of the motivation for a more comprehensive block style system is to make this easier to control — either by opening up or closing it down.

@kraftner
Copy link

I think what you outline is a perfectly valid use case that should be easy to accommodate and improve upon.

This sounds great @mtias. I just wanted to re-emphasize this, especially the point that I think that it should be a blocking requirement for each and any of these user-controllable features to only go into the plugin or at least core when it comes with an option to control (configure, restrict, disable).

The reason I came here basically is due to the fact that you have pretty much said the same thing in the Office Hours at WCEU in June when you, me and @m where talking about this particular topic. But I already then had the impression that this was seen as a "nice-to-have" thing for later, not a hard requirement or even a priority. But without such control it is currently practically impossible to use Gutenberg in a tightly controlled setup.

Concerning the examples of the loss of control I am talking about - most of them already have issues, but unfortunately for a lot of them there is little to no movement. Often I also don't understand how corresponding bugs can even happen, why turning it off apparently isn't even tried for new features during development.
So in the end it feels like a very frustrating "Whac-a-mole" situation where for every feature one can regain control of, two more that are not controllable are entering the editor. Just to emphasize what I am talking about, here is a list of issues I am currently or was previously struggling with, having no or only hacky ways of controlling them. (This is right out of my still ongoing research for my upcoming WC talk on this very topic)

I know that at the end of this list I am somewhat digressing from styling issues, but I believe all of this together shows the overarching theme: Taking control of Gutenberg is still somewhere between hard and impossible.

Don't get me wrong: I do understand that Gutenberg is still under heavy development and looking at all of these issues in isolation one can of course say bugs happen, nothing is ever perfect and we can only work on one thing after the other. But the sum of all of the above makes me feel like it isn't seen as a priority by the project leadership and instead of solving this important issues with content editing first, we're already pressing ahead to the next, even more complex area of full site editing.

TL;DR: Control over users capabilities is an important and urgent need across the whole of Gutenberg, and I really think that it is high time to put some more focus on this. Please, please give us back control before implementing any more features. 🙏

@talldan
Copy link
Contributor

talldan commented Jan 21, 2020

I don't want to dig into the weeds too much, but one thing jumped out to me in one of the examples from the issue description:

For blocks to start using global styles, their CSS will need to be refactored to use the CSS variables outputted by system.

and then this is part of the example:

border: 1px solid var(--wp-gs-color-primary-dark20);

Colors are always difficult when it comes to theming because it's hard to base them on a particular semantic. Essentially this is hard-coding a relationship between the theme schema and a block's style, and that might be restrictive. I might be wrong, but I think it means the number of colors and types of color that can be defined are fixed. Also the block implementor has to make a judgement about the theme's configuration and what sort of color this should be. I wondered if there might need to be a separate layer where these styles are wired up to the theme's schema.

Instead, this is maybe defined as:

border: 1px solid var(--wp-button-block-border-color);

And then a relationship is defined (by the theme?):

--wp-button-block-border-color: --wp-gs-color-primary-dark20

Not sure if I've explained that clearly. It's already sounding quite complicated, but it's something I anticipate might need to be solved.

edit: On further though not sure how what I've proposed would work with third-party blocks. Perhaps the issue is that semantics need to be defined more clearly, so we avoid naming something 'dark' or 'light'.

@karmatosed
Copy link
Member

As I am visual, I wondered if this might help anyone to see what gets effected each time something was iterated. For example, how the layers filter down. This builds on the pyramid cascade @ItsJonQ created and is done in collaboration.

Flow_ typography

What this does is look at specific actions:

  • Globally changing a style: for example, increasing baseline font size. In this case it goes across everything as global. The dots show that is the top (respecting the hierarchy) and what is therefore affected.
  • Local/document changing a style: for example baseline font size increase just on the about page.
  • Block style changes: for example just changing baseline font size just on a quote block.

If you notice there are 2 columns which don't have any interaction there, these persist throughout before you do any interactions.

Another point to note blocks includes a few things:

  • Style variations
  • Custom styling
  • Developer (plugin) block styles

In this image, the theme is the layer next to the core default, anything that changes globally above that all way up to the block at the top.

@ItsJonQ
Copy link
Author

ItsJonQ commented Jan 22, 2020

Global Styles - The First Iteration

This morning (Toronto time), I hopped on a call with @nosolosw and @jorgefilipecosta to sync up on this new Global Styles focus.

We have each independently worked on this problem at one point in time. Given that many aspects of Global styles is brand new (in the context of Gutenberg), and the complexities involved in all of the various moving parts, we felt like it would be good to sync up - to share our experiences, thoughts, experiments, and to start planning.

The 3 Parts

gs-mechanics-flow

After discussing the various implementation details and various edge cases, we simplified the original proposed concept into 3 main parts:

  • Resolver (we called it "Merge" during the meeting)
  • Blocks
  • Controls

These 3 parts still embody the spirit of the original design, except the "Transformers", "Hooks", and "Renderer" pieces were simplified and consolidated into "Resolver" and "Blocks"

Resolver

The Resolver is responsible for checking a theme.json file and the previously saved global styles data from the database. It will then merge the dataset together and prepare something for Gutenberg to consume.

Blocks

Blocks are the Gutenberg blocks we all know and love! Updates will need to be made to the blocks' .css to start using the CSS variables that will be core to the Global Styling system.

Controls

Controls are user-interfaces that enable the user to adjust global style values. These will be presented within the Full Site Editing experiences.

Note: Controls also exists for individual block instances. They are the various control fields that are available in a block's InspectorControls. The update will only adjust how the style values are applied, i.e. adding inline style CSS variables, rather than hard coded values. (More on that later)

Flow ("Backend")

The following will detail the backend flow of the Global Style system.

Resolver

gs-resolver

When the Resolver starts, it first looks locally for a theme.json file.

1. theme.json

For this proposal, a theme.json file is an easy way for a theme to:

a. Opt-in to global styles
b. Declare theme specific style values

An example of a theme.json may look like this:

{
	"name": "Awesome Theme",
	"global": {
		"color": {
			"background": "black",
			"text": "red"
		}
	},
	"blocks": {
		"core/paragraph": {
			"color": {
				"text": "hotpink"
			}
		}
	}
}

In this example, the theme is overriding the global default color.background and color.text. It's also specifically adjusting the core Paragraph block's color.text.

2. Checking the Database

If a theme.json is found, the resolver will then check the database for previously saved global styles.

Theme Specific global styles

Global styles are to be saved in a way that's specific to a theme. This enables the ability to safely switch themes without style aesthetic conflicts.

This can be demonstrated in this demo

(Try applying some global / document styles and switching the themes).

3. Merging

The theme.json data and database data are merged and prepared to be consumed by Gutenberg.

Block

gs-block

1. Rendered

Blocks are loaded and rendered by the Gutenberg editor. Blocks use a series of CSS variables that correspond to the global styling conventions.

CSS Variables

The following is an example of the core Paragraph block using Global Styles:

p {
    color: currentColor;
}

.wp-gs p {
	color: var(--wp-gs-paragraph-color-text, var(--wp-gs-color-text));
}
.wp-gs scope

For our initial implementation, the idea is to apply a specific CSS selector (.wp-gs) to either the html or body DOM node to effectively establish a "Global Style supported environment".

This raises the CSS specificity for globally styled blocks.

In the above example, a <p> rendering without global styles (like how they do today) would inherit it's color from a parent selector.

However, within a "Global style supported environment", the .wp-gs p rule would apply.

var(...)

The convention we're going with is to prefix the global style CSS variables with --wp-gs (WordPress Global Styles). In the example above:

.wp-gs p {
	color: var(--wp-gs-paragraph-color-text, --wp-gs-color-text);
}

var() is setup to:

  1. Handle custom block specific style rendering
  2. Fallback to generic values

The convention of using block specific styles with fallbacks gives the system both finer grain control as well as stability.

2. Block x Controls

If a block instance has custom aesthetic changes, e.g. a textColor update, the updated attribute(s) will be rendered as inline styles. The difference between the current implementation and the proposed implementation, is the use of CSS Variables over hard coded values.

Current:

<p style="color: red;">...</p>

Proposed:

<p style="--wp-gs-paragraph-color-text: red;">...</p>

In many ways, the effects are the same. However, using CSS Variables allows for most of the CSS cascading effects to apply.

Controls

gs-controls-3

Controls will be present within the site edit flow from Full Site Editing.

1. Update

gs-controls-1

When an update is made, the updated attribute is sent to the resolver.

gs-controls-2

The resolver does it's thing (perhaps coordinating with the @wordpress/data store), and pushes the updated consumable data back down to Gutenberg.

gs-controls-3

Lastly, that updated data (as well as updated CSS variables) are hydrated and rendered to the FSE user interface, and any visible blocks.

Flow ("Frontend")

The solution we've come up with (currently) requires the Resolver to check/read the theme.json and database to get and create the latest combinations of style values for a given page.

This happens server side on page load.

The Resolver will then create a <style> tag to be injected into the <head> of the page on load. Example:

<head>
    ...
    <style>
        :root {
            --wp-gs-color-text: black;
            --wp-gs-color-background: white;
            --wp-gs-color-primary: blue;
            ...
            --wp-gs-paragraph-color-text: hotpink;
        }
    </style>
</head>

Lastly, the .wp-gs CSS selector needs to be injected to activate the global styles from blocks.

- <html>
+ <html class="wp-gs">

And that's it!

The First Initiative

@nosolosw, @jorgefilipecosta, and I will be working together to create the 3 parts outlined above:

The first initiative we're going to be building for allows for users to:

  1. Set global styles for default text color
  2. Enable the core Paragraph + Heading blocks to use global styles
  3. Allow a theme to customize the default text color, heading and paragraph block text colors

Gotchas

The biggest gotcha at the moment is we're unsure how to properly support IE. Our current system relies very heavily on CSS Variables, and browser native feature that is not supported in IE11.

There are polyfills available, but there are not sufficient to do the rendering consolidations that we need (at least, not out of the box).

We are very mindful of this gap. For now, we're going to proceed with a CSS Variable based solution, as we want to validate the mechanics and flow of the system. Not only do they work, but they should not break backwards compatibility, and they should help Block Builders, Themers, and Users feel empowered rather than overwhelmed (by things like verbose API or CSS fiddliness).

Feedback + Thanks!

First off, if you've made it to the read (whether you've skimmed this or not), thank you so much! I know this was an awful lot to read. ❤️

My intention is to be as transparent as possible in regards to any designs, experiments, and implementations.

Global styles involves a lot of moving parts. The more context + updates I can give, the more informed folks involved with this project can be. Which will lead to better discussions and decisions.

As always, thoughts and feedback welcome!!

Thanks again :)

@ItsJonQ
Copy link
Author

ItsJonQ commented Jan 31, 2020

Oxygen is already doing that in a wonderful way, allowing to change global colors AND seeing the effect of it live. I hope this behavior will come to the Global Style System in some way too.

@dnnsjsk Thank you for your support and for that lovely example!

Yes, we have to support this interaction.

Ultimately, this entire effort is to improve (end) user experience. Not only should the adjustments change what you expect, but the experience must feel good while you're making those adjustments. It should feel creative, freeing, and fun. Which is in line with the goals of the Gutenberg editing experience :).

Admittedly, this experience is hard to pick out based on the wall of text I've shared in this issue 😅. I've mostly focused on the technical aspects and how those pieces will work together. In addition to a good end-user experience, the developer experience must be good. The workflows we establish feel easier and better for Themers and Block devs.

@karmatosed, @shaunandrews, and others are starting to explore the design/UX of this experience here:

#19255

We're still very early! I think the recent Roadmap will help the dev x design efforts work together.

@jasmussen
Copy link
Contributor

Hello! I just wrote a long comment on #19255 (comment) which I should have probably have posted here instead? In any case the key takeaway from that comment is that as part of an effort to design a basic block pattern, I inadvertently created a wishlist of items I'd love to see the global style system able to accomplish (like font-weight, line-height, dropcap-size), mostly things I've already seen mentioned.

@carike-codes
Copy link

Please make the sliders "a bit sticky" (smooth sliders are a terrible user experience over large ranges) and display the current value.

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 5, 2020

@carike-codes We have those featrures in the updated RangeControl component 🙌
#19916

The slider behaves exactly like how a default HTML input[type="range"] would. In a situation where there are many steps, example, min: 0, max: 1000, step: 1, it'll feel "smoother", as there are 1000 values to increment over

(Hope that helps)

@Tug
Copy link
Contributor

Tug commented Feb 6, 2020

Note: This comment is about react-native compatibility

That API would not include the theme default styles, just the user customizations. So we will probably need another API that provides the join of WordPress defaults with theme defaults. This problem is pretty similar to what happens with FSE...

So if I understand this correctly we'll want to implement a client-side version of the resolver that will consume that API? Wouldn't that also help for FSE to be able to update the blocks styles as global styles are updated in the Controls?

If that's the case gutenberg native will want to reuse this client-side resolver.

There is still the issue of css variables which are not supported on react-native. A postcss transform won't work as we need those to be dynamic as to update with a new fetch to the API.

Taking into consideration the fact that we can only ever use inline styles in native so we'll need an extra module to fill in the style for any particular block in the editor.

Let's take an exemple of block styling where the user overwrote the text color for this block in the InspectorControls, we have this:

--wp-gs-color-text: #fff;
--wp-gs-background-color: yellow;
--wp-gs-paragraph-color-text: #000;

.wp-gs p {
	color: var(--wp-gs-color-text);
	background-color: var(--wp-gs-background-color);
}

<!-- wp:paragraph {"textColor":"red"} -->
<p style="--wp-gs-paragraph-color-text: red;">
Lorem ipsum dolor sit amet</p>
<!-- /wp:paragraph -->

On mobile we need to generate the same exact code, so save must be unchanged. However, edit must avoid using css variables:

<RichText style={ { color: "red", backgroundColor: "yellow" } } .../>

I'm not sure what are the implications in terms of writing X-platform blocks yet. Writing such a module that automatically resolve all styles for a particular block should be doable, at least if we limit ourselves to some CSS properties and if we don't have several customizable elements inside a RichText component.
Web and mobile could probably share this although it might have a performance impact in which case we'll need this only activated on mobile. @jorgefilipecosta @koke Not sure if you have any thoughts on this?

@koke
Copy link
Contributor

koke commented Feb 7, 2020

@koke Not sure if you have any thoughts on this?

Yes, I share similar concerns, but I don't have any clear answers yet. My current instincts are that our best bet might actually be improving the CSS parser that we use on native to actually support CSS variables (and a few other things, like dark mode, media queries, and @supports). Instead of trying to convert the CSS to a StyleSheet in one step, we could have it generate a DynamicStyleSheet that would be able to adjust some variables and queries at runtime. This sounds like a big effort, so I'm still looking for alternatives that might be easier.

@Bowriverstudio
Copy link

Could I use this to overwrite scss variables defined in bootstrap?

@ZebulanStanphill
Copy link
Member

I just finished reading Adventures With Complex Global Systems in Block-Based Websites - Part One, and I thought it would be a good idea to share it here. It's a long, but interesting read, which might give some people here some ideas.

@johnstonphilip
Copy link
Contributor

johnstonphilip commented Mar 25, 2020

We have been playing with Global Styles and have a few thoughts/ideas/questions.

Current direction of Global Styles

If I understand the current direction, the idea is to hardcode global style variables into blocks so that they use them.

The difficult part about this is that granular controls could end up being extremely numerous and extensive, and also hard to utilize in custom places. For example, these might be the way granular controls work (conceptually speaking, not an syntactical example):

gs-background-color
gs-another-background-color
gs-yet-another-background-color
gs-h1-font-color
gs-h1-font-color-exception
gs-yet-another-h1-font-color-variation
...many more variables required

wordpress comcolors

Another idea for the direction of Global Styles

After playing with that approach we were wondering if global styles could be more "thing-agnostic", and simplified like this:

gs-color-1
gs-color-2
gs-color-3
...more variables required, but no values are ever duplicated

The idea here is that blocks can choose if they want the background to be gs-color-1, gs-color-2, or gs-color-3. Possibly even through a global-style-selector UI. Here's a really rough mockup of how that might look.

Screen Shot 2020-03-25 at 11 26 58 AM

A few advantages to this approach:

  • Less total global styles variables required.
  • Application of styles moves to blocks, allowing for much more design flexibility.
  • No duplication of values required (choose #fffff as gs-color-1 once, instead of many times)
  • Block users have the flexibility to use a global style wherever they see fit. For example, it could be for the background, or the font, or the border, but all could use gs-color-1, instead of defining #fffff 3 separate times.

@cbirdsong
Copy link

cbirdsong commented Mar 26, 2020

If I have to pick between the two, I'd go with Approach B, but the version of this I've landed on for themes is a less granular version of Approach A built on top of Approach B.

First, I define a bunch of colors:

--white: #ffffff;
--gray-light: #d4d4d4;
--gray-dark: #333333;
--black: #141414;
--aqua: #7fdbff;
--blue: #0074d9;
--navy: #00203f;
--teal: #38cccc;
--fuchsia: #f012bc;
--purple: #b00dc9;
--maroon: #85144b;

(I can see the advantage of your proposed more generic --color-1 type setup for Wordpress at large, but the principal is the same.)

Then, I map those onto some relatively generic high-level variables like these:

--text-color: var(--black);
--text-color-secondary: var(--gray-dark);
--accent-color: var(--aqua);
--background-color: var(--white);
--heading-color: var(--navy);
--action-color: var(--purple);
--action-color--invoked: var(--maroon); /* hover & focus states */

These can then be cascaded down through more specific variables as the theme requires:

--focus-ring-color: var(--accent-color);

--link-color: var(--action-color);
--link-color--hover: var(--action-color--invoked);

--input-color: var(--text-color);
--input-border-color: var(--input-color);
--input-background-color: var(--background-color);

--button-color: var(--action-color);
--button-color--hover: var(--action-color--hover);

/* whatever other theme-specific variables I want to make up */

Theme authors could get as granular as they want here (--h2-color, --page-title-color, --subhead-color, whatever), but they could always try to build off of the core with the cascade in mind. For example, if I was adding a block with a dark color scheme in my theme:

.is-theme-dark {
    --text-color: var(--white);
    --text-color--secondary: var(--gray-light);
    --accent-color: var(--aqua);
    --background-color: var(--navy);
    --heading-color: var(--blue);
    --action-color: var(--fuchsia);
    --action-color--invoked: var(--purple);
    /* whatever other theme-specific variables I want to switch */
}

Switching gears, someone making a recipe plugin who has used the top-level colors like so would have a decent chance of just working fine when dropped into the theme author's .is-theme-dark group block:

Recipe plugin style example
.recipe {
   color: var(--text-color);
}
.recipe__image {
    border: 0.5rem solid var(--accent-color);
}
.recipe-print-button {
   color: var(--action-color);
}
.recipe-print-button__icon {
   color: var(--accent-color);
}

@dashkevych
Copy link

Regarding IE11 support:

Have you checked this solution: https://github.com/jhildenbiddle/css-vars-ponyfill ?

@oandregal
Copy link
Member

I was suggested to bring clarity on where we are regarding the Block Style System. I'm trying to consolidate what we have in the two main threads: vision #9534 and specific steps #20331 which I'll update in the following days.

Since this issue was created we've advanced a lot, implementation-wise: we have a working theme.json as well as a more mature understanding of its role to control the editor, we've created new design tools, an initial mechanism to connect the block & global levels of the block style system, and more than I'm missing.

This issue has served its purpose, so I'm closing it. The above hints should serve as a testament of how fundamental this conversation has been to uncover the concrete steps we needed to walk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json Needs Technical Feedback Needs testing from a developer perspective.
Projects
None yet
Development

No branches or pull requests