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

Open
10 of 24 tasks
Tracked in #41232
andrewserong opened this issue Jan 24, 2022 · 35 comments
Open
10 of 24 tasks
Tracked in #41232

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

andrewserong opened this issue Jan 24, 2022 · 35 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 For issues used to track large changes across the codebase

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 For issues used to track large changes across the codebase [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

youknowriad commented Jan 24, 2022

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
@andrewserong
Copy link
Contributor Author

andrewserong commented Feb 2, 2022

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

andrewserong commented Feb 3, 2022

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

andrewserong commented Feb 15, 2022

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

fwazeter commented Feb 15, 2022

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

fwazeter commented Feb 15, 2022

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.

@ramonjd ramonjd added the [Package] Style Engine /packages/style-engine label Apr 12, 2022
@ndiego
Copy link
Member

ndiego commented Apr 12, 2022

Given @ramonjd most recent comment, I am removing this from the 6.0 Project Board.

@ramonjd
Copy link
Member

ramonjd commented May 20, 2022

Update Friday, May 20, 2022

This update outlines style engine work being done right now, upcoming tasks and (roughly) how we might progress beyond these stages.

Phase 1: moving all style engine code to a package and using it to generate block supports styles (ongoing, about 75% complete)

Work continues on the initial phase of migrating existing block supports styles to the style engine.

This involves:

  • using the style engine to generate inline block styles for spacing, elements, typography, colors, duotone and borders in both the backend (PHP) and editor (JS).
  • moving all style engine code to a packages file and modifying the build script include it and rename any wp_ functions to gutenberg_

Related PRs:

This is work is mostly complete with the exception of elements, duotone and borders, both PHP and JS versions.

We've initiated the backend work for a couple of these already:

Phase 2: generate inline stylesheets (started, about 10% complete)

With the work in #40987, as well as migrating elements block supports, we're testing ways to generate style selector + rules combinations that are intended to be embedded in HTML pages.

Whether in the header, footer, or inline, Gutenberg renders various and separate <style /> elements onto the page: global styles/presets, layout rules, and bespoke block implementations such as the gallery block.

Each area comes with its own challenges, however having a functioning way to embed styles in place will allow us to make a start on migrating them as well.

The goal of this phase is for the style engine to generate these rules and, preferably, be responsible for outputting them wherever it makes sense.

Tasks include:

  • identifying where we generate/output inline stylesheets right now and how
  • start a basic implementation, e.g., for elements, and see if it translates to other areas

Phase 3: investigate where to start generating global styles (started, about 1% complete)

Note: this could be phase 4.

I've flirted with this already, demonstrating the minimal code changes required to output global styles:

The aim is to use the style engine to take over the work of building and output style rules from WP_Theme_JSON.

When we arrive to this task, I expect to break it down into further tasks.

Phase 4: generate layout styles (Not started)

Note: this could be phase 3.

Here we want to consolidate and simplify the layout implementation, e.g., layout.php in the backend.

There are several pain points associated with layout styles, many of them related to block gap 😄 e.g.,

While the style engine doesn't promise to make these issues go away on its own, we hope that having more control over where and how we output layout styles will help us resolve them.

Furthermore, also important will be to consider during this phase is how we will support future layout styles.

Beyond Phase 4

Presently, the style engine is not inventing new ways to generate styles and classnames. Rather, the initial goal is to centralize where we generate styles from a block styles object.

That means we can view the style engine as a tool to generate styles: it doesn’t yet change how we express styles, e.g., for block supports the inline styles the engine outputs are the same as they were before.

That being said, once phases 1-4 are functional, there is scope later to leverage the style engine to improve Gutenberg's style output. For example:

  • output all inline stylesheets in one <style /> tag
  • dedupe rules
  • generate classnames
  • utilizing the style engine for new layout styles
  • ??

There have already been some experimental side hustles in relation to these ideas:

Also, there is the question of whether the style engine should take over classname generation, that is, building has-$slug-$feature classnames for blocks based on presets.

We have implemented this on the backend already, the motivation being to centralize such things, however whether the style engine should be doing this in principle is up for debate.

Is the style engine running in WordPress core?

Not yet.

Note: We skipped merging the backend component of the style engine to WordPress 6.0, though we expect it be included in 6.1 for at least color, typography, border and spacing block supports.

@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

andrewserong commented Jun 16, 2022

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

apeatling commented Aug 16, 2022

@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.

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 For issues used to track large changes across the codebase
Projects
Status: 📖 Project overview
Development

No branches or pull requests

10 participants