Repository navigation
Column block: Migrate width to dimensions block support - #76684
aaronrobertshaw wants to merge 12 commits into
Conversation
|
Size Change: +322 B (0%) Total Size: 7.66 MB
ℹ️ View Unchanged
|
11e335a to
928613c
Compare
|
Important Review skippedAuto reviews are disabled on this repository. To trigger a review, include ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
I've rebased this and fixed what that turned up, including:
One thing worth a look: |
🤖 PR meta 🤖🎉 PropsIf you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. Updated as activity occurs, without notifying anyone named here. Add the 📦 Bundle sizeSize Change: +1.38 kB (+0.02%) Total Size: 8.21 MB 📦 View Changed
From ⚡ PerformanceShow the resultsClient side metrics exclude the server response time. front-end-block-theme
front-end-classic-theme
media-processing
media-upload
post-editor
site-editor
From 🏁 Flaky testsSome tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information. List in
|
928613c to
92824d9
Compare
Handle the Column block's width through the `dimensions.width` block support so it aligns with Global Styles and can be set from theme.json. Columns size themselves with `flex-basis` rather than `width` because they live in a flex container, so `WP_Theme_JSON` converts the computed `width` declaration and pins `flex-grow` to `0`. This runs at the block level, on block style variations, and on variation styles set within a responsive breakpoint. The block.json and theme.json changes arrive separately with the next Gutenberg package update, following the Button block migration. Developed in: WordPress/gutenberg#76684 See #66055.
|
I've updated this now as well for responsive styles and states. So should be ready enough for an initial review. |
ramonjd
left a comment
There was a problem hiding this comment.
I tested all the steps and things are working as described 🎉
✅ migrations take place on save
✅ widths are preserved on the transforms.
✅ theme.json styles are applied
While testing I think I found a bug, but I think it's existing and only made obvious by this PR:
- Insert a columns block, e.g., 33/66
- See that the widths of the child column blocks are 33% and 66% respectively
- Select child column blocks, e.g., in the list view and apply a background
- The second column's width changes from 66% to 33%
Kapture.2026-09-07.at.14.51.08.mp4
After a conversation with my robot, I discovered that multi-selection sends one attributes object to every selected block, and the reducer merges it shallowly.
So the style from the block that renders the control replaces the other block's style objects fully.
Now that column width lives in style.dimensions.width, setting a background on two columns copies the first one's width over the second.
I can reproduce on trunk with core/button, so not a blocker here.
I think maybe we'd could only update the attributes of the other blocks if something has changed.
tellthemachines
left a comment
There was a problem hiding this comment.
I gave this a test run and it's mostly working fine, except for responsive behaviour. Aside from the issue with block instances I commented on below, we have a specificity problem to deal with 😅 relative to the default responsive styles of the columns block:
@media (max-width: 781px) {
.wp-block-columns:not(.is-not-stacked-on-mobile) > .wp-block-column {
flex-basis: 100% !important;
}
}
These currently override both the global viewport state styles and the per-instance ones. I'm not sure there's much that can be done about the global ones, because they shouldn't use !important, but for the instance ones we could probably make the selector itself more specific.
It would be better if we could reduce the specificity of that default rule, but that would risk causing breakage of existing websites that rely on it.
A consequence of being able to set width globally for Column is that, if it is set, it's no longer possible to unset the width of a Column instance to make it take up the remaining space of its container, so instead of this:
we end up with this (where the second column obtains its width from global styles):
Not sure how best to fix that; perhaps making the unset value explicitly output flex-basis: 0 and flex-grow:1? But that shouldn't really be blocking for this PR; I suspect it's unlikely that global width on Column will be a popular setting.
5a49497 to
ef5b666
Compare
|
I believe all the feedback to date has been addressed, with tests included. I've also rebased this on trunk for good measure. Preset slugs Used Instance state widths Moved this out of the class and into The I ultimately decided to try an alternative approach here. Rather than raise the state selector, the stacking rule is now wrapped in Unsetting a width under a global one Thanks for the suggestion on this one @tellthemachines. It now outputs It does change the Width control from a plain unit control into a preset control, but that lets Fill work the same way as any other preset. Multi-selection I'm not sure the proposed fix here holds up. Adding a background changes It might need a per-leaf diff, but I can also see other cases and other blocks where we do want a blanket copy of styles. I'm honestly not sure on the best course here. ScreenshotsFixed 200px, Fill, fixed 200px. Only the middle column grows.
Fill selected in the Width control.
A global column width of 25%, without and with Fill on the second column.
A preset with a camelCase slug now resolves.
A mobile state width at a 400px viewport. The first column keeps its 75%, the second still stacks.
|
ef5b666 to
010dcb3
Compare
| "width": true, | ||
| "dimensionSizes": [ | ||
| { | ||
| "name": "Fill", |
There was a problem hiding this comment.
Fill feels wrong as a preset, because presets are usually discrete values that can be configured by a theme, and in this case if a theme were to override these presets we’d lose what should be base functionality for this control. The way presets are displayed in the UI also makes it very weird - you go from fill to 25%, and the Column jumps from wide to small again.
I think it’s OK to leave this out for now - as I mentioned before, this only becomes a problem if you set a global width value for Column, which feels unlikely because most sites would need a range of different column widths (Or we could disallow global width for Column perhaps).
This does raise the question of whether using the child layout controls isn’t more appropriate here, because the problem of allowing both fill and fixed widths is already solved there. Columns is always a flex layout and Column blocks can only exist within it. Plus, child layout controls already work with viewport states, so it could probably be done fairly easily with only some specificity overrides needed for those legacy responsive Column styles.
| const getRowInnerBlocks = ( innerBlocks ) => { | ||
| const columnWidths = innerBlocks.map( ( column ) => { | ||
| return getColumnWidth( column?.attributes?.width ); | ||
| return normalizeWidth( column?.attributes?.style?.dimensions?.width ); |
There was a problem hiding this comment.
| export function hasExplicitPercentColumnWidths( blocks ) { | ||
| return blocks.every( ( block ) => { | ||
| const blockWidth = block.attributes.width; | ||
| const blockWidth = block.attributes?.style?.dimensions?.width; |
There was a problem hiding this comment.
Should this match presets? I'm seeing a difference against trunk for the case of 2 x cols at 50% width. If I reduce the number of cols to 1 it no longer expands to 100%:
2026-09-23.17.22.49.mp4
Replaces the bespoke `width` attribute with `dimensions.width`, so the column width is set from the Dimensions panel and can be styled from theme.json and Global Styles. This mirrors how `core/button` handles its own width, including `__experimentalSkipSerialization`. Deprecations migrate the numeric and string forms of the old attribute. Existing front end markup is unaffected because the block is static and keeps its inline `flex-basis`. Callers that built columns with the old attribute are updated: the block variations, the column count control and the Comments block inner blocks template.
A width set on `core/column` in theme.json or Global Styles had no effect. Three things were missing. `settings.blocks.core/column.dimensions.width` is now enabled in `lib/theme.json`. Block supports only ever disable that setting, so without it the Dimensions panel never rendered the width control. `WP_Theme_JSON` and the style engine rewrite the generated `width` declaration to `flex-basis` and add `flex-grow: 0`, because a column sizes itself within a flex container. The default flex sizing in `style.scss` now uses the same `:root :where()` format as the global styles output, so the generated rules win on source order. It previously sat at a higher specificity and overrode them.
The Row and Grid transforms still read and wrote the old `width` attribute, so a column's width was dropped when converting in either direction. They also copy a column's styles onto the Group they create. Now that the width lives in `style.dimensions.width` it rode along, and rendered as a `width` on the Group. It is stripped instead, matching the previous behaviour: Grid drops it, Row converts it to the child layout's `flexSize`.
Sets a width from the Dimensions panel and asserts it is written to `style.dimensions.width`. The existing width redistribution tests are updated to the new attribute shape.
Column widths set on a block style variation within a breakpoint were output as `width` rather than `flex-basis`, so they had no effect. The breakpoint loop inside the variation branch computes its declarations on its own path and never reached the existing conversion. Adds the missing call site, plus PHP coverage for column widths at the block level, within a breakpoint, and on a style variation.
The column width was extracted by deleting `dimensions.width` from the node's styles. Those styles are a live reference into the global styles tree, so the render pass mutated its caller's data. Replaces it with a helper that rewrites the generated declarations instead, mirroring `WP_Theme_JSON_Gutenberg::update_column_width_declarations`. Every node type passes through `renderStylesNode`, so responsive and style variation widths are covered by the one call site.
…support Tracks the Core PR that backports the `WP_Theme_JSON` changes.
`getDimensionPresetCssVar` built the custom property from the raw slug, but both the PHP and JS sides kebab-case it when they declare it. A slug such as `wideColumn` or `size2` therefore resolved to a property that was never declared, and whatever consumed it got no value. Delegates to the style engine's `getCSSValueFromRawStyle`, which already handles the conversion, rather than parsing the prefix again here.
A width set for a viewport state on a single column was output as `width`, which does nothing to a flex child, so the value never took effect. Theme.json state styles already converted; this is the instance path, on the front end in `gutenberg_add_block_state_style_rule` and in the editor's state CSS. The conversion was a private method on `WP_Theme_JSON_Gutenberg`, which `states.php` cannot reach, and a module-private function in the global styles engine, which `block-editor` cannot import. Moves the PHP copy to `lib/block-supports/dimensions.php` as a plain function and exports the JS mirror, so each language has one place both callers share.
Once a column width is set in theme.json or Global Styles, every column inherits it and there is no way to say "no width, take the remaining space". Adds width presets for the column, following the pattern `core/button` already uses, including a Fill preset that outputs `flex-basis: 0` and `flex-grow: 1`. Columns with a width still keep it and do not grow, so a row of fixed, fill, fixed behaves as expected. Only a filling column writes `flex-grow` inline; the rest rely on the existing `[style*="flex-basis"]` rule, which leaves existing markup unchanged. The Fill preset's size is `auto` rather than `0` because the width control resolves a preset to its size before matching it back to a slug, and `0` is the sentinel for its built-in "None" option. With a size of `0` the control showed None instead of Fill. This turns the column's Width control from a plain unit input into a preset control.
Below 782px the columns stylesheet forces `flex-basis: 100% !important` so columns stack. Block instance state styles are also `!important`, so the two compete inside the important tier, where the stacking rule's three classes beat the state rule's single hashed class. A width set for the mobile or tablet state was therefore discarded. Wraps the stacking selector in `:root :where()`, so it sits at the same specificity as the rest of the global styles output and a state width wins on source order. State styles always print after the block library stylesheet, on the front end and in the editor canvas. Stacking itself is unchanged: `!important` still beats the inline width and any global styles width, because important beats normal whatever the specificity.
010dcb3 to
0362cfd
Compare






What?
See #76525
Migrate the column block's custom
widthattribute to use thedimensions.widthblock support.Why?
The column block manages its own width attribute and a bespoke inspector control, separate from the rest of the design tooling. Moving to the block support lines it up with everything else, makes column width available in theme.json and Global Styles, and drops the custom UI.
How?
widthattribute to thedimensions.widthblock support, and the custom width panel goes with it.settings.blocks.core/column.dimensions.widthis enabled inlib/theme.json, without which the dimensions panel never shows the control.flex-basisrather thanwidth. Serialization is skipped for the support, and the conversion happens in one place per language, inlib/block-supports/dimensions.phpand in the global styles engine. theme.json, style variations, breakpoints and per-block states all run through it.Fill. A column set to Fill takes the remaining space in its row, which is how one column opts out of a width set for every column. Adding presets turns the Width control into a preset control rather than a plain input.columns/style.scssdrop to:root :where(), so a width from Global Styles or a viewport state can win on source order.getDimensionPresetCssVar, which built the custom property from the raw slug. A preset likewideColumnpointed at a property that was never declared.Screenshots
The Width control, now showing presets.
A row of fixed, fill, fixed. Only the middle column grows.
Testing Instructions
Existing content
style.dimensions.width.Setting a width
flex-basisis applied, notwidth.%,pxandem.Fill
Fill. Only the second should grow.styles.blocks.core/column.dimensions.widthto25%. Columns without their own width should take it rather than stretching, and a width set on one column should still win.Fill. It should ignore the global width and take the remaining space.Responsive
style['@mobile'].dimensions.width.Transforms