From 63d9662683381677c7e97b41528a8ff99c5623bc Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 10 Feb 2026 11:35:44 +1100 Subject: [PATCH 1/5] Allow grid to use style variation blockGap values for columns calculation --- lib/block-supports/layout.php | 54 ++++++++++++++++++- packages/block-editor/src/hooks/layout.js | 63 +++++++++++++++++++++-- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index b9904d35ac9d86..a2b248cc0f15eb 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -5,6 +5,42 @@ * @package gutenberg */ +/** + * Get the first style variation name from a className string that matches a registered style. + * + * @param string $class_name CSS class string for a block. + * @param array $registered_styles Currently registered block styles. + * + * @return string|null The name of the first registered variation, or null if none found. + */ +function gutenberg_get_variation_name_from_class( $class_name, $registered_styles = array() ) { + if ( empty( $class_name ) ) { + return null; + } + + $matches = array(); + $classes = explode( ' ', $class_name ); + + foreach ( $classes as $name ) { + if ( str_starts_with( $name, 'is-style-' ) ) { + $match = substr( $name, strlen( 'is-style-' ) ); + if ( 'default' !== $match ) { + $matches[] = $match; + } + } + } + + foreach ( $matches as $variation ) { + foreach ( $registered_styles as $style ) { + if ( isset( $style['name'] ) && $style['name'] === $variation ) { + return $variation; + } + } + } + + return null; +} + /** * Returns layout definitions, keyed by layout type. * @@ -898,12 +934,26 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { $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. + // Check style variation first, then block-specific styles, then fall back to root 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 ); + + // Check if the block has an active style variation with a blockGap value. + // Only check the registry if the className contains a variation class to avoid unnecessary lookups. + $variation_block_gap_value = null; + $block_class_name = $block['attrs']['className'] ?? ''; + if ( ! empty( $block_class_name ) && str_contains( $block_class_name, 'is-style-' ) && ! empty( $block_name ) ) { + $styles_registry = WP_Block_Styles_Registry::get_instance(); + $registered_styles = $styles_registry->get_registered_styles_for_block( $block_name ); + $variation_name = gutenberg_get_variation_name_from_class( $block_class_name, $registered_styles ); + if ( $variation_name ) { + $variation_block_gap_value = $global_styles['blocks'][ $block_name ]['variations'][ $variation_name ]['spacing']['blockGap'] ?? null; + } + } + + $global_block_gap_value = $variation_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; diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 9df7695f1c1403..462fb9dbc3a6f3 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -8,7 +8,11 @@ import clsx from 'clsx'; */ import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; -import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; +import { + getBlockSupport, + hasBlockSupport, + store as blocksStore, +} from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; import { __experimentalToggleGroupControl as ToggleGroupControl, @@ -32,6 +36,39 @@ import { useBlockSettings, useStyleOverride } from './utils'; import { unlock } from '../lock-unlock'; import { globalStylesDataKey } from '../store/private-keys'; +const VARIATION_PREFIX = 'is-style-'; + +/** + * Get the first style variation name from a className string that matches a registered style. + * + * @param {string} className CSS class string for a block. + * @param {Array} registeredStyles Currently registered block styles. + * + * @return {string|null} The name of the first registered variation, or null if none found. + */ +function getVariationNameFromClass( className, registeredStyles = [] ) { + if ( ! className ) { + return null; + } + + const matches = className.split( /\s+/ ).reduce( ( acc, name ) => { + if ( name.startsWith( VARIATION_PREFIX ) ) { + const match = name.slice( VARIATION_PREFIX.length ); + if ( match !== 'default' ) { + acc.push( match ); + } + } + return acc; + }, [] ); + + for ( const variation of matches ) { + if ( registeredStyles.some( ( style ) => style.name === variation ) ) { + return variation; + } + } + return null; +} + const layoutBlockSupportKey = 'layout'; const { kebabCase } = unlock( componentsPrivateApis ); @@ -452,15 +489,35 @@ export const withLayoutStyles = createHigherOrderComponent( ); // Get default blockGap value from global styles for use in layouts like grid. - // Check block-specific styles first, then fall back to root styles. + // Check style variation first, then block-specific styles, then fall back to root styles. const globalStyles = settings[ globalStylesDataKey ]; + + // Check if the block has an active style variation with a blockGap value. + // Only check the registry if the className contains a variation class to avoid unnecessary lookups. + let variationBlockGapValue; + const className = attributes?.className; + if ( className?.includes( VARIATION_PREFIX ) ) { + const { getBlockStyles } = select( blocksStore ); + const registeredStyles = getBlockStyles( name ); + const variationName = getVariationNameFromClass( + className, + registeredStyles + ); + variationBlockGapValue = variationName + ? globalStyles?.blocks?.[ name ]?.variations?.[ + variationName + ]?.spacing?.blockGap + : undefined; + } + const globalBlockGapValue = + variationBlockGapValue ?? globalStyles?.blocks?.[ name ]?.spacing?.blockGap ?? globalStyles?.spacing?.blockGap; return { blockGapSupport, globalBlockGapValue }; }, - [ blockSupportsLayout, clientId ] + [ blockSupportsLayout, clientId, attributes?.className, name ] ); if ( ! extraProps ) { From 93b266185c0d65aa60dd6d6490722624134b5cfe Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Wed, 11 Feb 2026 16:03:27 +1100 Subject: [PATCH 2/5] improvements --- lib/block-supports/layout.php | 22 +++++-------- .../src/hooks/block-style-variation.js | 2 +- packages/block-editor/src/hooks/layout.js | 32 +------------------ 3 files changed, 10 insertions(+), 46 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index a2b248cc0f15eb..e619b7b08c67c9 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -18,21 +18,15 @@ function gutenberg_get_variation_name_from_class( $class_name, $registered_style return null; } - $matches = array(); - $classes = explode( ' ', $class_name ); - - foreach ( $classes as $name ) { - if ( str_starts_with( $name, 'is-style-' ) ) { - $match = substr( $name, strlen( 'is-style-' ) ); - if ( 'default' !== $match ) { - $matches[] = $match; - } - } - } + $registered_names = array_filter( array_column( $registered_styles, 'name' ) ); + + $prefix = 'is-style-'; + $len = strlen( $prefix ); - foreach ( $matches as $variation ) { - foreach ( $registered_styles as $style ) { - if ( isset( $style['name'] ) && $style['name'] === $variation ) { + foreach ( explode( ' ', $class_name ) as $class ) { + if ( str_starts_with( $class, $prefix ) ) { + $variation = substr( $class, $len ); + if ( 'default' !== $variation && in_array( $variation, $registered_names, true ) ) { return $variation; } } diff --git a/packages/block-editor/src/hooks/block-style-variation.js b/packages/block-editor/src/hooks/block-style-variation.js index 054241746a737c..273bbe4c841b4b 100644 --- a/packages/block-editor/src/hooks/block-style-variation.js +++ b/packages/block-editor/src/hooks/block-style-variation.js @@ -40,7 +40,7 @@ function getVariationMatches( className ) { * * @return {string|null} The name of the first registered variation. */ -function getVariationNameFromClass( className, registeredStyles = [] ) { +export function getVariationNameFromClass( className, registeredStyles = [] ) { // The global flag affects how capturing groups work in JS. So the regex // below will only return full CSS classes not just the variation name. const matches = getVariationMatches( className ); diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 462fb9dbc3a6f3..5610be04464661 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -35,40 +35,10 @@ import { LAYOUT_DEFINITIONS } from '../layouts/definitions'; import { useBlockSettings, useStyleOverride } from './utils'; import { unlock } from '../lock-unlock'; import { globalStylesDataKey } from '../store/private-keys'; +import { getVariationNameFromClass } from './block-style-variation'; const VARIATION_PREFIX = 'is-style-'; -/** - * Get the first style variation name from a className string that matches a registered style. - * - * @param {string} className CSS class string for a block. - * @param {Array} registeredStyles Currently registered block styles. - * - * @return {string|null} The name of the first registered variation, or null if none found. - */ -function getVariationNameFromClass( className, registeredStyles = [] ) { - if ( ! className ) { - return null; - } - - const matches = className.split( /\s+/ ).reduce( ( acc, name ) => { - if ( name.startsWith( VARIATION_PREFIX ) ) { - const match = name.slice( VARIATION_PREFIX.length ); - if ( match !== 'default' ) { - acc.push( match ); - } - } - return acc; - }, [] ); - - for ( const variation of matches ) { - if ( registeredStyles.some( ( style ) => style.name === variation ) ) { - return variation; - } - } - return null; -} - const layoutBlockSupportKey = 'layout'; const { kebabCase } = unlock( componentsPrivateApis ); From cdb7b3afc773d764f717ba33e71226d51ebe9272 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 12 Feb 2026 09:41:36 +1100 Subject: [PATCH 3/5] Format spaces Co-authored-by: Mukesh Panchal --- lib/block-supports/layout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index e619b7b08c67c9..bef7ccc50c2b11 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -8,7 +8,7 @@ /** * Get the first style variation name from a className string that matches a registered style. * - * @param string $class_name CSS class string for a block. + * @param string $class_name CSS class string for a block. * @param array $registered_styles Currently registered block styles. * * @return string|null The name of the first registered variation, or null if none found. From 2d1d77b074b8a92dcf06386bf02092c6e794a92a Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 12 Feb 2026 11:23:27 +1100 Subject: [PATCH 4/5] Add unit test --- phpunit/block-supports/layout-test.php | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/phpunit/block-supports/layout-test.php b/phpunit/block-supports/layout-test.php index 861fa23dc6c51d..7af36ef59410fd 100644 --- a/phpunit/block-supports/layout-test.php +++ b/phpunit/block-supports/layout-test.php @@ -37,6 +37,22 @@ public function set_up() { // Clear caches. wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); + + /* + * Register a style variation with a custom blockGap value for testing. + */ + register_block_style( + 'core/group', + array( + 'name' => 'custom-gap', + 'label' => 'Custom Gap', + 'style_data' => array( + 'spacing' => array( + 'blockGap' => '99px', + ), + ), + ) + ); } public function tear_down() { @@ -44,6 +60,11 @@ public function tear_down() { wp_clean_themes_cache(); unset( $GLOBALS['wp_themes'] ); WP_Style_Engine_CSS_Rules_Store_Gutenberg::remove_all_stores(); + + // Clean up variation test data. + unregister_block_style( 'core/group', 'custom-gap' ); + WP_Theme_JSON_Resolver::clean_cached_data(); + parent::tear_down(); } @@ -777,4 +798,48 @@ public function data_layout_support_flag_renders_consistent_container_hash() { ), ); } + + /** + * Tests that block style variations with blockGap values are applied to layout styles. + * + * @covers ::wp_render_layout_support_flag + */ + public function test_layout_support_flag_uses_variation_block_gap_value() { + switch_theme( 'block-theme' ); + add_theme_support( 'appearance-tools' ); + + $block_content = '
'; + $block = array( + 'blockName' => 'core/group', + 'attrs' => array( + 'className' => 'is-style-custom-gap', + 'layout' => array( + 'type' => 'grid', + 'columnCount' => 3, + 'minimumColumnWidth' => '12rem', + + ), + ), + 'innerBlocks' => array(), + 'innerHTML' => '
', + 'innerContent' => array( + '
', + ), + ); + + gutenberg_render_layout_support_flag( $block_content, $block ); + + // Get the generated CSS from the style engine. + $actual_stylesheet = gutenberg_style_engine_get_stylesheet_from_context( 'block-supports', array( 'prettify' => false ) ); + + // The CSS grid declaration should contain the variation's blockGap value of 99px. + $this->assertStringContainsString( + 'grid-template-columns:repeat(auto-fill, minmax(max(min(12rem, 100%), (100% - (99px * (3 - 1))) /3), 1fr))', + $actual_stylesheet, + 'Generated CSS should contain the variation blockGap value of 99px.' + ); + + // Clean up. + remove_theme_support( 'appearance-tools' ); + } } From 728371578c1daf5fb714ebab5e1310d375d8ad8a Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 12 Feb 2026 11:29:53 +1100 Subject: [PATCH 5/5] changelog --- backport-changelog/7.0/10906.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 backport-changelog/7.0/10906.md diff --git a/backport-changelog/7.0/10906.md b/backport-changelog/7.0/10906.md new file mode 100644 index 00000000000000..77b475a7a7d421 --- /dev/null +++ b/backport-changelog/7.0/10906.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/10906 + +* https://github.com/WordPress/gutenberg/pull/75360 \ No newline at end of file