From eb03789c24095f333157f6c148f2c412244f12fd Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Wed, 21 Jan 2026 10:42:08 +1100 Subject: [PATCH 1/6] Add styles for responsive grid layout --- src/wp-includes/block-supports/layout.php | 97 ++++++++++++++++++----- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/src/wp-includes/block-supports/layout.php b/src/wp-includes/block-supports/layout.php index fb46b78ab8f24..e361bc462bd34 100644 --- a/src/wp-includes/block-supports/layout.php +++ b/src/wp-includes/block-supports/layout.php @@ -233,7 +233,7 @@ function wp_register_layout_support( $block_type ) { * @param bool $has_block_gap_support Optional. Whether the theme has support for the block gap. Default false. * @param string|string[]|null $gap_value Optional. The block gap value to apply. Default null. * @param bool $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false. - * @param string $fallback_gap_value Optional. The block gap value to apply. Default '0.5em'. + * @param string|array $fallback_gap_value Optional. The block gap value to apply. If it's an array expected properties are "top" and/or "left". Default '0.5em'. * @param array|null $block_spacing Optional. Custom spacing set on the block. Default null. * @return string CSS styles on success. Else, empty string. */ @@ -427,7 +427,12 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false foreach ( $gap_sides as $gap_side ) { $process_value = $gap_value; if ( is_array( $gap_value ) ) { - $process_value = $gap_value[ $gap_side ] ?? $fallback_gap_value; + if ( is_array( $fallback_gap_value ) ) { + $fallback_value = $fallback_gap_value[ $gap_side ] ?? reset( $fallback_gap_value ); + } else { + $fallback_value = $fallback_gap_value; + } + $process_value = $gap_value[ $gap_side ] ?? $fallback_value; } // Get spacing CSS variable from preset value if provided. if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) { @@ -490,21 +495,14 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false } } } elseif ( 'grid' === $layout_type ) { - if ( ! empty( $layout['columnCount'] ) ) { - $layout_styles[] = array( - 'selector' => $selector, - 'declarations' => array( 'grid-template-columns' => 'repeat(' . $layout['columnCount'] . ', minmax(0, 1fr))' ), - ); + /* + * If the gap value is an array, we use the "left" value because it represents the vertical gap, which + * is the relevant one for computation of responsive grid columns. + */ + if ( is_array( $fallback_gap_value ) ) { + $responsive_gap_value = $fallback_gap_value['left'] ?? reset( $fallback_gap_value ); } else { - $minimum_column_width = ! empty( $layout['minimumColumnWidth'] ) ? $layout['minimumColumnWidth'] : '12rem'; - - $layout_styles[] = array( - 'selector' => $selector, - 'declarations' => array( - 'grid-template-columns' => 'repeat(auto-fill, minmax(min(' . $minimum_column_width . ', 100%), 1fr))', - 'container-type' => 'inline-size', - ), - ); + $responsive_gap_value = $fallback_gap_value; } if ( $has_block_gap_support && isset( $gap_value ) ) { @@ -514,7 +512,12 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false foreach ( $gap_sides as $gap_side ) { $process_value = $gap_value; if ( is_array( $gap_value ) ) { - $process_value = $gap_value[ $gap_side ] ?? $fallback_gap_value; + if ( is_array( $fallback_gap_value ) ) { + $fallback_value = $fallback_gap_value[ $gap_side ] ?? reset( $fallback_gap_value ); + } else { + $fallback_value = $fallback_gap_value; + } + $process_value = $gap_value[ $gap_side ] ?? $fallback_value; } // Get spacing CSS variable from preset value if provided. if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) { @@ -524,14 +527,58 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false } $combined_gap_value .= "$process_value "; } - $gap_value = trim( $combined_gap_value ); + $gap_value = trim( $combined_gap_value ); + $responsive_gap_value = $gap_value; + } - if ( null !== $gap_value && ! $should_skip_gap_serialization ) { + // Ensure 0 values have a unit so they work in calc(). + if ( '0' === $responsive_gap_value || 0 === $responsive_gap_value ) { + $responsive_gap_value = '0px'; + } + + if ( ! empty( $layout['columnCount'] ) && ! empty( $layout['minimumColumnWidth'] ) ) { + $max_value = 'max(' . $layout['minimumColumnWidth'] . ', (100% - (' . $responsive_gap_value . ' * (' . $layout['columnCount'] . ' - 1))) /' . $layout['columnCount'] . ')'; + $layout_styles[] = array( + 'selector' => $selector, + 'declarations' => array( + 'grid-template-columns' => 'repeat(auto-fill, minmax(' . $max_value . ', 1fr))', + 'container-type' => 'inline-size', + ), + ); + if ( ! empty( $layout['rowCount'] ) ) { $layout_styles[] = array( 'selector' => $selector, - 'declarations' => array( 'gap' => $gap_value ), + 'declarations' => array( 'grid-template-rows' => 'repeat(' . $layout['rowCount'] . ', minmax(1rem, auto))' ), ); } + } elseif ( ! empty( $layout['columnCount'] ) ) { + $layout_styles[] = array( + 'selector' => $selector, + 'declarations' => array( 'grid-template-columns' => 'repeat(' . $layout['columnCount'] . ', minmax(0, 1fr))' ), + ); + if ( ! empty( $layout['rowCount'] ) ) { + $layout_styles[] = array( + 'selector' => $selector, + 'declarations' => array( 'grid-template-rows' => 'repeat(' . $layout['rowCount'] . ', minmax(1rem, auto))' ), + ); + } + } else { + $minimum_column_width = ! empty( $layout['minimumColumnWidth'] ) ? $layout['minimumColumnWidth'] : '12rem'; + + $layout_styles[] = array( + 'selector' => $selector, + 'declarations' => array( + 'grid-template-columns' => 'repeat(auto-fill, minmax(min(' . $minimum_column_width . ', 100%), 1fr))', + 'container-type' => 'inline-size', + ), + ); + } + + if ( $has_block_gap_support && null !== $gap_value && ! $should_skip_gap_serialization ) { + $layout_styles[] = array( + 'selector' => $selector, + 'declarations' => array( 'gap' => $gap_value ), + ); } } @@ -804,6 +851,16 @@ function wp_render_layout_support_flag( $block_content, $block ) { $block_gap = $global_settings['spacing']['blockGap'] ?? null; $has_block_gap_support = isset( $block_gap ); + // Get default blockGap value from global styles for use in layouts like grid. + // Check block-specific styles first, then fall back to root styles. + $block_name = $block['blockName'] ?? ''; + $global_styles = wp_get_global_styles(); + $global_block_gap_value = $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? ( $global_styles['spacing']['blockGap'] ?? null ); + + if ( null !== $global_block_gap_value ) { + $fallback_gap_value = $global_block_gap_value; + } + /* * Generates a unique ID based on all the data required to obtain the * corresponding layout style. Keeps the CSS class names the same From 23f0581423045a92cc9974ba93199c8cbdfcf187 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 22 Jan 2026 10:00:45 +1100 Subject: [PATCH 2/6] Ensure grid column never exceeds parent's width --- src/wp-includes/block-supports/layout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/block-supports/layout.php b/src/wp-includes/block-supports/layout.php index e361bc462bd34..e1c301971d701 100644 --- a/src/wp-includes/block-supports/layout.php +++ b/src/wp-includes/block-supports/layout.php @@ -537,7 +537,7 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false } if ( ! empty( $layout['columnCount'] ) && ! empty( $layout['minimumColumnWidth'] ) ) { - $max_value = 'max(' . $layout['minimumColumnWidth'] . ', (100% - (' . $responsive_gap_value . ' * (' . $layout['columnCount'] . ' - 1))) /' . $layout['columnCount'] . ')'; + $max_value = 'max(min(' . $layout['minimumColumnWidth'] . ', 100%), (100% - (' . $responsive_gap_value . ' * (' . $layout['columnCount'] . ' - 1))) /' . $layout['columnCount'] . ')'; $layout_styles[] = array( 'selector' => $selector, 'declarations' => array( From c0e33fe61fd7f29be17d8edb868093fd9ca11962 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 22 Jan 2026 13:17:40 +1100 Subject: [PATCH 3/6] perf improvement --- src/wp-includes/block-supports/layout.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/block-supports/layout.php b/src/wp-includes/block-supports/layout.php index e1c301971d701..7be87bfe34562 100644 --- a/src/wp-includes/block-supports/layout.php +++ b/src/wp-includes/block-supports/layout.php @@ -615,6 +615,8 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false * @return string Filtered block content. */ function wp_render_layout_support_flag( $block_content, $block ) { + static $global_styles = null; + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); $block_supports_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false ); $child_layout = $block['attrs']['style']['layout'] ?? null; @@ -853,8 +855,10 @@ function wp_render_layout_support_flag( $block_content, $block ) { // Get default blockGap value from global styles for use in layouts like grid. // Check block-specific styles first, then fall back to root styles. - $block_name = $block['blockName'] ?? ''; - $global_styles = wp_get_global_styles(); + $block_name = $block['blockName'] ?? ''; + if ( null === $global_styles ) { + $global_styles = gutenberg_get_global_styles(); + } $global_block_gap_value = $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? ( $global_styles['spacing']['blockGap'] ?? null ); if ( null !== $global_block_gap_value ) { From b8c247150781d37624ec57025b8bc6c0afec09f9 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 22 Jan 2026 14:27:16 +1100 Subject: [PATCH 4/6] correct prefix --- src/wp-includes/block-supports/layout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/block-supports/layout.php b/src/wp-includes/block-supports/layout.php index 7be87bfe34562..bd1badc7a6cd6 100644 --- a/src/wp-includes/block-supports/layout.php +++ b/src/wp-includes/block-supports/layout.php @@ -857,7 +857,7 @@ function wp_render_layout_support_flag( $block_content, $block ) { // Check block-specific styles first, then fall back to root styles. $block_name = $block['blockName'] ?? ''; if ( null === $global_styles ) { - $global_styles = gutenberg_get_global_styles(); + $global_styles = wp_get_global_styles(); } $global_block_gap_value = $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? ( $global_styles['spacing']['blockGap'] ?? null ); From 793540492a5a13ae54a590fe4c1f90a84841b415 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 22 Jan 2026 14:47:54 +1100 Subject: [PATCH 5/6] Update test hashes and ensure test outputs actual classsname as well as expected for easier debug --- tests/phpunit/tests/block-supports/layout.php | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/tests/phpunit/tests/block-supports/layout.php b/tests/phpunit/tests/block-supports/layout.php index 3b71b2c4b7482..8661077df8662 100644 --- a/tests/phpunit/tests/block-supports/layout.php +++ b/tests/phpunit/tests/block-supports/layout.php @@ -293,7 +293,7 @@ public function data_layout_support_flag_renders_classnames_on_wrapper() { ), ), ), - 'expected_output' => '
', + 'expected_output' => '
', ), 'single wrapper block layout with grid type' => array( 'args' => array( @@ -312,7 +312,7 @@ public function data_layout_support_flag_renders_classnames_on_wrapper() { ), ), ), - 'expected_output' => '
', + 'expected_output' => '
', ), 'skip classname output if block does not support layout and there are no child layout classes to be output' => array( 'args' => array( @@ -542,9 +542,19 @@ public function test_layout_support_flag_renders_consistent_container_hash( $blo $processor = new WP_HTML_Tag_Processor( $output ); $processor->next_tag(); - $this->assertTrue( - $processor->has_class( $expected_class ), - "Expected class '$expected_class' not found in the rendered output, probably because of a different hash." + // Extract the actual container class from the output for better error messages. + $actual_class = ''; + foreach ( $processor->class_list() as $class_name ) { + if ( str_starts_with( $class_name, 'wp-container-core-group-is-layout-' ) ) { + $actual_class = $class_name; + break; + } + } + + $this->assertEquals( + $expected_class, + $actual_class, + 'Expected class not found in the rendered output, probably because of a different hash.' ); } @@ -566,7 +576,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { ), ), ), - 'expected_class' => 'wp-container-core-group-is-layout-c5c7d83f', + 'expected_class' => 'wp-container-core-group-is-layout-a6248535', ), 'default type block gap 24px' => array( 'block_attributes' => array( @@ -579,7 +589,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { ), ), ), - 'expected_class' => 'wp-container-core-group-is-layout-634f0b9d', + 'expected_class' => 'wp-container-core-group-is-layout-61b496ee', ), 'constrained type justified left' => array( 'block_attributes' => array( @@ -588,7 +598,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { 'justifyContent' => 'left', ), ), - 'expected_class' => 'wp-container-core-group-is-layout-12dd3699', + 'expected_class' => 'wp-container-core-group-is-layout-54d22900', ), 'constrained type justified right' => array( 'block_attributes' => array( @@ -597,7 +607,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { 'justifyContent' => 'right', ), ), - 'expected_class' => 'wp-container-core-group-is-layout-f1f2ed93', + 'expected_class' => 'wp-container-core-group-is-layout-2910ada7', ), 'flex type horizontal' => array( 'block_attributes' => array( @@ -607,7 +617,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { 'flexWrap' => 'nowrap', ), ), - 'expected_class' => 'wp-container-core-group-is-layout-2487dcaa', + 'expected_class' => 'wp-container-core-group-is-layout-f5d79bea', ), 'flex type vertical' => array( 'block_attributes' => array( @@ -616,7 +626,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { 'orientation' => 'vertical', ), ), - 'expected_class' => 'wp-container-core-group-is-layout-fe9cc265', + 'expected_class' => 'wp-container-core-group-is-layout-2c90304e', ), 'grid type' => array( 'block_attributes' => array( @@ -624,7 +634,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { 'type' => 'grid', ), ), - 'expected_class' => 'wp-container-core-group-is-layout-478b6e6b', + 'expected_class' => 'wp-container-core-group-is-layout-5a23bf8e', ), 'grid type 3 columns' => array( 'block_attributes' => array( @@ -633,7 +643,7 @@ public function data_layout_support_flag_renders_consistent_container_hash() { 'columnCount' => 3, ), ), - 'expected_class' => 'wp-container-core-group-is-layout-d3b710ac', + 'expected_class' => 'wp-container-core-group-is-layout-cda6dc4f', ), ); } From 033d1b596576781deae1821e5d0a28c3617c6e99 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 22 Jan 2026 15:10:39 +1100 Subject: [PATCH 6/6] hash updates in columns fixtures --- tests/phpunit/data/blocks/fixtures/core__columns.server.html | 2 +- .../data/blocks/fixtures/core__columns__deprecated.server.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/data/blocks/fixtures/core__columns.server.html b/tests/phpunit/data/blocks/fixtures/core__columns.server.html index 02d855cbd6c38..5b5faf4f3d7a4 100644 --- a/tests/phpunit/data/blocks/fixtures/core__columns.server.html +++ b/tests/phpunit/data/blocks/fixtures/core__columns.server.html @@ -1,5 +1,5 @@ -
+
diff --git a/tests/phpunit/data/blocks/fixtures/core__columns__deprecated.server.html b/tests/phpunit/data/blocks/fixtures/core__columns__deprecated.server.html index 6b695d15963de..b19349744dfbc 100644 --- a/tests/phpunit/data/blocks/fixtures/core__columns__deprecated.server.html +++ b/tests/phpunit/data/blocks/fixtures/core__columns__deprecated.server.html @@ -1,5 +1,5 @@ -
+

Column One, Paragraph One