WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.54k stars 4.21k forks source link

Extend theme.json to provide spacing size presets #39371

Open glendaviesnz opened 2 years ago

glendaviesnz commented 2 years ago

What problem does this address?

Background discussion.

On the above issue, there was some consensus that there would be value in extending theme.json to allow spacing size presets to be added, which gives users a set of preset size tokens to select from as well as/or instead of just adding custom spacing values.

This blog post provides a good introduction to some of the reasons for this.

This is intended to be a top-level issue for some of the initial discussions, as well as tracking the ongoing work needed to implement this.

The block UI will need to allow people to select from the presets (e.g. padding value Medium) in addition to the "absolute value" settings (e.g typing "23px"), and also allow for theme authors to disable the option to add absolute values.

Update

Summary of discussion and suggested way forward

Todo

Done

eric-michel commented 2 years ago

With regards to spacing, I think there are two levels of issues:

  1. For margin and padding, there is no way to define a limited, preset list of values at all in theme.json. The current margin and padding controls only allow for direct numeric control of spacing (via units like px, rem, etc). This is in contrast to font size and color palette, which at least allow for defining a limited list of options.
  2. If preset values become allowed, should they be unified into a standard naming convention, like gap-1, gap-2, gap-3 etc to improve interoperability? This is the proposal in the issue (https://github.com/WordPress/gutenberg/issues/38998) that inspired this more narrowly focused one.

Item 1 is probably the biggest missing feature for the way we use Gutenberg. Item 2 is a larger project that I definitely think has merits, but even addressing item 1 would be a huge leap forward.

Just allowing spacing.units to accept an array of preset options similar to font size and then having those options selectable from the margin and padding controls for supported blocks would be amazing. Something like:

"spacing": {
    "units": [
        {
            "slug": "xs",
            "size": "0.25rem",
            "name": "XS"
        },
        {
            "slug": "sm",
            "size": "0.5rem",
            "name": "SM"
        },
        {
            "slug": "md",
            "size": "1rem",
            "name": "MD"
        },
        {
            "slug": "lg",
            "size": "2rem",
            "name": "LG"
        },
        {
            "slug": "xl",
            "size": "4rem",
            "name": "XL"
        },
    ]
},

Issue https://github.com/WordPress/gutenberg/issues/35306 includes some additional conversation regarding defining preset spacing values.

cbirdsong commented 2 years ago

Summarizing my points in #35306, most of which are mirrored in #38998 and its accompanying blog post:


Continuing discussion from #38998, I think the biggest point of debate so far is whether the default[^1] scale should use numbers (1, 2, 3) or T-shirt sizes (sm, md, lg).

From @landwire in https://github.com/WordPress/gutenberg/issues/38998#issuecomment-1067367194:

I had not the time to read all of this thread yet, but I would like to see consistency in the naming of the variants of property values, if at all possible and where it makes sense. I find just numbers 1, 2, 3, 4 etc. too abstract, but prefer the T-shirt sizing system that @justintadlock mentioned here: https://github.com/WordPress/gutenberg/issues/38998#issuecomment-1053727408

So using a standard set: xxs, xs, s, m, l, xl, xxl or a subset thereof, if less is needed.

On one hand, I think this makes sense for consistency with font sizes, though I'd suggest sizes smaller/bigger than xs/xl be 2xl, 3xl, 4xl, etc, since that's easier to parse at a glance than xxl, xxxl, xxxxl.

On the other hand, I think that most designs could use more spacing values than the basic 5/7 this supports elegantly.

It also feels like only adding new sizes onto the ends of the scale would make it more difficult to support the kind of system described by @mrwweb in https://github.com/WordPress/gutenberg/issues/38998#issuecomment-1069277170:

Based on your font-weight example and some of @justintadlock's spacing suggestions, it does seem like a system that allows theme authors to omit/combine certain values on these scales would be very helpful. (Although I also think there's a risk of over-engineering here!) The trick is figuring out how to "merge" points on the scale together so that omitted points are rounded to their nearest defined value, maintaining the relationship between user selected values. I think custom property fallbacks could end up being of huge value for doing this, and I built a proof of concept idea of how standardized classes based on optional custom properties could be used for variable scales.

Sizes smaller/bigger than xs and xl could easily fall back to using those, but that likely wouldn't translate well in practice.

There is also the issue of communicating what the "default" gap would be. In font sizes, it's pretty clear that "medium" is probably the theme's default, though the user can't actually see the word "medium" very easily with the current design and stock font sizes, and resetting to the default is extremely confusing:

CleanShot 2022-03-21 at 08 42 26

Personally, I've implemented this sort of system in ACF blocks using a range slider, which felt appropriate and fits a numeric system better:

CleanShot 2022-03-21 at 09 25 31

It's also worth considering that Bootstrap and Tailwind use numbers. (Tailwind also includes numbers with decimals by default, which I find very weird.)


From @landwire in https://github.com/WordPress/gutenberg/issues/38998#issuecomment-1067367194:

Not sure if we need an explicit none/zero value for gap, margin, padding?

I would say we do. I often add a "seamless" style to the columns block that removes space between child columns, which is useful if you're adding a background color to all of them.


From @glendaviesnz in https://github.com/WordPress/gutenberg/issues/38998#issuecomment-1067367194:

Do you think it will be a requirement of theme authors to enable/disable both options independently, eg. allow users to select either presets, or, absolute, or both?

It's hard for me to imagine a situation where a theme author that would want to disable presets but allow custom values. It doesn't look like it's possible to do that with font sizes right now, and it would probably make sense to be consistent with that.

This should probably be a separate issue, but the editor also needs to get much better at handling custom values in existing content, as right now it's impossible to tell that a custom font size is attached to a block when custom value entry has been disabled. This will definitely be a problem here since all gap values are currently all custom sizes.

[^1]: As with font sizes, theme authors should be able to throw out the default scale and insert whatever fits their needs. I doubt many will, though, since there will be benefits for using the standard sizes.

eric-michel commented 2 years ago

@cbirdsong

Not sure if we need an explicit none/zero value for gap, margin, padding?

I would say we do. I often add a "seamless" style to the columns block that removes space between child columns, which is useful if you're adding a background color to all of them.

I completely agree. I regularly want to set margin to zero on things like two group blocks with background colors when I want to have no gap between them. Or (same as you) want to have no gap between columns in the columns block.

mrwweb commented 2 years ago

@cbirdsong amazing writeup. Thanks for that!

Do you think it will be a requirement of theme authors to enable/disable both options independently, eg. allow users to select either presets, or, absolute, or both?

It's hard for me to imagine a situation where a theme author that would want to disable presets but allow custom values. It doesn't look like it's possible to do that with font sizes right now, and it would probably make sense to be consistent with that.

I agree that following the existing options supported by Font size makes sense since that is the closest existing system to what's proposed here. It should definitely be possible to only use the scale and not enable custom values.


The main purposes of adding this feature are:

  1. Increase consistency from page to page by making it easier to select consistent spacing settings
  2. Maintain editor intent when switching themes and increase content portability
  3. Expose spacing values to plugins and core

Notably, the goal is NOT to support a theme's full spacing needs. I think this needs to weigh into how many values are in the standardized spacing scale. While themes might well want more than 5-7 values for spacing in custom CSS, I wonder how valuable it is to go beyond that for block settings.


It seems to me that in order for this feature to move forward, the following questions need to be answered. I offer them to hopefully stimulate further conversation!

  1. Is a single spacing scale sufficient or do gap/margin/padding/etc. need separate scales?
  2. How many points on are on the spacing scale, and can themes go beyond the required points. If so, how? How does that work when switching themes?
  3. What naming scheme should the scale use? (Leading contenders: sequential numbers or "t-shirt sizes" i.e. xs, s, m, l, xl)
  4. Could scales be derived from a single spacing value and a multiplier or should each point be individually defined?
  5. Should there be default values for the spacing provided by core? (If following the path of font size settings, the answer is almost certainly yes)
  6. How are the scale values exposed to themes, patterns, and plugins for use? (This is where the issue of the style engine comes in on #37495)

Finally, I have a question about implementation. Do we need the block settings to be implemented before the scale itself is finalized as a standard? I wonder if it would be better to define the scale and expose it in code one release before any settings are added to allow for a bit more flexibility on the final feature and move this issue forward faster.

cbirdsong commented 2 years ago
  1. What naming scheme should the scale use? (Leading contenders: sequential numbers or "t-shirt sizes" i.e. xs, s, m, l, xl)

I would also throw in the sequential numbers variant of 10, 20, 30, which leaves a natural place for additional spacing values to be added.

eric-michel commented 2 years ago

@mrwweb I really like your list of questions and think it's a great place to start. If this were only a discussion of adding preset spacing options, it would be pretty simple - do it like we do font sizes. Full list of options, custom naming and values, WP just generates the classes based on that (has-[name]-font-size just becomes has-[name]-margin or maybe something shorter like m-[name] etc).

With the added wrinkle of standardization of tokens (for at least a limited number of spacing options), we have to balance between flexibility and standardization.

Is a single spacing scale sufficient or do gap/margin/padding/etc. need separate scales?

This is a tough one. A single scale is certainly sufficient for the way we operate. But maybe we should err on the side of greater flexibility for folks who would want it.

It could be that there is a spacing.sizes value that is universal, but gets overridden by spacing.margin.sizes, spacing.padding.sizes, and spacing.gap.sizes. Or alternatively, define a spacing size and optionally allow for multiple values based on property. Something like:

"spacing": {
    "options": [
        {
            "slug": "xs",
            "baseSize": "0.25rem",
            "marginSize": "0.5rem", //optional value, overrides baseSize for margin only
            "name": "XS",
        },
        ...
    ]
},

How many points on are on the spacing scale, and can themes go beyond the required points. If so, how? How does that work when switching themes?

I think 5 or 6 standard options should be sufficient. But I do think it's really important that themes be able to go beyond the required options to avoid the "bootstrap effect" that @Luehrsen mentioned in the main thread.

In terms of defining those options, simply listing whatever values you want (including both required and your own options) seems the simplest. Let's say the standard sizes are sm, md, lg, xl, and 2x. If I also wanted to have a mdlg option, the following seems intuitive to me:

"spacing": {
    "options": [
        {
            "slug": "md",
            "baseSize": "1rem",
            "name": "MD",
        },
        {
            "slug": "mdlg", //this is a nonstandard option, but gets defined along with the standard ones
            "baseSize": "1.5rem",
            "name": "MDLG",
        },
        ...
    ]
},

For theme switching, I wonder if we should just do nothing. Maybe we add an alert when activating a theme that uses nonstandard options that warns the user that content from this theme may not be portable to other themes. This allows for flexibility at the expense of portability when desired. Many theme creators will likely stick to the standard set of options. And there will still be enormous value on the plugin side regardless, because the standard options will be available for plugins to take advantage of even when custom options are created by the theme as well.

What naming scheme should the scale use? (Leading contenders: sequential numbers or "t-shirt sizes" i.e. xs, s, m, l, xl)

Originally I was favoring the t-shirt sizing scale, but if we're looking to offer the flexibility of any amount of custom options on top of the standard ones, maybe @cbirdsong's idea of sequential numbers in 10s is the way to go.

Could scales be derived from a single spacing value and a multiplier or should each point be individually defined?

I think each point should be individually-defined for sure. Better to give more flexibility.

Should there be default values for the spacing provided by core? (If following the path of font size settings, the answer is almost certainly yes)

I'd say yes as well. I think following the example of the font size settings is ideal here (except for the added wrinkle of standard tokens).

How are the scale values exposed to themes, patterns, and plugins for use? (This is where the issue of the style engine comes in on https://github.com/WordPress/gutenberg/discussions/37495)

Perhaps this is overkill, but I'd love to see the generation of both utility classes and custom properties, similar to the way font size classes and custom properties are currently generated.

Something like:

"spacing": {
    "options": [
        {
            "slug": "30",
            "baseSize": "1rem",
            "name": "MD",
        },
        ...

would generate:

:root {
    --wp--preset--gap-size--30: 1rem;
}
.m-30 {
    margin: var( --wp--preset--gap-size--30 );
}
.mt-30 {
    margin-top: var( --wp--preset--gap-size--30 );
}
etc etc

.p-30 {
    padding: var( --wp--preset--gap-size--30 );
}
etc etc

Using custom properties would then allow for things like smaller gap values at various viewport sizes (which I think would be beyond the remit of theme.json). For instance, I may want the gap value of 30 to drop to 0.5rem on mobile. In my theme's CSS I can set --wp--preset--gap-size--30 to 0.5rem at my chosen breakpoint, and all of my spacing (margin, padding, and gap) would adjust appropriately. This, of course, assumes that I have not given different values to margin, padding, or gap for size 30.

cbirdsong commented 2 years ago
  1. Is a single spacing scale sufficient or do gap/margin/padding/etc. need separate scales?

Thinking about this more, I don't think the difference between these would be easily explainable to users, which would mean that they need to be based on the same scale. For instance, in the case of gap and margin, choosing between the two is often an implementation detail, and if the defined gap and margin values differ then the choices presented to a user would vary by block in a seemingly arbitrary way.

For theme switching, I wonder if we should just do nothing.

This is probably fine, as that's how custom font sizes work.

Perhaps this is overkill, but I'd love to see the generation of both utility classes and custom properties, similar to the way font size classes and custom properties are currently generated.

I'm also not sure utility classes are the best idea considering how quickly they could multiply. A simple .wp-gap-whatever class/token would let theme authors have more flexibility to take the token and use it in a way that fits their design without dictating how. For instance, I often apply what would normally be padding as margin with the column blocks so that a child block can optionally span the full width of the content. Demo: https://codepen.io/cbirdsong/pen/VwymdvO

eric-michel commented 2 years ago

Is a single spacing scale sufficient or do gap/margin/padding/etc. need separate scales?

Thinking about this more, I don't think the difference between these would be easily explainable to users, which would mean that they need to be based on the same scale. For instance, in the case of gap and margin, choosing between the two is often an implementation detail, and if the defined gap and margin values differ then the choices presented to a user would vary by block in a seemingly arbitrary way.

That's a good point. Using a single scale for all spacing would certainly make things simpler all around.

I'm also not sure utility classes are the best idea considering how quickly they could multiply. A simple .wp-gap-whatever class/token would let theme authors have more flexibility to take the token and use it in a way that fits their design without dictating how.

Without utility classes, what are you thoughts on how customizations would be applied? Right now, blocks can have top and bottom margins customized independently (albeit with fully custom units) and top, bottom, left, and right padding customized independently: image

My thought is that you'd need a utility class for each of those directions and for each spacing size in order to apply those customizations in a portable way. Or are you thinking it would be handled inline styles and custom properties? Like I could imagine applying a 30 padding left and right and 50 padding top and bottom might output like this:

style="padding-left: var( --wp--preset--gap-size--30 ); padding-right: var( --wp--preset--gap-size--30 ); padding-top: var( --wp--preset--gap-size--50 ); padding-bottom: var( --wp--preset--gap-size--50 );"

Is that what you're thinking?

cbirdsong commented 2 years ago

Hmm, yeah, I'd like no more inline styles than is absolutely necessary, which leaves us with utility classes.

My lack of post-theme.json theme experience is showing – is it possible to disable padding/margin values for individual directions, leaving only a single control for all sides or pair of controls for horizontal/vertical? If so, that could be taken into account and cut down on the number of utility classes generated.

In the case of margin, I think it would be best to encourage a default of layout composition being controlled by the container using gap or margin-based stacks instead of running around adjusting spacing on individual elements.

eric-michel commented 2 years ago

I know from my perspective, having independent control over all 4 directions of both margin and padding is really important.

Our parent theme contains some utility classes for margin and padding that I use all the time since spacing is not currently a feature in Gutenberg. These utility classes allow me to set 5 standard spacing amounts (plus 0) for margin and padding. I can set those spacing amounts for all 4 directions, top/bottom, left/right, or any individual direction.

The total size for these utility classes (including !important flags, which unfortunately is like a third of the overall character count) is only 3.58KB uncompressed. Here they are in their entirety:

.p-0{padding:0 !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.px-0{padding-left:0 !important;padding-right:0 !important}.pt-0{padding-top:0 !important}.pb-0{padding-bottom:0 !important}.pl-0{padding-left:0 !important}.pr-0{padding-right:0 !important}.m-0{margin:0 !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.mx-0{margin-left:0 !important;margin-right:0 !important}.mt-0{margin-top:0 !important}.mb-0{margin-bottom:0 !important}.ml-0{margin-left:0 !important}.mr-0{margin-right:0 !important}.p-xs{padding:0.25rem !important}.py-xs{padding-top:0.25rem !important;padding-bottom:0.25rem !important}.px-xs{padding-left:0.25rem !important;padding-right:0.25rem !important}.pt-xs{padding-top:0.25rem !important}.pb-xs{padding-bottom:0.25rem !important}.pl-xs{padding-left:0.25rem !important}.pr-xs{padding-right:0.25rem !important}.m-xs{margin:0.25rem !important}.my-xs{margin-top:0.25rem !important;margin-bottom:0.25rem !important}.mx-xs{margin-left:0.25rem !important;margin-right:0.25rem !important}.mt-xs{margin-top:0.25rem !important}.mb-xs{margin-bottom:0.25rem !important}.ml-xs{margin-left:0.25rem !important}.mr-xs{margin-right:0.25rem !important}.p-sm{padding:0.5rem !important}.py-sm{padding-top:0.5rem !important;padding-bottom:0.5rem !important}.px-sm{padding-left:0.5rem !important;padding-right:0.5rem !important}.pt-sm{padding-top:0.5rem !important}.pb-sm{padding-bottom:0.5rem !important}.pl-sm{padding-left:0.5rem !important}.pr-sm{padding-right:0.5rem !important}.m-sm{margin:0.5rem !important}.my-sm{margin-top:0.5rem !important;margin-bottom:0.5rem !important}.mx-sm{margin-left:0.5rem !important;margin-right:0.5rem !important}.mt-sm{margin-top:0.5rem !important}.mb-sm{margin-bottom:0.5rem !important}.ml-sm{margin-left:0.5rem !important}.mr-sm{margin-right:0.5rem !important}.p-md{padding:1rem !important}.py-md{padding-top:1rem !important;padding-bottom:1rem !important}.px-md{padding-left:1rem !important;padding-right:1rem !important}.pt-md{padding-top:1rem !important}.pb-md{padding-bottom:1rem !important}.pl-md{padding-left:1rem !important}.pr-md{padding-right:1rem !important}.m-md{margin:1rem !important}.my-md{margin-top:1rem !important;margin-bottom:1rem !important}.mx-md{margin-left:1rem !important;margin-right:1rem !important}.mt-md{margin-top:1rem !important}.mb-md{margin-bottom:1rem !important}.ml-md{margin-left:1rem !important}.mr-md{margin-right:1rem !important}.p-lg{padding:2rem !important}.py-lg{padding-top:2rem !important;padding-bottom:2rem !important}.px-lg{padding-left:2rem !important;padding-right:2rem !important}.pt-lg{padding-top:2rem !important}.pb-lg{padding-bottom:2rem !important}.pl-lg{padding-left:2rem !important}.pr-lg{padding-right:2rem !important}.m-lg{margin:2rem !important}.my-lg{margin-top:2rem !important;margin-bottom:2rem !important}.mx-lg{margin-left:2rem !important;margin-right:2rem !important}.mt-lg{margin-top:2rem !important}.mb-lg{margin-bottom:2rem !important}.ml-lg{margin-left:2rem !important}.mr-lg{margin-right:2rem !important}.p-xl{padding:4rem !important}.py-xl{padding-top:4rem !important;padding-bottom:4rem !important}.px-xl{padding-left:4rem !important;padding-right:4rem !important}.pt-xl{padding-top:4rem !important}.pb-xl{padding-bottom:4rem !important}.pl-xl{padding-left:4rem !important}.pr-xl{padding-right:4rem !important}.m-xl{margin:4rem !important}.my-xl{margin-top:4rem !important;margin-bottom:4rem !important}.mx-xl{margin-left:4rem !important;margin-right:4rem !important}.mt-xl{margin-top:4rem !important}.mb-xl{margin-bottom:4rem !important}.ml-xl{margin-left:4rem !important}.mr-xl{margin-right:4rem !important}

Note that this also includes classes like py-md which sets a md padding to top and bottom (the y axis) which would not be needed in WP, reducing the character count. But then again, these are also applying direct amounts (margin-left:2rem for .ml-lg for instance) instead of variables, which in WP tend to be long (like margin-left:var(--wp--preset--gap-size--30) for .ml-30), so that would add to the character count.

I'd love to see fully-independent 4-direction control of padding and margin available directly in the editor. I don't think it would create a particularly onerous amount of CSS in the grand scheme of things.

bradhogan commented 2 years ago

I like the idea of making spacing (margin / padding) options similar to the typography UI. If we were able to set specific pre-defined limits in that regard like we can now do with font sizing, then we'd be able to utilize clamp() to make the spacing more responsive as well, which is a big issue for me right now. If I add custom spacing to a column, for example, on mobile, that spacing sticks around. I'll use vw as a workaround for the time being, but I think setting presets would be great.

glendaviesnz commented 2 years ago

It would be good to get some design feedback on this, eg.

We could go ahead and start putting in some of the backend settings for this in the theme.json, etc. but the way this is intended to be expressed in the UI may affect the required data structures.

Have pinged the design channel.

eric-michel commented 2 years ago

How would this potentially look in the UI, buttons like font sizes, slider, dropdown, and how does a combination of custom size input and/or preset selector look

I could see theme developers adding at least 6+ spacing options, so I think something that allows for a large number makes the most sense. A stepped slider or dropdown seems most intuitive to me.

Should independent control of spacing on each side of block be allowed, in which case is there a control similar to borders which allows toggling between single value and separate sides

Big +1 for independent side control. This is essential to how we would use this feature, to the point that if it didn't allow for independent spacing, we would disable the feature and continue to use utility classes. There are a million applications for single-side control of spacing. I would also love to see top/bottom and left/right combo controls, although I could understand the utility of that might not be worth the extra complication of the UI. We very often vary the top/bottom amount of vertical padding of a group block with a color background, for instance.

glendaviesnz commented 2 years ago

@WordPress/gutenberg-design it would be great to get some design feedback on these points when someone has time - thanks.

jameskoster commented 2 years ago

Are there any blockers to adding this option to the spacing UI controls

Are you asking if we could update theme.json to allow for these standard spacing values without updating the controls in the UI?

How would this potentially look in the UI

To clarify, any spacing input needs to support:

  1. Stepped changing of preset values (e.g. jumping between 10px, 20px, 30px, 40px, etc). There could be many values (more than seven).
  2. In the case of margin / padding there could be up to 4 inputs for the same control. Two inputs (top + bottom, and left + right) could be worth exploring too.
  3. Option to specify a custom value that does not adhere to the preset scale.
  4. A clear way the select the 'default' value.

Is that correct? What about changing the unit, is that something that's only possible if you select the 'custom' option?

fabiankaegy commented 2 years ago

@jameskoster regarding your last question id say yes. The theme author can specify the unit for each of the spacing presets. But you as the user can only change the unit or the value when you switch to a custom value.

eric-michel commented 2 years ago

Option to specify a custom value that does not adhere to the preset scale.

This may not be necessary to stipulate, but it would be great if this was an option that could be disabled in theme.json in order to limit users to only the preset values.

jameskoster commented 2 years ago

When setting a custom value, should we account for fluid sizing?

Edit: And if so, would those fluid sizes be configurable per value? For example if I split margin into 4 inputs, would it be possible to set fluid values for some, but not others?

glendaviesnz commented 2 years ago

When setting a custom value, should we account for fluid sizing?

@jameskoster by fluid if you mean vh, vw, % then yes we need to account for these with custom - currently it is possible to set these with both single and split margin and padding inputs.

jameskoster commented 2 years ago

@glendaviesnz ah, no I didn't mean fluid units, sorry for the confusion. I meant fluid sizes as they're being implemented for type sizes.

So if I want a custom bottom margin, should the UI enable me to choose between a static value, or a fluid min/max?

eric-michel commented 2 years ago

@jameskoster

I meant fluid sizes as they're https://github.com/WordPress/gutenberg/pull/39529.

I don't know that I would personally ever use this option, but more flexibility is always better than less if it's not onerous to implement. Since font sizes seem to be heading that direction, maybe spacing should use the same implementation.

glendaviesnz commented 2 years ago

So if I want a custom bottom margin, should the UI enable me to choose between a static value, or a fluid min/max?

Good question. I haven't seen any discussion on this. Currently, the UI only allows a static value. I guess it is worth considering how that might fit in with the other options.

jameskoster commented 2 years ago

Was chatting with Matias earlier and he mentioned it might be better if themes only supply two values; a base unit and a scale factor.

We can then use those two values to dynamically create a range of options in things like padding controls. There are a couple of benefits to this:

  1. Padding settings can translate across themes (base value * scale factor is always going to work, whereas slugs cannot be relied upon to be consistent)
  2. Adjusting those values in global styles will affect all spacing instances in sync

cc @mtias in case I mis-interpreted any of that 😅

glendaviesnz commented 2 years ago

Was chatting with Matias earlier and he mentioned it might be better if themes only supply two values; a base unit and a scale factor.

Thanks @jameskoster. This idea was suggested above by @mrwweb:

Could scales be derived from a single spacing value and a multiplier or should each point be individually defined?

but it either got lost in all the discussion, or there are some gotchas with this approach - would be good to hear if people can see any immediate drawbacks with this approach.

In the meantime, I will see if I can open a parallel PR to #41527 that explores this approach so we can get a better idea of how they compare in reality.

glendaviesnz commented 2 years ago

My thought is that you'd need a utility class for each of those directions and for each spacing size in order to apply those customizations in a portable way. Or are you thinking it would be handled inline styles and custom properties?

At the moment the plan is to try and apply these presets via inline styles and custom properties, but to do this dynamically on the frontend, not via hardcoded inline styles in the static block content, using the new style engine framework. This removes the need for a large number of utility classes to cover every variation of space type and side, while also making these settings more transportable between themes. But the style engine isn't at the point of being able to do this for static blocks yet, so there may be some staging of the implementation needed.

cbirdsong commented 2 years ago

Was chatting with Matias earlier and he mentioned it might be better if themes only supply two values; a base unit and a scale factor.

Thanks @jameskoster. This idea was suggested above by @mrwweb:

Could scales be derived from a single spacing value and a multiplier or should each point be individually defined?

but it either got lost in all the discussion, or there are some gotchas with this approach - would be good to hear if people can see any immediate drawbacks with this approach.

I would be fine with an automatic scale being an option, but I also want the ability to define my own sizes/scale instead of being forced into using whatever system core decides on. A big benefit of presets, design tokens and the whole framework proposed in #38998 is that a theme developer can define them however they like while still maintaining compatibility with core blocks, patterns and other plugins, and can also use them as cues for writing additional CSS.

At the moment the plan is to try and apply these presets via inline styles and custom properties, but to do this dynamically on the frontend, not via hardcoded inline styles in the static block content, using the new style engine framework. This removes the need for a large number of utility classes to cover every variation of space type and side, while also making these settings more transportable between themes.

Similarly, I hope that there is a way to avoid outputting autogenerated CSS and/or injected inline styles when dealing with known non-user-provided values. style="" attributes and <style> tags are incompatible with strict content security policies, and the latter also causes issues with headless use of Wordpress content via the REST API.

If core is determined to use autogenerated CSS and inline styles, then the default way to handle it could be to adding semantic classes to the block markup/output (.has-padding-top-40, etc), and then have the style engine only generate the required styles for those semantic classes on the page instead of inline styles or .wp-container-XX-style classes. The result would be the same, except it would be based around sensible human-readable classes that could also reasonably be used if the style engine isn't present.

glendaviesnz commented 2 years ago

I would be fine with an automatic scale being an option, but I also want the ability to define my own sizes/scale instead of being forced into using whatever system core decides on.

Thanks @cbirdsong, that is helpful to know as we explore the various options.

fabiankaegy commented 2 years ago

I can echo that I can see a scale factor be something useful for some. But sadly in the world of existing design systems not everything adheres nicely to a programmatically generated scale.

Having the ability to define your own preset values in a theme would be crucial here for a lot of agency work.

scruffian commented 2 years ago

I agree. A base + scale approach would be great for those who want something to work out of the box, but we also need to have the flexibility to add custom sizes.

eric-michel commented 2 years ago

I can echo that I can see a scale factor be something useful for some. But sadly in the world of existing design systems not everything adheres nicely to a programmatically generated scale.

Having the ability to define your own preset values in a theme would be crucial here for a lot of agency work.

Agreed. Perhaps the solution is a hybrid system in which the base + scale approach produces the standardized tokens used across themes and plugins, and extra tokens can be defined for use only with that theme.

This seems a bit overly complicated, though, compared to having a standard set of slugs (ie, 10, 20, 30, etc) that are used across themes and leave room for easily adding theme-only options. Especially when we'll have the same goal with a standard set of font sizes, and I'm assuming a scaling factor won't be used there.


I'm curious about where everyone here sees the value in creating standard tokens. I've seen a lot of talk regarding the ability for site owners to be able to change themes. But in my experience, this is a pretty rare circumstance, and comes with some expected content updating.

I feel like the larger value is in creating standard styling components for plugins that produce frontend content, such that those plugins can reliably use custom properties or classes that will match whatever theme they're installed on top of, creating a much more streamlined and consistent look. So many plugins that create frontend content come packaged with very opinionated styling because of a lack of reliable and accessible styling from the theme.

I guess I'm wondering what y'all's view is on the primary goal of this project.

glendaviesnz commented 2 years ago

I guess I'm wondering what y'all's view is on the primary goal of this project.

Just to add some context, the reason for exploring the scale approach is to easily allow the adjustment of the spacing across a whole site or page within the site editor/global styles, as in this example. So this is thinking about it from the point of view of an individual end user who has installed a theme on their site and would like a little more/little less space all around, without having to know about theme.json file settings. But, as noted by others, whatever setup is decided on needs to also take into account the needs of an agency type user who wants a strict set of spacing options that can't be adjusted by the user, and which may or may not be able to be autogenerated.

eric-michel commented 2 years ago

Just to add some context, the reason for exploring the scale approach is to easily allow the adjustment of the spacing across a whole site or page within the site editor/global styles, as in this example. So this is thinking about it from the point of view of an individual end user who has installed a theme on their site and would like a little more/little less space all around, without having to know about theme.json file settings. But, as noted by others, whatever setup is decided on needs to also take into account the needs of an agency type user who wants a strict set of spacing options that can't be adjusted by the user.

I totally agree with this, and realize my question was really unclear. Let me rephrase.

Referring to my first comment in this thread:

  1. For margin and padding, there is no way to define a limited, preset list of values at all in theme.json. The current margin and padding controls only allow for direct numeric control of spacing (via units like px, rem, etc). This is in contrast to font size and color palette, which at least allow for defining a limited list of options.
  2. If preset values become allowed, should they be unified into a standard naming convention, like gap-1, gap-2, gap-3 etc to improve interoperability? This is the proposal in the issue (https://github.com/WordPress/gutenberg/issues/38998) that inspired this more narrowly focused one.

My question was specifically in regards to the 2nd item there. How important is interoperability? Is it worth the work? Is it even truly viable given how much flexibility theme and plugin authors desire? What are the primary problems we're trying to solve (e.g. theme switching vs plugin frontend styling)? How likely are theme authors to be in compliance (i.e. how often will they properly set the standard list of spacing tokens) vs just creating all of their own options that will not be usable by other themes or plugins?

I see you've already started a PR that already does some awesome work getting this rolling, and I'm wondering if having more clarity on how (or even if) interoperability should work would help your efforts.

From our perspective, simply having the ability to set a standard list of spacing options (totally ignoring interoperability) would be a huge leap forward. Interoperability certainly sounds nice, but isn't game-changing. Others might feel very differently though.

glendaviesnz commented 2 years ago

My question was specifically in regards to the 2nd item there. How important is interoperability?

Thanks for clarifying, would be good to hear people's thoughts on this. One other aspect to add to this would be the transferability of blocks and patterns between sites - a standardised set of spacing presets would help to make this more seamless.

scruffian commented 2 years ago

One other aspect to add to this would be the transferability of blocks and patterns between sites

This is the main pain point IMO. Using custom names in a theme means that when users switch themes all the patterns from their old theme will lose all styles - what we really want is for patterns to work in any theme.

Similarly, if blocks know that certain values will always be available they can integrate more fully with the theme.

fabiankaegy commented 2 years ago

I'm definitely not opposed to interoperability. It shouldn't come at the expense of creative freedom though in my opinion. In this case that means that whilst the default behavior may be a standard scale that gets generated automatically based on a scale factor, theme authors should always have the option to break out of that system and provide their own.

This especially comes true when we start looking at custom build solutions where theme switching is not really a common occurrence.

cbirdsong commented 2 years ago

As an agency dev building custom themes, my personal primary interest here is having spacing adjustable in the editor with consistent responsive presets instead of only via custom values, but I can also see how interoperable spacing, color and typography tokens would be useful for us. It could:


How likely are theme authors to be in compliance (i.e. how often will they properly set the standard list of spacing tokens) vs just creating all of their own options that will not be usable by other themes or plugins?

From a broader perspective, any improvement on the status quo here would be great, even if it's imperfect. Right now you might have to readjust all of your spacing values if they don't match your new theme, and if that changes to "oh, in this new theme lots of things using spacing value 3 are a bit cramped, so I need to go through and set most of them to spacing value 4" then that's a big improvement.

glendaviesnz commented 2 years ago

Thanks for the detailed input everyone! I think this has been a really useful discussion to try and get a better understanding of the various needs of different user groups in relation to getting some standardised spacing options into Gutenberg and core WP.

I am going to try and summarise the key points from the above discussion and then suggest a way forward to start getting something implemented.

Summary of key points

Suggested way forward

  1. A PR is open here that adds a default spacingScale to the core theme.json. From this spacing scale a 6 values list of presets is generated including a 0 space option. The slugs are 0,10,20,30,40,50 and the values are currently the rem equivalent of a standard 8 point grid system - the exact values in the core defaults is up for discussion
  2. Themes can either add their own spacingScale to override the core values, or they can add a static spacingSizes array. The static spacingSizes array behaves the same as fontSizes in that it is merged with the core array, and any custom theme settings with the same slug will override the core ones.
  3. While spacingScale and spacingSizes will initially be shared across padding/margin/gap, these values could be extended if needed at a later stage, eg. spacingSizes could be a single array, or an object with separated arrays for padding, margin, gap
  4. The above PR currently only generates the css custom properties for the spacing presets, eg. --wp--preset--spacing-size--30: 1.5rem. Once this approach is agreed, and the PR merged, work can then start on adding the UI that is needed in order to select from the presets when editing blocks. This stage of the work will also look at what/how utility classnames will be adding to indicate which spacing presets are applied
  5. In terms of applying these presets to the block content the aim is to do this dynamically in the editor and the frontend rather than serialising class or styles into the static block content. The new style engine is the likely place to implement this, which is not quite ready for handling static blocks, so there may be a staged implementation with the spacing presets being adding to dynamic blocks first, and then static blocks once the style engine supports them.
bradhogan commented 2 years ago

@glendaviesnz I'm not entirely sure if this is covered in your outline of the key points, but in my opinion, allowing some sort of control for spacing on mobile is absolutely critical and the lack of mobile spacing right now makes designs in Gutenberg very difficult to look good across multiple devices. I think allowing a min, max, clamp in the theme.json option would be a simple solution that could go a long way - similar to fontSize. Thanks!

eric-michel commented 2 years ago

@glendaviesnz

Themes can either add their own spacingScale to override the core values, or they can add a static spacingSizes array. The static spacingSizes array behaves the same as fontSizes in that it is merged with the core array, and any custom theme settings with the same slug will override the core ones.

I think this is an outstanding implementation. It offers an easy way to provide a custom scale for most, but extra flexibility to those who need it. I also agree that steps-of-10 is the ideal slug naming scheme. This same functionality could be extended to font sizes so both work similarly.

In terms of applying these presets to the block content the aim is to do this dynamically in the editor and the frontend rather than serialising class or styles into the static block content. The new style engine is the likely place to implement this, which is not quite ready for handling static blocks, so there may be a staged implementation with the spacing presets being adding to dynamic blocks first, and then static blocks once the style engine supports them.

Regarding execution on the frontend, I think @cbirdsong made a great point here:

Similarly, I hope that there is a way to avoid outputting autogenerated CSS and/or injected inline styles when dealing with known non-user-provided values. style="" attributes and