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

Tracking: Add a Style Engine to manage rendering block styles #38167

Closed
16 of 25 tasks
andrewserong opened this issue Jan 24, 2022 · 39 comments
Closed
16 of 25 tasks

Tracking: Add a Style Engine to manage rendering block styles #38167

andrewserong opened this issue Jan 24, 2022 · 39 comments
Labels
[Package] Style Engine /packages/style-engine [Status] In Progress Tracking issues with work in progress [Type] New API New API to be used by plugin developers or package users. [Type] Tracking Issue Tactical breakdown of efforts across the codebase and/or tied to Overview issues.

Comments

@andrewserong
Copy link
Contributor

andrewserong commented Jan 24, 2022

This is a tracking issue for implementing a Style Engine as discussed in improving our saving/rendering of block styles. The goal is to have a consistent API for rendering styling for blocks, across both client-side and server-side applications. The server implementation is preferred for a site's frontend.

Rather than a monolithic refactor, the aim should be to build the smallest implementation possible and land it in the plugin, and iteratively enhance it with small PRs as we go.

In principle, the style engine should be able to receive a style object, and return the rendered styling for that style object, along with any required class names or other data needed to reassemble or use those styles in all required environments. Depending on the implementation, it may be possible for us to avoid or minimise the current reliance on rendering inline styles directly into block markup.

⭐ Current phase - Phase 1: block styles consolidation and refactoring the layout abstraction

Goals:

  • To audit and consolidate where the code generates block support CSS in the backend so that they are delivered from the same place (as opposed to multiple places). This covers CSS rules such as margin and padding, typography, colors and borders.
  • Removing repetitive layout-specific styles and generating semantic class names for each layout.

Planning discussions

To-do

The below to-do list is an assortment of shorter-term to longer-term features that would be great to implement. We should aim to not let the longer-term goals block landing the shorter-term goals — but let's try to use the longer-term goals to inform the design decisions for the shorter-term ones.

The approach will be work iteratively rather than treating this as a project that needs to be complete all at once.

Project board

We've started a project board for managing the above tasks over in: https://github.com/orgs/WordPress/projects/19/views/1

Relevant discussions

@andrewserong andrewserong added [Status] In Progress Tracking issues with work in progress [Type] Tracking Issue Tactical breakdown of efforts across the codebase and/or tied to Overview issues. [Type] New API New API to be used by plugin developers or package users. labels Jan 24, 2022
@ramonjd
Copy link
Member

ramonjd commented Jan 24, 2022

Add support for de-duping common or repeated styles so that there are no duplicate styles rendered.

We already use cssnano, which can do a lot of this stuff.

From basic testing, it seems like if we stored the styles (in a store?) and then ran them concatenated through cssnano we might get something useful. I can take a look at this further.

@youknowriad
Copy link
Contributor

Refactor layout support to use the styles engine.
Refactor blockGap support to use the styles engine.

These two styles are different than the others in the sense that the generated styles shouldn't be applied to the "block wrapper" but instead to the "inner blocks wrapper". The difference is subtle since for most containers blocks, these two wrappers are the same but for something like Cover, they are not the same and that's why it's not possible to enable the current implementation of Layout bock supports for that block. (we need to first refactor the block support to apply to the inner blocks wrapper). I think that's something important to have in my mind while iterating on the style engine to avoid repeating the same mistake we have now.

@andrewserong andrewserong changed the title Tracking: Add a Styles Engine to manage rendering block styles Tracking: Add a Style Engine to manage rendering block styles Jan 25, 2022
@priethor priethor mentioned this issue Jan 28, 2022
57 tasks
@andrewserong
Copy link
Contributor Author

I believe the initial version of the style engine PR (#37978) is in a good place for wider feedback / review now, and to confidence check the approach. I've added a tiny comment/update to the PR.

@andrewserong
Copy link
Contributor Author

Just adding that there's been some discussion in #38277 and #27075 about exploring a standardized way to modify hover/focus/active states for blocks — it'd be good to keep an eye on these discussions to see if/when/how the style engine might slot into those explorations.

@gziolo
Copy link
Member

gziolo commented Feb 11, 2022

It looks like the automatically generated class names to move granular class names and styles outside of the block's HTML to the style tag introduce some challenges. See #38719 and #38694. It would be great to keep that on the radar while working on the Style Engine implementation.

There are vast benefits of the single automatically generated class name for a given block:

  • no excessive pollution of the HTML for the block that simplifies validation and deprecation handling
  • the ability to target inner HTML tags through CSS syntax generated on the server with style tag instead of complex handling through __experimentalSkipSerialization flag in block support.

However, it would be great to keep the flexibility of the current blocks that use several discrete class names to indicate selected block customizations like colors, fonts, etc.

@andrewserong
Copy link
Contributor Author

Update: the initial version of the style engine package has been merged in #37978, so we're now in a good place to start opening up smaller PRs to explore some of the ideas raised in discussions. I'm keen to have a play with how the server-rendering implementation might look.

Thanks @gziolo for linking in those related discussions. And good points about pollution of the HTML and targeting inner HTML elements. Since we're moving in a direction where most of the styles will ultimately be rendered server-side, hopefully that means if we expose discrete class names, it'll be fairly safe, because we'll be bypassing the issues of block validation and deprecations in post content.

Let's look at these issues in more depth in the discussions and related PRs.

@fwazeter
Copy link
Member

Glad to have found this tracking issue! Thanks @ramonjd for linking it! Which channel in slack is this stuff discussed in? Been looking for months for discussion around these issues!

Developed a CSS API via plugin to handle this exactly - taking sources from global styles, a CSS file (re-tooling some old PEAR PHP package for parsing CSS from css files), CPT etc, universalizes it then renders the styles inline, which was originally meant as a way to override the implementation of Layout Support (since we've been using FSE in production sites since 5.8 launched with the Gutenberg plugin).

I'd be happy to migrate the PHP classes to WP's coding standard (I've been writing in PSR style and tend to make smaller classes split up among files using interfaces & setter injection via containers) to get the ball rolling on a PHP side implementation.

In the plugin we made, we've been using a CPT as a data store for values, following the logic behind block-templates & navigation block. The flow is a little like this:

  1. files from a repository class are sent to the CSS parser (e.g. from core blocks, fromAPI (wp_get_global_stylesheet), from CPT (e.g. internal), from JSON.

  2. Parser outputs CSS into declaration block associative arrays, e.g.

"declaration_block" => array(
        "property" => "value"
        "property" => "value"
        ...

Where the declaration block acts as the selector name.

  1. Copy of these are saved as a stylesheet CPT with the corresponding declaration blocks

  2. Stylesheet(s) are enqueued and rendered as inline styles.

  3. for Layout Support, if block attr doesn't exist for (contentSize, wideSize, flex props, blockGap etc), then it creates a new CSS declaration block matching user input with a name suffix like __600px. If the style already exists in the stylesheet(s), rather than creating a new property, it simply uses the existing property.

  4. adds the class to the block that has the support flag.

This is also done for overriding core block styles to deregister and reregister new default block styles based on user / developer preference, which is the main reason for the CPT - the CPT provides an easy front end interface to 'hot swap' values on the defaults.

So, for example, if I wanted to change all 'margin-left' properties of all blocks & auto generated styles (e.g. from layout support), to be margin-inline-start, then I can just change a single setting and the above API does all the work for me, replacing the corresponding properties, but not their values - or vice versa.

What we're also experimenting with is, rather than using a new class name for each user defined custom value (which doesn't happen often - 99% of the time a block using contentSize and wideSize is just using the theme default, we've literally overrode this with a custom value once across ~13 production sites in the wild.

For instance, if you assign contentSize and wideSize (or a flex property or whatever) to a custom property, and then add a css style that says "#make200" that sets the value of the customproperty to something different, then it'll change the custom prop just for that unique id - eliminating the need for a new class altogether.

<style>
--wp--style--content-size: 800px;

.wp-container-default {
   max-inline-size: var(--wp--style--content-size);
}

#make200 {
--wp--style--content-size: 200px;
}
</style>

<div class="wp-container-default">
     <p>My container is 800px wide</p>
</div>

<div class="wp-container-default" id="#make200">
     <p>My container is 200px wide</p>
</div>

Ultimately, the trickiest / most verbose part comes in expanding flex properties and a custom user setting, and so far the cleanest solution has just been to break up the overall declaration block into smaller declarations, but that does make for a long class="" line. In the end, that one just gets tricky if a box has an option to be both say, horizontal and vertical and have a variety of different options - you end up with a large matrix pretty fast.

Adding global / common alignment options for e.g. items-justified-center, etc as already exists seems to be the most prudent there - we just tap into those already declared classes rather than re-rendering them in a separate call.

For cases like row vs column, it's probably just more prudent to keep handling that via block style variants, such as is already done with row block & group block.

We also reduce the overall styling duplication with a couple smart global defaults, like:
* { box-sizing: border-box; } and then a hook into entry-content / main the_content tag for something like:

main > * {
box-sizing: content-box;
padding-inline-start: var(--wp--style--block-gap);
padding-inline-end: var(--wp--style--block-gap);
}

The above allows us to remove any box-sizing property in core blocks, because they by default inherit border-box and inline elements aren't effected. Then the direct children only of the post_content area have padding visibly only on mobile completely preventing the problem of a paragraph block's text being flush with the side of the mobile device.

The slight tweak to .has-background on a paragraph element to make it still flush is like this:

/* in this implementation we were assigning the box-sizing prop to a custom prop in theme.json, with a default to border-box */
p.has-background {
    --wp--custom--box-sizing: content-box;
    box-sizing: var(--wp--custom--box-sizing);
}

This keeps the element in alignment as expected and not doubled up on padding via inheritance.

@fwazeter
Copy link
Member

Refactor layout support to use the styles engine.
Refactor blockGap support to use the styles engine.

These two styles are different than the others in the sense that the generated styles shouldn't be applied to the "block wrapper" but instead to the "inner blocks wrapper". The difference is subtle since for most containers blocks, these two wrappers are the same but for something like Cover, they are not the same and that's why it's not possible to enable the current implementation of Layout bock supports for that block. (we need to first refactor the block support to apply to the inner blocks wrapper). I think that's something important to have in my mind while iterating on the style engine to avoid repeating the same mistake we have now.

Can personally attest to how finicky this subtly is to work with. The cover block overall is simultaneously one of the most useful blocks in the arsenal, but also the hardest to work with and tweak because of it's structure. Part of it's finicky nature is the fact that it's trying to handle so much at once - it's pretty much got n possibilities of configuration.

Current manipulation to specific sizing usually involves stacking it within other container blocks or other container blocks within the cover block to achieve desired widths inside/outside (even stacking covers inside covers!). In the end we made a few custom blocks that separated out that logic, rather than try to contain it all within a single block, and used block patterns to handle the re-use.

I've got thoughts on a few potential work arounds, but will have to consolidate thoughts on them later.

@andrewserong
Copy link
Contributor Author

andrewserong commented Feb 18, 2022

Which channel in slack is this stuff discussed in?

Thanks for sharing your thoughts here! Just adding that most of the discussion is happening async in Github, which works nicely for a lot of us since there's such a wide timezone spread of people contributing to the discussions. (Many of which began in this discussion thread). I've linked a few of the discussions and issues in the issue description. If there are issues or discussions that anyone thinks should be included here, please feel free to link to this tracking issue and we can update the description so that they're all gathered together.

@mrwweb
Copy link

mrwweb commented Feb 22, 2022

I just opened a ticket that overlaps with many of the things being discussed here on #38998. As it relates to this ticket, I hope this can call attention to the needs of theme and plugin developers for core markups and styles to be consistent and easy to customize, both through standardized conventions in WP and with custom CSS.

@gziolo
Copy link
Member

gziolo commented Feb 24, 2022

As reported in #38917 by @oldrup, after upgrading to WordPress 5.9, sites have issues with HTML validation: "Error: Element style not allowed as child of element body in this context". These errors seem to be related to the styling of blocks when .wp-container-* class is injected in the HTML. It's something that should be included in the planning of the tasks for the style engine.

@ramonjd
Copy link
Member

ramonjd commented Feb 24, 2022

after upgrading to WordPress 5.9, sites have issues with HTML validation: "Error: Element style not allowed as child of element body in this context".

Thanks for the linking that issue. I think it's something we should prioritize, not only to have the satisfaction of receiving digital praise from online validators, but to avoid errors in browsers we haven't tested.

@mrwweb
Copy link

mrwweb commented Jun 15, 2022

Thanks for posting this big update, @ramonjd. It's helpful as I continue trying to grok this work.

I want to briefly share my own experiences following the style engine work and suggest something that might hopefully help make sure the result of the style engine is positive for everyone involved.

Here's been my experience trying to follow and provide input (I suspect many others are in the same boat):

  1. I'm trying really hard to follow along with the style engine project because it seems to have a big impact on the future of front-end output which impacts my work building custom themes for clients.
  2. It is really hard to figure out exactly where the boundaries of the style engine scope are and how/when to provide feedback. I'm pretty sure that's because it's not completely defined yet, but it does make it hard to know exactly what comments will be useful.
  3. A lot of the planned phases seem to move directly through a ton of known pain points for themers such as inline styles (and therefore missing semantic classes), mostly redundant style blocks, etc. I don't quite know how to interpret that fact, but it all makes me extremely nervous about the long-term results of this project.

Here's what I think might really help: Can there be some work done and communication around the final desired outcome of the style engine product as represented by working front-end code? Even after the recent big update on progress and plans, it's still very unclear to me what the final shape of front-end code will be.

If I understand this right, evaluating the success of the Style Engine project, once complete, comes down to whether the front-end code is both efficient and extensible, not how the backend makes it possible to output that code. Therefore, it seems that work should be done backwards from the target markup, which is also something that I and many others could give much more valuable feedback on.

Working backwards from some proof-of-concept front end code would also have other benefits like making it possible to evaluate some of the assertions around the various benefits between inline styles, inlined style blocks, and static CSS files.

@ramonjd
Copy link
Member

ramonjd commented Jun 16, 2022

Thanks for taking the time to write down your comments and questions @mrwweb

I'll do my best to answer with as much information as I have 🤞 Maybe grab a tea or kombucha first.

The usual disclaimer is warranted: these are my own ideas and I'm expressing them using my limited knowledge.

The TL;DR for folks who don't want to read my babble is that:

  1. Right now, my efforts, at least in relation to the style engine, are focussed on consolidating where we generate styles.
  2. At some stage we must have a discussion about how, and if, the style engine will solve issues associated with styles/classnames in Gutenberg.
  3. Defining a "perfect" or "desired" state of the frontend code will assist to guide work and discussion in this area (not just the style engine)

I'm trying really hard to follow along with the style engine project because it seems to have a big impact on the future of front-end output which impacts my work building custom themes for clients.

It's not just you 😄 I joke that the style engine will also achieve interstellar travel and decrypt the Voynich manuscript such have the goals and potential of the project been inflated with various offhand comments such as "Oh, the style engine will take care of that".

If strapped to a chair with a hot poker to my eye, I would confess that the style engine will one day allow us to reason better about and even implement solutions to the big problems you've cited. This is the hope.

However, I'm sitting in Phase 1 and I might be there for a while.

As mentioned above, Phase 4 is when we can really start thinking about how to leverage a consolidated system of style generation.

Right now the aim is not to invent new ways to generate styles and classnames. Rather, the initial goal is to centralize 'where' we generate styles.

As you're aware we're rendering styles in a bunch of areas. It's not optimal. It's not pretty, and we have to rein them in.

Controlling 'where' Gutenberg generates styles I think is the first step towards addressing the 'how'.


It is really hard to figure out exactly where the boundaries of the style engine scope are and how/when to provide feedback. I'm pretty sure that's because it's not completely defined yet, but it does make it hard to know exactly what comments will be useful.

I share your concern, and agree that it comes down to the lack of concrete plans in relation to the desired state of the frontend code.

I'm personally focussed on the centralization effort at present, and have therefore made peace with the fact that it might take time to get to point where the style engine is ready to assume the responsibility of processing and rendering better frontend CSS.

So for me, the boundary is Phase 1: generating styles that previously were generated in block supports, and later, by global styles.

Bear in mind I feel like I've been working in a U-boot, and I need nudges like yours at times in order to surface and see the horizon. 😄

Nevertheless, my opinion is that, once the plugin can start responding to known pain points via code, for example, ridding ourselves of redundant style blocks, feedback on these specific points in the context of the style engine will be invaluable.


A lot of the planned phases seem to move directly through a ton of known pain points for themers such as inline styles (and therefore missing semantic classes), mostly redundant style blocks, etc. I don't quite know how to interpret that fact, but it all makes me extremely nervous about the long-term results of this project.

The issues you've mentioned are the things that really need fixing.

It's important to me to other folks working closely with styles to understand these problems, and listen to your advice and experience so that contributors can agree on a "final desired outcome".

It's also possible that some of theme will be incrementally addressed by parallel efforts outside the current style engine scope.

For example, @andrewserong is looking at introducing semantic classnames to our layouts in #40875. (See also the surrounding layout discussions in #39336)

With regards to unease about the "long-term results" of this project, I think one should be careful about overpromising what the style engine will deliver, at least for today.

Yes, Gutenberg has to improve the way it approaches styles and classnames. To get there, I think it first must cease the adhoc printing of styles + classnames and have a central, unified means of generating them.


If I understand this right, evaluating the success of the Style Engine project, once complete, comes down to whether the front-end code is both efficient and extensible, not how the backend makes it possible to output that code.

If the plugin were to be measured by whether "front-end code is both efficient and extensible", then yeah, the test results wouldn't be flattering.

I'm trying to get to a point where the style engine can incrementally improve the situation.

You've made a great point: the frontend code will be the proof of success of any work in this area, not the implementation so much.

The reason I keep banging on about "centralization" and consolidation, which are the current goals of the backend implementation, is because I see it as them means to arrive at the situation in which we can judge the output; where the implementation remains hidden.


Therefore, it seems that work should be done backwards from the target markup, which is also something that I and many others could give much more valuable feedback on.

Can there be some work done and communication around the final desired outcome of the style engine product as represented by working front-end code? Even after the recent big update on progress and plans, it's still very unclear to me what the final shape of front-end code will be.

Working backwards from some proof-of-concept front end code would also have other benefits like making it possible to evaluate some of the assertions around the various benefits between inline styles, inlined style blocks, and static CSS files.

Definitely. There'll be a need to propose and communicate the outcome and this gets my 👍

Moreover, if no-one else puts their hands up, I'll be the first to sit in the firing line and get a draft proposal up when the time comes.

My focus is getting to that stage by creating the means by which we all can demonstrate and experiment with a "final desired outcome ... represented by working front-end code".

To be clear, I'm not stating that it's too early or not helpful to start communicating about "target markup" or otherwise now: there are probably various desired "end results".

A "what if" post could be beneficial, not so much to act as a compass but to inspire and to instruct our work as to the most coveted state of Gutenberg classnames and styles.

I am however wary of talking about a contract to which outcomes must adhere. Along the way, there's the challenge of dealing with a the push and pull of new features, changes to existing features, and backwards compatibility. As more folks work on the code, and @youknowriad returns from his break 😄 , new and better ideas will flow in as well.

I don't know if any of this been instructive or has left you feeling more optimistic, sorry!

I'm writing all this as one would write the first entry of a diary: with the present firmly in mind.

Please let me know if you have any questions in relation to what I've written here. I might have made some erroneous assumptions (and probably typos!).

@andrewserong
Copy link
Contributor Author

Thanks for writing out your thoughts @ramonjd! I share many of the same views, and my efforts in reviews and experimental PRs (like #40875) has been to focus on attempting to consolidate how things work, and fix bugs that are high priority for the next major WordPress release. In the case of Layouts, which is work that I think of as related to, but adjacent to the style engine, my main goal at the moment is to see if I can help get blockGap working correctly in theme.json, reduce some of the style duplication, improve how layouts are rendered in async contexts, and gently nudge us in the direction of semantic classnames, without imposing new decisions just yet.

My hope with the work in #40875 and the Layout tracking issue in #39336, is that along with the style engine work, we'll have a more consolidated, closer-to-declarative way to define and output styles, as a pre-requisite for the bigger conversations about what form those styles and classes will take.

There's been (and continues to be) a lot of very useful discussions and ideas, some of them quite aspirational, so I think it's good for us to focus on the nuts-and-bolts of getting things more or less working in a consolidated way (perhaps we could call this working bottom-up) before imposing a more top-down approach that aims to achieve an end state.

One of the concerns that folks raised early on with adding in the style engine package (I can't remember where this was raised, I'm sorry!) was with open-ended or large scale projects that never manage to ship because the scope is too big. I think part of the rationale for working iteratively on existing features, and fix bugs along the way, is so that the code changes have immediate code quality / stability value in the short-term, without becoming too monolithic of a project.

So, I think I'd say that the counter point to potential anxiety over not knowing the shape that the final output will take, is that the current work will make it easier for us to change the final output, whatever it'll be. For me, having a goal be "let's make this whole system easier to change" seems to be one that lines up with whatever decisions or proposals folks make for how we think styles should ultimately be output.

Fortunately, there's a bunch of work happening in parallel (e.g. spacing presets in #39371 (comment)) that don't depend on the style engine directly, so I wouldn't think of the style engine work as being a blocker for anything right now, per se.

@ramonjd
Copy link
Member

ramonjd commented Jun 16, 2022

open-ended or large scale projects that never manage to ship because the scope is too big. I think part of the rationale for working iteratively on existing features, and fix bugs along the way, is so that the code changes have immediate code quality / stability value in the short-term, without becoming too monolithic of a project.

the current work will make it easier for us to change the final output, whatever it'll be. For me, having a goal be "let's make this whole system easier to change" seems to be one that lines up with whatever decisions or proposals folks make for how we think styles should ultimately be output.

Thanks @andrewserong This neatly wraps up what I tried to say in a bazillion words!

There are lots of stepping stones when crossing this bridge, and some are bound to trigger poison darts! 😆

I still think the idea of eventually having an ideal outcome published somewhere might help guide us across.

@mrwweb
Copy link

mrwweb commented Jun 23, 2022

@ramonjd @andrewserong Thanks for the excellent replies! I look forward to the day the Style Engine brings us world peace and free ice cream every day!

I don't think a huge point-by-point response is needed (largely because your replies were so informative).

Right now, my efforts, at least in relation to the style engine, are focussed on consolidating where we generate styles.

This totally makes sense. An accounting and consolidating makes total sense as a first step.

I still think the idea of eventually having an ideal outcome published somewhere might help guide us across.

1000% percent. You've totally convinced me that early work in the back end also makes lots of sense, and so maybe the best metaphor is that we should know the starting point and ending point of this tunnel and the goal is meet the tunnels in the middle.

I'm trying to get to a point where the style engine can incrementally improve the situation.

I think part of the rationale for working iteratively on existing features, and fix bugs along the way, is so that the code changes have immediate code quality / stability value in the short-term, without becoming too monolithic of a project.

The primary goal of my last comment is not to say that incremental work is bad. I agree that it's probably the only way a task this large happens. Rather, the concern is that this journey will accidentally repeat past errors and lose folks' confidence along the way regardless of the final destination. Like I said:

A lot of the planned phases seem to move directly through a ton of known pain points for themers such as inline styles (and therefore missing semantic classes), mostly redundant style blocks, etc.

That's why I recommend publishing a goal end point sooner than later. I think doing that can increase buy-in up front, identify risks of the path along the way, and find quick wins that can be implemented quickly once you're ready to start changing how the front-end code is shaped and output.

@ramonjd
Copy link
Member

ramonjd commented Jun 24, 2022

That's why I recommend publishing a goal end point sooner than later. I think doing that can increase buy-in up front, identify risks of the path along the way, and find quick wins that can be implemented quickly once you're ready to start changing how the front-end code is shaped and output.

Thanks for the feedback @mrwweb

I've just published a post that publicizes the rough roadmap towards better styles: https://make.wordpress.org/core/2022/06/24/block-editor-styles-initiatives-and-goals/

Much of it has been covered in this tracking PR, with the exception of the layout improvement, which I think will address a lot of the immediate pain.

The post deliberately doesn't refer to an end point because I don't know what that will look like yet, but I think we can imagine one, and agree that it would do more help than harm to discuss a wish list. 😄

@fwazeter
Copy link
Member

fwazeter commented Jun 28, 2022

I've been working a lot on this in my own plugin, and like the broader effort here, am having a few issues with scoping the StyleEngine class (been mostly working on it from the PHP side for now).

Almost all of my effort so far has centered around the Layout Support module of Block Supports, since it's by far the biggest issue when it comes to implementing theme designs. Colors, Border, Typography is mostly fine as-is, and precision implementations can easily be overridden via skipping serialization on blocks that support the features.

Layout and spacing however, force a never-ending re-adjustment of what should be sensible global defaults. Just the nature of the beast.

One approach that I'm having success with in minimizing the amount of rendered CSS via PHP (that implementation proved very difficult to maintain and adjust over time natively), is to set a series of CSS custom props that render the dynamic values from merged styles.

My perspective has been so far that theme.json works best as a series of design tokens, and therefore basic values should be considered global CSS custom props. This leverages the strengths of Custom Props, the cascade & theme & user set values with easy overrides.

For example:

:body {
   --wp--style--content-size: <merged data>
   --wp--style--wide-size: <merged data>
}

These are then hooked into a basic global reset:

/* among a few other very basic properties not 
relevant here, note that I place fallback 
values but likely unnecessary 
*/

* { 
    max-inline-size: var(--wf--content-size, 60ch) 
} 

Solving the content-size & wide-size problem without dynamic properties & while respecting CSS flow / default layout

First, to override specificity of the universal selector above on HTML tags that are either commonly used as containers or as sectional elements that would be intended to expand in width, a basic selector in the reset follows like so:

html, body, div, article, 
header, main, footer, nav, section {
       max-inline-size: none;
}

Then, two utility classes to account for content-alignment wide & full:

.align-wide {
      inline-size: var(--wp--style--wide-size, 70rem);  
      margin-inline-start: calc(50% - calc(var(--wp--style--wide-size, 70rem) / 2));
}

.align-full {
     inline-size: 100vw;
     margin-inline-start: calc(50% - 50vw);
}

/* to center content by default, 
such as the default setting for WP 
with contentSize 
*/

.align-center {
      margin-inline: auto;
      /* for fun, we could make it box-sizing: content-box; 
         & add padding-inline start & end to fix global padding 
         from theme.json 
      */
      box-sizing: content-box;
      padding-inline-start: var(--wp--style--padding, 1rem);
      padding-inline-end: var(--wp--style--padding, 1rem);
}

Conceivably, an align-right & align-left class could simply adjust where the margin-property is autoing.

Importantly with the two align-wide and align-full utility classes, now a block, such as the group block, can be nested inside of another block with a different content-width setting and "bust out" of the container, respecting the new larger width value (100vw, & explicit content-wide value is used because % or "none" would simply match the parent container size), while maintaining it's place in the normal document flow.

This means we no longer need any repetition or redeclaration of the CSS classes or new stylesheets and they can simply pull from the same one, because the only dynamic value that's actually changed is the CSS Custom Property at the top level. Plus, child elements respect the content size of their parent containers by default and we don't need to do any goofy things like:

<group block contentWide>
     <group block inheritDefault Layout>
           <group block contentFull>

For the case of a user set value for layout width on a block, rather than generate a new inline style, the custom property is simply updated, like so:

/* personally, I've been experimenting with adding a 
 [data-wp-styles] attribute to the tag and a basic check 
if the property already exists, don't create it again 
*/

 <group block style="--wp--style--content-size: <custom value>;"

This gives:

  • Themers & Plugins a defacto 'hook' via the custom prop.
  • Honors the cascade at all levels
  • Is easy to override by user, theme or plugin preferred values because there are multiple ways to override the style.
  • Radically simplifies Layout Support part of Block Support API & makes the styles / codes easier to maintain

StyleEngine scope

This part is a bit messy. Generally, I've been tinkering with a two part API. One for generating CSS style declarations, and the other for retrieving, registering and rendering styles. So a single StyleEngine class is responsible for generating the styles themselves, and a StyleManager centralizes the sources of data, 'sanitizes' the output through the StyleEngine and outputs / registers with WP the result.

What I'm really aiming for is total separation between the Block Support API and style-generation or registration of any kind, simply providing the flags for the styles to 'hook' into, since we'd basically want to be able to at a glance see where every style is coming from in a centralized repo, without having to do research on why that one random file is appearing.

@ramonjd
Copy link
Member

ramonjd commented Jun 29, 2022

Hi @fwazeter,

Thanks for the comment, and for taking the time to write down your thoughts!

The layout ideas might be also related to the proposals going on over at Tracking: extend layout options in Gutenberg as well.

One for generating CSS style declarations, and the other for retrieving, registering and rendering styles. So a single StyleEngine class is responsible for generating the styles themselves, and a StyleManager centralizes the sources of data, 'sanitizes' the output through the StyleEngine and outputs / registers with WP the result.

This is close to the current thinking on the division of labour of any "style engine/manager".

So registering, storing, generating and outputting CSS style declarations and CSS rules.

See for example #41424 and #41896

What I'm really aiming for is total separation between the Block Support API and style-generation or registration of any kind, simply providing the flags for the styles to 'hook' into, since we'd basically want to be able to at a glance see where every style is coming from in a centralized repo, without having to do research on why that one random file is appearing.

The "centralized repo" part is where folks are concentrating efforts right now. See the Make Core post.

Beyond that, the hope is that it will make working on how to express, store and access styles a bit easier.

@ramonjd
Copy link
Member

ramonjd commented Jul 6, 2022

Update Wednesday, July 6, 2022

The following updates are loosely grouped according to the Phases outlined in the recent Make Core post Block editor styles: initiatives and goals

Phase 1 (block supports and foundation building)

Work continues on creating equivalent style generation tools in JS for the block editor (non-frontend facing)

As 6.1 approaches there have been some PRs to improve operability and tighten up naming conventions:

Phase 2 (global styles consolidation and reducing style tags)

To achieve style tag reduction, we'll need a way to store CSS rules and enqueue stylesheets.

Given that, a discussion about the longer term architecture of the style engine is open: #42028

There have also been some experimental PRs that are testing boundaries and various approaches:

The first candidate for reducing style tags will be the layout abstraction as proposed in #40875.

As for global styles, there are a couple of experimental PRs looking at how to integrate the style engine:

Global styles contains many custom rules and style compilations. Furthermore, it's an area of active work and so the class changes every week.

One of the first tasks, therefore, will be to ensure we can introduce the style engine into global styles iteratively. For example, that we only parse certain top-level styles like styles.spacing and styles.typography and nothing under styles.blocks or styles.elements yet.

Phase 3 (to infinity and beyond! 🚀)

While this is beyond the scope of current work, there are things to keep in mind now.

In particular, how to store styles in a way that will allow us to control and output "layers" according to CSS cascade layers paradigm. See the description in #41424

@ramonjd
Copy link
Member

ramonjd commented Aug 3, 2022

Update Wednesday, August 3, 2022

This update highlights important milestone PRs. A complete overview of completed and ongoing tasks is available on the Gutenberg Style Engine project board.

For more information about planned phases, see the Make Core post Block editor styles: initiatives and goals

Phase 1 (block supports and foundation building)

Much of the backend groundwork is now complete. Augmenting the JS package has taken a back seat to backend operability and code enhancement changes, e.g.,

There has also been a lot of movement on removing repetitive layout-specific styles, paving the way for the style engine to combine selectors and bundle a more optimized block supports CSS.

See:

Phase 2 (global styles consolidation and reducing style tags)

PRs to optimize CSS rules, enqueue styles and reduce the number of style tags have been merged 🎉

Work continues on refining the public methods, documentation and experimenting with global styles:

@apeatling
Copy link
Contributor

@ramonjd What's the latest on this one? I think work has wrapped for 6.1 on this?

@ramonjd
Copy link
Member

ramonjd commented Aug 16, 2022

What's the latest on this one? I think work has wrapped for 6.1 on this?

Aside from any incoming stability/code enhancement tweaks or bug fixes, there are no further plans to extend functionality until after 6.1.

The focus will be to assist with 6.1 migration and related core patches.

Merged plugin features that are planned for WordPress 6.1

Following the Block editor styles: initiatives and goals, Phase 1 is complete:

  • Audit and consolidate where the code generates block support CSS in the backend so that they are delivered from the same place (as opposed to multiple places). This covers CSS rules such as margin and padding, typography, colors and borders.
  • Removing repetitive layout-specific styles and generating semantic class names

A major goal from Phase 2 has also been concluded:

  • Reduce the number of inline style tags we print to the page for block, layout and elements support.

Outstanding PRs

The JS version of the style engine covers all foundational block supports styles, i.e., spacing, color and typography.

There is a lone PR to switch over to the style engine in the editor hooks.

This PR is optional and not required for 6.1.

Beyond 6.1

Imminent plans to complete Phase 2 and augment functionality include:

  • Connect global styles to the same mechanism with which we’re generating block styles.
  • Improve pre-render CSS rule processing with the intention of deduplicating other common and/or repetitive block styles.
  • Establish and document standards by which to extend/override CSS, e.g, filters and so on
  • Extend the scope of semantic class names and/or design token expression, and encapsulate rules into stable utility classes.

@ramonjd
Copy link
Member

ramonjd commented Nov 18, 2022

Update Friday, November 18, 2022

The Style Engine is running in WordPress 6.1 🎉

Not much has happened since then aside from monitoring stability (there have been few, if no, bugs).

Completed since 6.1

In progress PRs

What's next

  • Early planning of global styles integration, breaking down tasks and so on. (To be continued in early 2023)

@oldrup
Copy link

oldrup commented Nov 18, 2022

Nice work! It's a huge improvement overall.

@mtias
Copy link
Member

mtias commented Jun 28, 2023

@andrewserong @ramonjd seems most of the items are concluded or no longer relevant. Should this be closed in favor of separate issues for remaining follow ups and ideas?

@ramonjd
Copy link
Member

ramonjd commented Jun 29, 2023

seems most of the items are concluded or no longer relevant. Should this be closed in favor of separate issues for remaining follow ups and ideas

Good call. I'll create a follow up tracking issue with the main items we still have to address (but have had not time/resources).

I'll close this for for now and link the follow up here when I get a chance to collate and publish.

Note to self, important items in the medium term:

  • Refactor blockGap support to use the style engine.
  • Style engine: extend block support style definitions #45296
  • Next big thing is global styles/theme.json - it's always lingering at the back of my mind, then 🤞🏻 the sky is the limit 😄
  • Look into filters for CSS/style engine output and, possibly, general config options such as prettify

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Style Engine /packages/style-engine [Status] In Progress Tracking issues with work in progress [Type] New API New API to be used by plugin developers or package users. [Type] Tracking Issue Tactical breakdown of efforts across the codebase and/or tied to Overview issues.
Projects
Status: 🏆 Done
Development

No branches or pull requests