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: extend layout options in Gutenberg #39336

Closed
10 of 30 tasks
ramonjd opened this issue Mar 10, 2022 · 21 comments
Closed
10 of 30 tasks

Tracking: extend layout options in Gutenberg #39336

ramonjd opened this issue Mar 10, 2022 · 21 comments
Labels
[Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi [Feature] Layout Layout block support, its UI controls, and style output. [Type] Tracking Issue Tactical breakdown of efforts across the codebase and/or tied to Overview issues.

Comments

@ramonjd
Copy link
Member

ramonjd commented Mar 10, 2022

This issue will track experimentation, discussion and other output in relation to the extension of Gutenberg's layout capabilities.

Prerequisites

There are some outstanding issues with the current layout implementation we should address as a priority:

The direction

Blocks may opt into Gutenberg’s experimental layout API through their individual block.json files, for example:

"__experimentalLayout": {
    "allowSwitching": false,
    "allowInheriting": false,
    "allowEditing": false,
    "default": {
        "type": "flex",
	"flexWrap": "nowrap"
    }
}

There are presently two layouts: flex and flow. The aim of this tracking issue is to explore approaches that will open up the ability to add and control advanced layouts with blocks that go beyond the block gap, or margin/padding controls.

In #33687 a"layout" is defined as:

a configuration you apply to a container block that changes how its inner blocks are aligned, and also impacts the behavior of these inner blocks (enable/disable some customization options).

The corresponding UI objective is to allow users to choose between a set of layouts and related options in the editor.

Current layout related PRs, issues and other background context

Earlier layout related drafts, experiments and WIPs

Future layout possibilities:

Future layout WIPs and experiments:

Why?

Gutenberg should widen the availability of default (and robust) layout options so that users can structure their sites in various ways.

Rather than provide a panacea for every layout problem, we could offer common patterns that appear, and have proved successful, in modern websites.

It would be preferable to provide an extensible system through which we could add future layouts as well.

Current work

There has been some preliminary discussion around how to refactor existing, and integrate new, layouts into Gutenberg in #38974. That discussion has since evolved into a proposal for a refactor of how the Layout support works in: #40875

So far, the work has been to target the current layout implementation and focussing on the means to register and output styles, while trying to take into account contemporary ideas and explorations surrounding the Style Engine.

The plates are still up in the air and spinning, but some concepts that folks are trying (or will have to try) to reconcile include:

  1. compatibility: layout rules should be portable across themes, and work well in modern devices/browsers
  2. theme.json schema: theme authors should be able to use JSON to determine the layout of their site and blocks.
  3. nomenclature: how layouts will "identify" themselves to the application and in the rendered content, their property names, element classnames and modifiers

What’s next (short term)

Continuation of the experimentation and research phase

With the following goals:

  • Standardize styles and classname generation for the layout support, reduce style duplication
  • General research, read Every Layouts and, if relevant, examine CSS design systems for inspiration
  • Experimentation, for example, what it would take to introduce many of the layouts from
  • Implement and test extensibility
  • Test backwards compatibility
  • Document findings
  • Define incremental tasks.

A further line of inquiry is to research how to provide layout options at the root and inner block level, that is, applying "layout" to a blocks container element (and therefore immediate first-level descendants) and targeting specific inner block "containers".

The relevance of the Style Engine

Integral to the refactor and addition of layout styles to Gutenberg is the Style Engine, which has thus far a few stated goals and many more hopes pinned upon it.

A distilled statement of these goals and hopes might be “to offer a consistent API for generating classnames and rendering CSS for blocks”.

In the context of layouts (and beyond), a centralized way to generate lean, consistent CSS and classnames is important to:

  • Reduce duplication of code, and maximize cacheability
  • Ensure internal and external uniformity and predictability.

One potential path forward is to explore centralising base layout styles output in global styles, and use a centralised set of definitions for the various layout types. This is being explored in #40875.

TODO

This is a short-list attempting to capture some incremental steps toward a centralised approach for outputting layout styles, instead of layout styles being applied one-at-a-time when each individual block is rendered.

@ramonjd ramonjd 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. [Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi labels Mar 10, 2022
@cbirdsong
Copy link

Fleshing out my comment from #38719, I feel like leaning on CSS custom properties could drastically simplify things compared to the other PRs I read through exploring this, so I spent a couple hours pulling together an example.

Again, apologies if I’ve missed something that makes this impractical or if this is the wrong place for this kind of feedback!


The default CSS custom property values for widths would appear in the global styles block (--wp--layout--content-size and --wp--layout--wide-size in this example), and elements with custom widths would have overrides for them inline (--wp--layout--content-size--custom and --wp--layout--wide-size--custom). Here's a cut down version:

<div class="wp-layout-container has-default-widths wp-block-group">
  <!-- inner content width is --wp--layout--content-size -->
  <div class="wp-layout-container wp-block-group">
    <!-- container width is --wp--layout--content-size -->
    <!-- inner content width is --wp--layout--content-size -->
  </div>
  <div class="wp-layout-container wp-block-group alignwide">
    <!-- container width is --wp--layout--wide-size -->
    <!-- inner content width is --wp--layout--wide-size -->
  </div>
  <div class="wp-layout-container has-default-widths wp-block-group alignwide">
    <!-- container width is --wp-wide-size -->
    <!-- inner content width is --wp--layout--content-size -->
  </div>
</div>
<div
  class="wp-layout-container has-custom-widths wp-block-group"
  style="--wp--layout--content-size--custom: 825px; --wp--layout--wide-size--custom: 1350px;"
>
  <!-- inner content width is --wp--layout--content-size--custom -->
  <div class="wp-layout-container wp-block-group">
    <!-- container width is --wp--layout--content-size--custom -->
    <!-- inner content width is --wp--layout--content-size--custom -->
  </div>
  <div class="wp-layout-container has-default-widths wp-block-group">
    <!-- container width is --wp--layout--content-size--custom -->
    <!-- inner content width is --wp--layout--content-size -->
  </div>
  <div class="wp-layout-container wp-block-group alignwide">
    <!-- container width is --wp--layout--wide-size--custom -->
    <!-- inner content width is --wp--layout--wide-size--custom -->
  </div>
  <div class="wp-layout-container has-default-widths wp-block-group alignwide">
    <!-- container width is --wp-wide-size--custom -->
    <!-- inner content width is --wp--layout--content-size -->
  </div>
</div>
:root {
  /* These would appear with all the other theme.json-generated custom properties: */
  --wp--layout--content-size: 650px;
  --wp--layout--wide-size: 1000px;

  /* These custom width values would be output as inline styles on a container element */
  /* --wp--layout--content-size--custom: 850px; */
  /* --wp--layout--wide-size--custom: 1400px; */
}

.wp-layout-container > * {
  width: var(--wp--layout--content-size--custom, var(--wp--layout--content-size));
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
}

.wp-layout-container.has-default-widths > * {
  width: var(--wp--layout--content-size);
}

.wp-layout-container.alignwide,
.wp-layout-container.alignwide:not(.wp-layout-container.has-default-widths) > * {
  width: var(--wp--layout--wide-size--custom, var(--wp--layout--wide-size));
}

.wp-layout-container.alignfull,
.wp-layout-container.alignfull:not(.wp-layout-container.has-default-widths) > * {
  width: 100%;
}

I have a full working example in Codepen based on output from Blockbase: https://codepen.io/cbirdsong/pen/dyJbyXO


Changes to the default markup:

As an aside, this could all be drastically simpler if the very confusing "inherit default layout" option was removed or reworked, as discussed in #36082. The --custom version of the variables are only necessary to keep that option working the way it does right now.

@ramonjd
Copy link
Member Author

ramonjd commented Mar 13, 2022

I feel like leaning on CSS custom properties could drastically simplify things compared to the other PRs I read through exploring this, so I spent a couple hours pulling together an example.

Thanks a lot for the comment, and for taking the time to illustrate things via code @cbirdsong 🙇

In my mind, the combination of global and local CSS variables/properties might have the potential to strike a balance between semantic classnames and bespoke, container-specific styles.

There are competing but, I believe, reconcilable demands in relation to how we define layout styles at various levels:

  1. Theme authors wish, at some level, to be able to rely on tokens in markup output not only for customization purposes, but to introduce consistency across blocks and layout HTML and CSS. See: Proposal: Standardized block markup, theme.json design tokens, and CSS classes to improve interoperability #38998
  2. Block HTML and associated CSS classnames/styles are considered low-level implementations of the theme.json API. There is a desire to discourage the overriding of these implementations. They will change as CSS specs evolve, or may differ across devices/platforms for example, and will result in maintenance overhead for theme developers.

I know you've seen it before, but just to document for others who might be reading: #38998 (comment) and #38998 (comment) contain more discussion on points 1 and 2.

The over-arching hooks, such as wp-layout-container and so on, seem descriptive enough to offer readable and consistent classification.

I suspect the area that needs careful consideration is how to represent auto-generated output – styles, classes and other "states" – based on declarations in block/theme.json.

Where (and in which format) to output them, while adhering to the hierarchical contract of Theme > Global Styles > Block, and also having a maintainable system to do so is the current challenge of both the layout and the style engine work.

A marriage of inline styles and CSS vars as you've demonstrated is one way to do it I'm sure.

Kind of related, is an idea to hook into theme.json presets to output styles.

@cbirdsong
Copy link

Block HTML and associated CSS classnames/styles are considered low-level implementations of the theme.json API. There is a desire to discourage the overriding of these implementations. They will change as CSS specs evolve, or may differ across devices/platforms for example, and will result in maintenance overhead for theme developers.

I feel like the idea that the editor can satisfactorily handle layout for every use case on 40%+ of the internet is fundamentally flawed – is this issue the right place to discuss that, or is there somewhere else I should take that discussion?

@ramonjd
Copy link
Member Author

ramonjd commented Mar 15, 2022

I feel like the idea that the editor can satisfactorily handle layout for every use case on 40%+ of the internet is fundamentally flawed

The editor isn't able to satisfactorily meet every conceivable layout challenge on the internet, but I'm not convinced anyone is making the claim that it can or ever will.

Plus this will be a slow moving train I suspect, the result of which – at least initially – will be a small and opinionated set of layouts followed by maturation and expansion.

is this issue the right place to discuss that, or is there somewhere else I should take that discussion?

Good question.

The motivation behind this issue is that Gutenberg should widen the availability of default layout options in the editor, so perhaps it'd be helpful to open the topic on a new discussion thread. I for one am interested in joining in, so thanks!

@andrewserong
Copy link
Contributor

andrewserong commented Mar 24, 2022

Update 2022/03/25:

We've been digging in and exploring a few potential longer-term options for how we might render out common layout styles to consolidate how things work (e.g. #39374 and #39708) — these are some experiments to think about how we might solve for issues like Global Styles: Make the default layout work as a preset. However, with the timeline for WordPress 6.0 fast approaching, we'll likely pause some of these more open-ended explorations, and focus more on the Prerequisites issues over the next couple of weeks:

Broadly the above issues cover the areas of better exposing some of the existing layout options that already exist, and improving the behaviour / stability of those features.

CC: @scruffian (or anyone else looking closely at this), please comment if there are other shorter-term issues that you feel are high priority for us to 👀 at!

@apeatling
Copy link
Contributor

@andrewserong @ramonjd What's the latest update on this one?

@andrewserong
Copy link
Contributor

andrewserong commented May 20, 2022

Update 2022/05/20:

Some of the current work underway:

Some of the current thinking about the above issues are that:

  • We've frequently run into lots of issues and edge cases with the current layout implementation, and fixing issues surrounding blockGap can be time consuming or tricky (e.g. dealing with fallback values)
  • If we move to a presets-based approach for generating layout styles, then we can more easily define layout styles in a declarative way, resolve some of the complexity of blockGap, and overall it should make it easier for us to add additional layouts
  • We then reduce the need for outputting as many individual values at the block-level at render time
  • We can then move those values that are output at render time to be handled by the style engine

While in the shorter-term, I'm very keen for us to resolve the issue of generating blockGap values at the block level in theme.json and global styles, the goal is that with the above work, it should put is in a better place to be able to implement some of the ideas for things that require more complex rules such as combining text-indent for paragraphs with spacing between paragraphs.

Next steps:

@andrewserong
Copy link
Contributor

Update 2022/05/31:

One of the main areas I've been looking into is how we can render out blockGap values at the block level in theme.json by using a classnames approach. The PR to implement this is still WIP, but folks can take a look at progress in: #40875

The current goals / explorations of that PR are:

  • Add a set of layout definitions to theme.json so that there's a centralised place to store some of the logic and share between the site editor and the server-rendered logic for the site frontend.
  • The layout definitions include the layout's classname (e.g. is-layout-flex or is-layout-flow).
  • When the block is rendered, this classname is injected to the markup.
  • When the block-level blockGap value is processed in global styles in the site editor, and in the theme JSON class for server-rendering, the gap value is output directly, using the appropriate selector and CSS property from the layout definition.

The result is that we should be able to have gap output like the following:

/* Root level blockGap in theme.json */
body .is-layout-flex {
  gap: 1.5rem;
}
/* Block level in theme.json */
.wp-block-buttons.is-layout-flex {
  gap: 5px 20px;
}

This exploration seeks to both solve the issue of supporting blockGap at the block level in theme JSON, as well as dipping our toes into adding in semantic classnames for the layout support. My hope is that by declaratively describing the layout in the JSON file, it'll be easier to both consolidate and extend the layout support, and have logical classnames. That's a bit of a bigger question though, so the idea with the PR is to see if there's a minimally viable approach to adding it in. If it does appear to be viable, then it could unlock follow-up PRs to address the other features of layout support and make them declarative in their output, too.

There's still more work to be done on the PR before it's ready for review (a couple of edge cases to deal with, tests are failing, etc), but I'm very happy for feedback if anyone's keen to take a look.

@cbirdsong
Copy link

cbirdsong commented Jun 8, 2022

Expanding on what I said in #40159: I think it would be best if the layouts used throughout the editor/front-end CSS were conceptualized/divided more cleanly. Ideally, every .is-layout- class should be designed to produce a clear end result in the way that the different layouts on Every Layout are, and any customizations applied should do nothing but supply what is needed to make those adjustments, applied directly if possible and using custom properties if not. Quoting from Every Layout's composition page:

The mistake in the last example was to think of everything about the dialog’s form as isolated and unique when, really, it's just a composition of simpler layouts. The purpose of Every Layout is to identify and document what each of these smaller layouts are. Together, we call them primitives.

The term primitive has linguistic, mathematical, and computing connotations. In each case, a primitive is something without its own meaning or purpose as such, but which can be used in composition to make something meaningful, or lexical. In language it might be a word or phrase, in mathematics an equation, in design a pattern, or in development a component.

[...]

Without primitive data types, you would have to be constantly teaching your programming language how to do basic operations. You would quickly lose sight of the specific, meaningful task you set out to accomplish with the language in the first place. A design system that does not leverage primitives is similarly problematic. If every component in your pattern library follows its own rules for layout, inefficiencies and inconsistencies will abound.

The primitives each have a simple responsibility: "space elements vertically", "pad elements evenly", "separate elements horizontally", etc. They are designed to be used in composition, as parents, children, or siblings of one another.

Viewed this way, .is-layout-flow works pretty well, but .is-layout-flex does not. The name doesn't describe what layout outcome is intended since flexbox can be used to accomplish many different layouts. It needs to be more specific.

Going over the blocks with significant layout components, here's how I'd divide them:

  • flow (.is-layout-flow), which manage vertical spacing using the the "lobotomized owl" selector and can contain floated children. This would be used on any generic text container like Group or Column, along with more specific ones like List and Blockquote.
  • stack (.is-layout-stack), which is similar to flow but use flexbox columns to allow for alignment and easier gap adjustment. These include the Stack variation of group, along with vertical versions of Navigation and Buttons.
  • row (.is-layout-row), for anything laid out horizontally1, such as the horizontal versions of Navigation and Buttons and the Row variation of group2.
  • grid (.is-layout-grid), which is currently only the Gallery block.

Listing these out makes it clear that customized spacing values shouldn't be applied directly to a .is-layout- , since it is then only applicable to that layout method – gap doesn't do anything on a flow layout. Instead, it should apply something equally useful to all the layout methods.

Here is a stab at some CSS that would cover flow, row, stack and grid, accounting for spacing customization across all of them:

First, set up the basic layouts and spacing:

.is-layout-flow > * + * {
  margin-top: var(--wp--default--spacing);
}

.is-layout-stack,
.is-layout-row {
  display: flex;
  gap: var(--wp--default--spacing);
}
.is-layout-stack {
  flex-direction: column;
}
.is-layout-row {
  flex-direction: row;
}

.is-layout-grid {
  display: grid;
  gap: var(--wp--default--spacing);
}

--wp--default--spacing could then be used for theme.json customization of block-level default values regardless of how the block is laid out:

.wp-block-group {
  --wp--default--spacing: 20px;
}
.wp-block-buttons {
  --wp--default--spacing: 10px;
}
.wp-block-row { 
  --wp--default--spacing: 30px;
}
.wp-block-gallery {
  --wp--default--spacing: 1px;
}

When custom spacing values are set by a user in the editor, a .has-custom-spacing class would be added to a block, which then applies it using a --wp--custom--spacing custom property:

.is-layout-flow.has-custom-spacing > * + * {
  margin-top: var(--wp--custom--spacing, var(--wp--default--spacing));
}

.is-layout-stack.has-custom-spacing,
.is-layout-row.has-custom-spacing {
  gap: var(--wp--custom--spacing, var(--wp--default--spacing));
}

.is-layout-grid.has-custom-spacing {
  gap: var(--wp--custom--spacing, var(--wp--default--spacing));
}

The .has-custom-spacing class3 is necessary to avoid issues with --wp--custom--spacing cascading down to child blocks that do not have a customized value. Using this class to apply the custom value means we can be certain it will only be used with this exact block, not one of its children.

--wp--custom--spacing makes it easy to fit custom values in via inline styles (<div class="wp-block-buttons has-custom-spacing" style="--wp--custom--spacing: 27px;">) and presets (#41527) via classes:

.has-spacing-10 {
  --wp--custom--spacing: var(--wp--preset--spacing--10);
}
.has-spacing-20 {
  --wp--custom--spacing: var(--wp--preset--spacing--20);
}
.has-spacing-30 {
  --wp--custom--spacing: var(--wp--preset--spacing--30);
}
/* etc, etc */

Other benefits of this approach:

Footnotes

  1. I could also see a case for making rows with wrapping disabled into a distinct .is-layout, as they lead to a pretty different end result.

  2. This could also include Media & Text and Columns, but they have a lot of unique quirks already and would be hard to fit into a generic layout primitive without breaking backwards compatibility. They might just have to be .is-layout-<block name>?

  3. An attribute selector isn't workable because custom values would be output as an inline style ([style*="wp--custom--spacing"]) while preset spacing values would be applied with a class ([class*="has-spacing-"]). Checking for both of these which would be very awkward.

@andrewserong
Copy link
Contributor

Thanks for sharing the ideas @cbirdsong!

Unfortunately there were a number of subtle issues at play when the blockGap behaviour relied more directly on CSS variables. The main issue tracking styling conflicts was #36521 and it partially resolved in #37360, which attempted to remove most used of CSS variables for block gap styles.

The problem comes when we have a flow-based block with a custom gap value like Group and a child of that block that also opts-in to gap support, like Buttons. In this case, adding a .has-custom-spacing class doesn't resolve the issue, because if the CSS variable is updated via the inline style, then that value is applied to both its top-margin (received via the parent's flow layout) and to its own gap property.

I think a safer approach for the layout support is to rewrite it to support directly computed values at the block level in theme.json / global styles (to restore that behaviour). We can use root-level CSS variables as spacing values, but I think we should avoid resetting / re-declaring the CSS variables from within individual block's inline styles since it can introduce subtle and difficult to debug styling issues.

For a bit more context, I'm currently exploring this (computing / outputting gap values directly) in #40875 (there's a bit more work to do before it's ready for review), which is exploring the idea that @youknowriad raised in #36521 (comment) last year.

Viewed this way, .is-layout-flow works pretty well, but .is-layout-flex does not. The name doesn't describe what layout outcome is intended since flexbox can be used to accomplish many different layouts. It needs to be more specific.

That's an interesting point. My understanding was that in the case of flex, the naming follows the idea of a one-dimensional layout model, which can have either vertical or horizontal orientation. While it's exposed in the block editor as two separate block variations for Row and Stack, in the context of other blocks that opt-in to it (like Buttons), the name flex ties it to the display mode, and we can potentially use utility classes to manage orientation. I suppose the question here is: are the Group block's variations meant to indicate how the layout support works internally, or is the naming just tied to those use cases?

As a first step, for simplicity of implementation, I'd be inclined to keep .is-layout-flex (it's currently used as a styling rule in the editor), and look at further naming changes as we add additional layouts.

Ideally, every .is-layout- class should be designed to produce a clear end result in the way that the different layouts on Every Layout are

A limitation here of trying to represent the layout in a single class is that there are many different parameters for each particular layout type. One way to think of it is that the base class contains just those common styles as a starting point for achieving the layout, but we then defer to utility classes for the things that can be variable (e.g. orientation, justification, etc). There's an open PR from @glendaviesnz (#41487) to add back in utility classnames for backwards compatibility with Buttons block, etc. Just thought I'd mention it in case you wanted to add any feedback on that PR 🙂

@cbirdsong
Copy link

cbirdsong commented Jun 9, 2022

The problem comes when we have a flow-based block with a custom gap value like Group and a child of that block that also opts-in to gap support, like Buttons. In this case, adding a .has-custom-spacing class doesn't resolve the issue, because if the CSS variable is updated via the inline style, then that value is applied to both its top-margin (received via the parent's flow layout) and to its own gap property.

Hmm, I can see you're right about this in the case of inline styles and flow layout. I had solved that problem by applying the custom property to the child, but that can't work with an inline style:

View Code
.is-layout-flow > * + * {
  margin-top: var(--flow-gap, 1.25rem);
}
.is-layout-flow.has-gap-1 > * {
  --flow-gap: 0.25rem;
}
/* etc, etc */

However, it does work for custom values in general. In my non-theme.json themes, I've successfully used the .has-background and .has-text-color classes to manage inheritance of a variety of color-related custom properties, though again, without introducing inline styles.


My understanding was that in the case of flex, the naming follows the idea of a one-dimensional layout model, which can have either vertical or horizontal orientation. While it's exposed in the block editor as two separate block variations for Row and Stack, in the context of other blocks that opt-in to it (like Buttons), the name flex ties it to the display mode, and we can potentially use utility classes to manage orientation.

I think the issue here is that "one-dimensional" isn't enough to hang it on conceptually. You see the issues start as soon as you write the justification utility classes. For vertical layout, you need to modify align-items, and for a horizontal layout you need to modify justify-content, and if your classes are human-readable they are basically unable to operate in both contexts. Look at how the buttons block CSS needed to be written:

.wp-block-buttons {
  &.is-vertical {
    flex-direction: column;
    > .wp-block-button {
      &:last-child {
        margin-bottom: 0;
      }
    }
  }

  // Increased specificity to override blocks default margin.
  > .wp-block-button {
    display: inline-block;
    margin: 0;
  }

  &.is-content-justification-left {
    justify-content: flex-start;
    &.is-vertical {
      align-items: flex-start;
    }
  }

  &.is-content-justification-center {
    justify-content: center;
    &.is-vertical {
      align-items: center;
    }
  }

  &.is-content-justification-right {
    justify-content: flex-end;

    &.is-vertical {
      align-items: flex-end;
    }
  }

  &.is-content-justification-space-between {
    justify-content: space-between;
  }
}

Changing layout behavior based on the presence of .is-vertical is needed at basically every step of the way, because it's an entirely different layout. Everything is much cleaner when you just start with that premise:

.is-layout-stack {
  flex-direction: column;

  &.is-content-justification-left {
    align-items: flex-start;
  }
  &.is-content-justification-center {
    align-items: center;
  }
  &.is-content-justification-right {
    align-items: flex-end;
  }
}

.is-layout-row {
  flex-direction: row;

  &.is-content-justification-left {
    justify-content: flex-start;
  }
  &.is-content-justification-center {
    justify-content: center;
  }
  &.is-content-justification-right {
    justify-content: flex-end;
  }
  &.is-content-justification-space-between {
    justify-content: space-between;
  }
}

@andrewserong
Copy link
Contributor

Everything is much cleaner when you just start with that premise:

That's a great point, thanks for the example CSS, that very much highlights the improvement of moving to separate classes between is-layout-stack and is-layout-row 👍

I'm hopeful that if and when #40875 is ready for review / looks viable, in a follow-up we can tackle the utility classes, and I think you've made a strong case for exploring seeing if we can separate out the flex layout type (as it exists in the current layout data) into those two separate classnames. I can imagine it being slightly complex getting there, but it sounds like the long-term benefits of clarity in both the classnames and CSS that gets output would be worth the effort.

@andrewserong
Copy link
Contributor

Update 2022/06/21:

The PR to explore refactoring the Layout support to centralise base styles output and add back in support for rendering blockGap values at the block level in theme.json is ready for review now: #40875. Its broad scope includes:

  • Centralise Layout block support definitions by storing them in theme.json.
  • Render base styles for the Layout block support (default/flow and flex) by outputting the common styles in global styles, targeted by a classname linked to the layout type (.is-layout-flow and .is-layout-flex).
  • With centralised base Layout styles being output from global styles, only inject a container classname and style tag if a block that opts-in to the Layout support has non-default values (reduce the volume of container classnames and style tags that are output).
  • Use direct CSS values instead of CSS variables at the block and root level for blockGap.
  • Allow block level blockGap in theme.json so that blocks such as Buttons and Social Icons can have their own block gap values in global styles.
  • Ensure fallback layout style output for classic themes so that blocks like Buttons and Columns still render correctly for themes without a theme.json file.

It's a pretty large PR that proposes rethinking how the Layout support works — unfortunately, it's a difficult PR to reduce in size, as the changes rely on a refactor happening all at once. The hope is that the PR can progress a few different ideas, incrementally get us toward reducing duplication of container / style tags, and a step forward in outputting semantic classnames, and improving the logic of how the support works.

I'm sure there are edge cases that I've missed while working on the PR, so I'm very happy for feedback / testing to help with the next stage of the PR, and to see whether or not folks think it'll be a viable path forwards.

@cbirdsong
Copy link

cbirdsong commented Jun 21, 2022

I recognize this is already a huge PR, but to me it would make sense to go ahead and get the .is-layout-row and .is-layout-stack classes in there. A big part of the complaints around the removal of comprehensible class names is a desire for stability in markup. From that perspective, it feels silly to add .is-layout-flex with plans already underway to remove it.

@andrewserong
Copy link
Contributor

andrewserong commented Jun 22, 2022

Thanks for the feedback @cbirdsong!

to me it would make sense to go ahead and get the .is-layout-row and .is-layout-stack classes in there.

The PR has been a tricky one to rein in, in terms of scope, and in my explorations so far, switching to these classnames means addressing more aspects of introducing the remaining semantic classnames of the layout attributes (to help close out #38719). In the Buttons block, for example, the .is-vertical class is currently in use, and with the switch to .is-layout-row, we'd be able to remove the Buttons' blocks hard-coded styles and switch to the Layout generated ones.

I think this is tricky to do in a backwards compatible way without running into edge cases, so while the step of introducing .is-layout-flex definitely looks redundant in the short-term, I think it could be a useful step to try to ensure that we have PRs that are a bit easier to test / present in order to gather and implement required feedback. The .is-layout-flex classname currently gets output in the editor in trunk in:

className: `is-layout-${ type }`,
(though that's of course quite a bit different to it being output on the site front end).

Definitely my intention would be for us to immediately explore a follow-up that looks at how we go about implementing the remaining semantic classnames and safely move the Buttons block over to a new classname structure — my hope is that the follow-up PR would be a bit easier to read so that we could get more contributors to offer their feedback / ideas in how to shape things, and we could tease apart the implications of splitting the flex layout type into two separate classname structures based on orientation.

@andrewserong
Copy link
Contributor

andrewserong commented Jul 12, 2022

Update 2022/07/12:

The Layout refactor PR (#40875) has now been merged in. This PR implemented a centralised place for layout definitions to be stored, and to be output in the global styles stylesheet using semantic classnames based on the layout type, block-level support for blockGap in theme.json, and some de-duping of redundant style tags for blocks that use the default layout properties. This opens up a few follow-up tasks. Some of the next steps now include:

Also, now that #40875 has landed, the PR (#42085) to add support for root padding CSS variables and factoring in padding when outputting alignfull styles is nearly ready for a final review.

@andrewserong
Copy link
Contributor

andrewserong commented Jul 20, 2022

Update 2022/07/20:

In Progress PRs:

The focus currently is on adding in a few new features, and optimising output, across the following PRs:

@andrewserong
Copy link
Contributor

andrewserong commented Aug 3, 2022

Update 2022/08/03:

Recently merged PRs:

In Progress PRs / tasks:

@apeatling
Copy link
Contributor

apeatling commented Aug 16, 2022

@andrewserong What's the latest here? I think progress is done in terms of what can make it for 6.1?

@andrewserong
Copy link
Contributor

andrewserong commented Aug 17, 2022

Thanks for the ping @apeatling, yes I think progress is nearly done for what can make it for 6.1. The main work to consolidate and improve the output for base layout styles and re-enable block spacing at the block level in global styles is complete. There are a couple of tidying up tasks that still need to be completed. Here's an update:

Update: 2022/08/17:

Recently merged PRs:

In Progress / hoping to make it in time for 6.1:

All in all, I think we'll be ready for the 6.1 feature freeze next month. Beyond 6.1, we should be in a good place for exploring adding additional layout types and controls, along with collaborating on the style engine project to encapsulate rules into stable utility classes, as flagged by @ramonjd in the latest style engine update.

@andrewserong
Copy link
Contributor

Update: 2022/10/06:

With all layout changes that were slated for 6.1 now backported and in the 6.1 beta, now is a good time to close out this particular tracking issue. For bug fixes and smaller tasks, there is a new [Feature] Layout label that can be added to new bug reports and enhancement requests.

For tracking work slated for WordPress 6.2, there is now a fresh issue for tracking those particular higher level tasks over in: #44720.

I'll close out this issue, and I'm happy to help out with status updates on #44720 as work kicks off following the stabilising of features that landed in 6.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi [Feature] Layout Layout block support, its UI controls, and style output. [Type] Tracking Issue Tactical breakdown of efforts across the codebase and/or tied to Overview issues.
Projects
None yet
Development

No branches or pull requests

5 participants