From 79c25b9bd2a9239ada35ed511bd465c171a78b46 Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Fri, 29 Aug 2025 20:01:46 +0200 Subject: [PATCH 01/18] initial try to get pseudo elements to work on blocks --- lib/class-wp-theme-json-gutenberg.php | 60 ++++++++ lib/global-styles-and-settings.php | 4 +- phpunit/class-wp-theme-json-test.php | 209 +++++++++++++++++++++----- schemas/json/theme.json | 59 +++++++- 4 files changed, 288 insertions(+), 44 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index ffc3f272369938..bc985a03b68239 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -590,6 +590,16 @@ class WP_Theme_JSON_Gutenberg { 'button' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':focus-visible', ':active' ), ); + /** + * The valid pseudo-selectors that can be used for blocks. + * + * @since 6.1.0 + * @var array + */ + const VALID_BLOCK_PSEUDO_SELECTORS = array( + 'core/button' => array( ':hover', ':focus', ':focus-visible', ':active' ), + ); + /** * The valid elements that can be found under styles. * @@ -1001,6 +1011,13 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_settings_blocks[ $block ] = static::VALID_SETTINGS; $schema_styles_blocks[ $block ] = $styles_non_top_level; $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; + + // Add pseudo-selectors for blocks that support them + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { + foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { + $schema_styles_blocks[ $block ][ $pseudo_selector ] = $styles_non_top_level; + } + } } $block_style_variation_styles = static::VALID_STYLES; @@ -1348,6 +1365,7 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' $origins = static::VALID_ORIGINS; } + if ( is_string( $types ) ) { // Dispatch error and map old arguments to new ones. _deprecated_argument( __FUNCTION__, '5.9.0' ); @@ -2815,6 +2833,23 @@ private static function get_block_nodes( $theme_json, $selectors = array(), $opt 'variations' => $variation_selectors, 'css' => $selector, ); + + // Handle any pseudo selectors for the block. + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] ) ) { + foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] as $pseudo_selector ) { + if ( isset( $theme_json['styles']['blocks'][ $name ][ $pseudo_selector ] ) ) { + $nodes[] = array( + 'name' => $name, + 'path' => array( 'styles', 'blocks', $name, $pseudo_selector ), + 'selector' => static::append_to_selector( $selector, $pseudo_selector ), + 'selectors' => $feature_selectors, + 'duotone' => $duotone_selector, + 'variations' => $variation_selectors, + 'css' => static::append_to_selector( $selector, $pseudo_selector ), + ); + } + } + } } if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { @@ -2936,6 +2971,23 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { $element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ]; } + /* + * Check if we're processing a block pseudo-selector. + * $block_metadata['path'] = array( 'styles', 'blocks', 'core/button', ':hover' ); + */ + $is_processing_block_pseudo = false; + $block_pseudo_selector = null; + if ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 4 ) { + $block_name = $block_metadata['path'][2]; // 'core/button' + $last_path_element = $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ]; // ':hover' + + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) && + in_array( $last_path_element, static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ], true ) ) { + $is_processing_block_pseudo = true; + $block_pseudo_selector = $last_path_element; + } + } + /* * Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover"). * This also resets the array keys. @@ -2965,6 +3017,14 @@ static function ( $pseudo_selector ) use ( $selector ) { && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); + } elseif ( $is_processing_block_pseudo ) { + // Process block pseudo-selector styles + // For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector + $block_name = $block_metadata['path'][2]; // 'core/button' + $block_data = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() ); + $pseudo_data = isset( $block_data[ $block_pseudo_selector ] ) ? $block_data[ $block_pseudo_selector ] : array(); + + $declarations = static::compute_style_properties( $pseudo_data, $settings, null, $this->theme_json, $selector, $use_root_padding ); } else { $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); } diff --git a/lib/global-styles-and-settings.php b/lib/global-styles-and-settings.php index 3ff5e6cb135e18..8111e8a5449311 100644 --- a/lib/global-styles-and-settings.php +++ b/lib/global-styles-and-settings.php @@ -287,8 +287,8 @@ function gutenberg_add_global_styles_for_blocks() { foreach ( $block_nodes as $metadata ) { if ( $can_use_cached ) { - // Use the block name as the key for cached CSS data. Otherwise, use a hash of the metadata. - $cache_node_key = isset( $metadata['name'] ) ? $metadata['name'] : md5( wp_json_encode( $metadata ) ); + // Generate a unique cache key based on the full metadata to ensure pseudo-selectors and other variations get unique keys. + $cache_node_key = md5( wp_json_encode( $metadata ) ); if ( isset( $cached['blocks'][ $cache_node_key ] ) ) { $block_css = $cached['blocks'][ $cache_node_key ]; diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index a6043cb907704a..34edc345a55d91 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -6123,21 +6123,81 @@ public function test_merge_incoming_data_duotone_presets_with_block_level_defaul ), ), ), - 'default' ); - $theme = new WP_Theme_JSON_Gutenberg( + } + + /** + * Test that block pseudo selectors are processed correctly. + */ + public function test_block_pseudo_selectors_are_processed() { + $theme_json = new WP_Theme_JSON_Gutenberg( array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'settings' => array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( 'blocks' => array( - 'core/image' => array( + 'core/button' => array( 'color' => array( - 'defaultDuotone' => false, - 'duotone' => array( - array( - 'slug' => 'dark-grayscale', - 'colors' => array( '#000000', '#7f7f7f' ), - 'name' => 'Theme Dark grayscale', + 'text' => 'white', + 'background' => 'blue', + ), + ':hover' => array( + 'color' => array( + 'text' => 'blue', + 'background' => 'white', + ), + ), + ':focus' => array( + 'color' => array( + 'text' => 'red', + 'background' => 'yellow', + ), + ), + ), + ), + ), + ), + ); + + $expected = ':root :where(.wp-block-button .wp-block-button__link){background-color: blue;color: white;}:root :where(.wp-block-button .wp-block-button__link:hover){background-color: white;color: blue;}:root :where(.wp-block-button .wp-block-button__link:focus){background-color: yellow;color: red;}'; + $this->assertSameCSS( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) ); + } + + /** + * Test that block pseudo selectors are processed correctly within variations. + */ + public function test_block_variation_pseudo_selectors_are_processed() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + 'color' => array( + 'text' => 'white', + 'background' => 'blue', + ), + 'variations' => array( + 'outline' => array( + 'color' => array( + 'text' => 'currentColor', + 'background' => 'transparent', + ), + 'border' => array( + 'color' => 'currentColor', + 'width' => '1px', + 'style' => 'solid', + ), + ':hover' => array( + 'color' => array( + 'text' => 'white', + 'background' => 'red', + ), + ), + ':focus' => array( + 'color' => array( + 'text' => 'black', + 'background' => 'yellow', + ), ), ), ), @@ -6147,49 +6207,116 @@ public function test_merge_incoming_data_duotone_presets_with_block_level_defaul ) ); - $expected = array( - 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, - 'settings' => array( - 'color' => array( - 'defaultDuotone' => true, - 'duotone' => array( - 'default' => array( - array( - 'slug' => 'dark-grayscale', - 'colors' => array( '#000000', '#7f7f7f' ), - 'name' => 'Default Dark grayscale', + $expected = ':root :where(.wp-block-button .wp-block-button__link){background-color: blue;color: white;}:root :where(.wp-block-button.is-style-outline .wp-block-button__link){background-color: transparent;border-color: currentColor;border-width: 1px;border-style: solid;color: currentColor;}:root :where(.wp-block-button.is-style-outline .wp-block-button__link:hover){background-color: red;color: white;}:root :where(.wp-block-button.is-style-outline .wp-block-button__link:focus){background-color: yellow;color: black;}'; + $this->assertSameCSS( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) ); + } + + /** + * Test that non-whitelisted pseudo selectors are ignored for blocks. + */ + public function test_block_pseudo_selectors_ignores_non_whitelisted() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + 'color' => array( + 'text' => 'white', + 'background' => 'blue', ), - array( - 'slug' => 'blue-orange', - 'colors' => array( '#0000ff', '#ff8800' ), - 'name' => 'Blue Orange', + ':hover' => array( + 'color' => array( + 'text' => 'blue', + 'background' => 'white', + ), + ), + ':levitate' => array( + 'color' => array( + 'text' => 'yellow', + 'background' => 'black', + ), ), ), ), ), - 'blocks' => array( - 'core/image' => array( - 'color' => array( - 'defaultDuotone' => false, - 'duotone' => array( - 'theme' => array( - array( - 'slug' => 'dark-grayscale', - 'colors' => array( '#000000', '#7f7f7f' ), - 'name' => 'Theme Dark grayscale', - ), + ) + ); + + $expected = ':root :where(.wp-block-button .wp-block-button__link){background-color: blue;color: white;}:root :where(.wp-block-button .wp-block-button__link:hover){background-color: white;color: blue;}'; + $this->assertSameCSS( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) ); + $this->assertStringNotContainsString( '.wp-block-button .wp-block-button__link:levitate{', $theme_json->get_stylesheet( array( 'styles' ) ) ); + } + + /** + * Test that blocks without pseudo selector support ignore pseudo selectors. + */ + public function test_blocks_without_pseudo_support_ignore_pseudo_selectors() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/paragraph' => array( + 'color' => array( + 'text' => 'black', + ), + ':hover' => array( + 'color' => array( + 'text' => 'red', ), ), ), ), ), - ), + ) ); - $defaults->merge( $theme ); - $actual = $defaults->get_raw_data(); + $expected = ':root :where(p){color: black;}'; + $this->assertSameCSS( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) ); + $this->assertStringNotContainsString( 'p:hover{', $theme_json->get_stylesheet( array( 'styles' ) ) ); + } - $this->assertEqualSetsWithIndex( $expected, $actual ); + /** + * Test that block pseudo selectors work with elements within blocks. + */ + public function test_block_pseudo_selectors_with_elements() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + 'color' => array( + 'text' => 'white', + 'background' => 'blue', + ), + ':hover' => array( + 'color' => array( + 'text' => 'blue', + 'background' => 'white', + ), + ), + 'elements' => array( + 'button' => array( + 'color' => array( + 'text' => 'green', + ), + ':hover' => array( + 'color' => array( + 'text' => 'orange', + ), + ), + ), + ), + ), + ), + ), + ) + ); + + $expected = ':root :where(.wp-block-button .wp-block-button__link){background-color: blue;color: white;}:root :where(.wp-block-button .wp-block-button__link:hover){background-color: white;color: blue;}:root :where(.wp-block-button .wp-block-button__link .wp-element-button,.wp-block-button .wp-block-button__link .wp-block-button__link){color: green;}:root :where(.wp-block-button .wp-block-button__link .wp-element-button:hover,.wp-block-button .wp-block-button__link .wp-block-button__link:hover){color: orange;}'; + $this->assertSameCSS( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) ); } public function test_merge_incoming_data_block_level_inherits_global_default_setting() { diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 02064dacd9bca9..2af6aeaecf4d0b 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -1779,6 +1779,23 @@ } } }, + "stylesBlocksPseudoSelectorsProperties": { + "type": "object", + "properties": { + ":hover": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":focus": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":focus-visible": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":active": { + "$ref": "#/definitions/stylesPropertiesComplete" + } + } + }, "stylesElementsPseudoSelectorsPropertyNames": { "enum": [ ":active", @@ -1790,6 +1807,9 @@ ":visited" ] }, + "stylesBlocksPseudoSelectorsPropertyNames": { + "enum": [ ":hover", ":focus", ":focus-visible", ":active" ] + }, "stylesElementsPropertiesComplete": { "description": "Styles defined on a per-element basis using the element's selector.", "type": "object", @@ -1905,7 +1925,44 @@ "$ref": "#/definitions/stylesPropertiesAndElementsComplete" }, "core/button": { - "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + }, + { + "$ref": "#/definitions/stylesBlocksPseudoSelectorsProperties" + }, + { + "type": "object", + "properties": { + "elements": { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + }, + "variations": { + "$ref": "#/definitions/stylesVariationsPropertiesComplete" + } + }, + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "$ref": "#/definitions/stylesElementsPseudoSelectorsPropertyNames" + }, + { + "$ref": "#/definitions/stylesBlocksPseudoSelectorsPropertyNames" + }, + { + "enum": [ "elements", "variations" ] + } + ] + } + } + ] }, "core/buttons": { "$ref": "#/definitions/stylesPropertiesAndElementsComplete" From 3ce94d4c799cba696d13e3d4f0223efc421bb2a7 Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Mon, 1 Sep 2025 11:29:23 +0200 Subject: [PATCH 02/18] run php format --- lib/class-wp-theme-json-gutenberg.php | 21 ++++++++++----------- phpunit/class-wp-theme-json-test.php | 14 +++++++------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index bc985a03b68239..3dcca4ab5fb500 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -1011,7 +1011,7 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_settings_blocks[ $block ] = static::VALID_SETTINGS; $schema_styles_blocks[ $block ] = $styles_non_top_level; $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; - + // Add pseudo-selectors for blocks that support them if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { @@ -1365,7 +1365,6 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' $origins = static::VALID_ORIGINS; } - if ( is_string( $types ) ) { // Dispatch error and map old arguments to new ones. _deprecated_argument( __FUNCTION__, '5.9.0' ); @@ -2976,15 +2975,15 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { * $block_metadata['path'] = array( 'styles', 'blocks', 'core/button', ':hover' ); */ $is_processing_block_pseudo = false; - $block_pseudo_selector = null; + $block_pseudo_selector = null; if ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 4 ) { - $block_name = $block_metadata['path'][2]; // 'core/button' + $block_name = $block_metadata['path'][2]; // 'core/button' $last_path_element = $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ]; // ':hover' - - if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) && - in_array( $last_path_element, static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ], true ) ) { + + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) && + in_array( $last_path_element, static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ], true ) ) { $is_processing_block_pseudo = true; - $block_pseudo_selector = $last_path_element; + $block_pseudo_selector = $last_path_element; } } @@ -3020,10 +3019,10 @@ static function ( $pseudo_selector ) use ( $selector ) { } elseif ( $is_processing_block_pseudo ) { // Process block pseudo-selector styles // For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector - $block_name = $block_metadata['path'][2]; // 'core/button' - $block_data = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() ); + $block_name = $block_metadata['path'][2]; // 'core/button' + $block_data = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() ); $pseudo_data = isset( $block_data[ $block_pseudo_selector ] ) ? $block_data[ $block_pseudo_selector ] : array(); - + $declarations = static::compute_style_properties( $pseudo_data, $settings, null, $this->theme_json, $selector, $use_root_padding ); } else { $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 34edc345a55d91..23d387ad06cf80 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -6136,7 +6136,7 @@ public function test_block_pseudo_selectors_are_processed() { 'styles' => array( 'blocks' => array( 'core/button' => array( - 'color' => array( + 'color' => array( 'text' => 'white', 'background' => 'blue', ), @@ -6221,11 +6221,11 @@ public function test_block_pseudo_selectors_ignores_non_whitelisted() { 'styles' => array( 'blocks' => array( 'core/button' => array( - 'color' => array( + 'color' => array( 'text' => 'white', 'background' => 'blue', ), - ':hover' => array( + ':hover' => array( 'color' => array( 'text' => 'blue', 'background' => 'white', @@ -6258,7 +6258,7 @@ public function test_blocks_without_pseudo_support_ignore_pseudo_selectors() { 'styles' => array( 'blocks' => array( 'core/paragraph' => array( - 'color' => array( + 'color' => array( 'text' => 'black', ), ':hover' => array( @@ -6287,11 +6287,11 @@ public function test_block_pseudo_selectors_with_elements() { 'styles' => array( 'blocks' => array( 'core/button' => array( - 'color' => array( + 'color' => array( 'text' => 'white', 'background' => 'blue', ), - ':hover' => array( + ':hover' => array( 'color' => array( 'text' => 'blue', 'background' => 'white', @@ -6299,7 +6299,7 @@ public function test_block_pseudo_selectors_with_elements() { ), 'elements' => array( 'button' => array( - 'color' => array( + 'color' => array( 'text' => 'green', ), ':hover' => array( From 3716d13f8971e976e05b7a013f6ef713af125e44 Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Mon, 1 Sep 2025 13:39:19 +0200 Subject: [PATCH 03/18] refactor theme.json schema --- schemas/json/theme.json | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 2af6aeaecf4d0b..4554d6882d405d 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -1927,40 +1927,10 @@ "core/button": { "allOf": [ { - "$ref": "#/definitions/stylesProperties" - }, - { - "$ref": "#/definitions/stylesElementsPropertiesComplete" + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" }, { "$ref": "#/definitions/stylesBlocksPseudoSelectorsProperties" - }, - { - "type": "object", - "properties": { - "elements": { - "$ref": "#/definitions/stylesElementsPropertiesComplete" - }, - "variations": { - "$ref": "#/definitions/stylesVariationsPropertiesComplete" - } - }, - "propertyNames": { - "anyOf": [ - { - "$ref": "#/definitions/stylesPropertyNames" - }, - { - "$ref": "#/definitions/stylesElementsPseudoSelectorsPropertyNames" - }, - { - "$ref": "#/definitions/stylesBlocksPseudoSelectorsPropertyNames" - }, - { - "enum": [ "elements", "variations" ] - } - ] - } } ] }, From e2797584dfa8b7d93a079e1d3c8d1d38bc76c1ad Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Mon, 1 Sep 2025 13:41:00 +0200 Subject: [PATCH 04/18] remove unused definition --- schemas/json/theme.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 4554d6882d405d..34f67f1ac00ee3 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -1807,9 +1807,6 @@ ":visited" ] }, - "stylesBlocksPseudoSelectorsPropertyNames": { - "enum": [ ":hover", ":focus", ":focus-visible", ":active" ] - }, "stylesElementsPropertiesComplete": { "description": "Styles defined on a per-element basis using the element's selector.", "type": "object", From 4a6e64e0518fcd0646aa8c11b82a6d1d4b0c432c Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Tue, 2 Sep 2025 10:53:49 +0200 Subject: [PATCH 05/18] add support for pseudo selectors inside variations --- lib/class-wp-theme-json-gutenberg.php | 59 ++++++++++++++++++++++++++- phpunit/class-wp-theme-json-test.php | 4 +- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 3dcca4ab5fb500..890206923d013d 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -690,6 +690,40 @@ protected static function schema_in_root_and_per_origin( $schema ) { return $schema_in_root_and_per_origin; } + /** + * Processes pseudo-selectors for any node (block or variation). + * + * @param array $node The node data (block or variation). + * @param string $base_selector The base selector. + * @param array $settings The theme settings. + * @param string $block_name The block name. + * @return array Array of pseudo-selector declarations. + */ + private static function process_pseudo_selectors( $node, $base_selector, $settings, $block_name ) { + $pseudo_declarations = array(); + + // Check if this block supports pseudo-selectors + if ( ! isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) { + return $pseudo_declarations; + } + + // Process each valid pseudo-selector for this block + foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) { + if ( isset( $node[ $pseudo_selector ] ) ) { + // Create the combined selector (base + pseudo-selector) + $combined_selector = static::append_to_selector( $base_selector, $pseudo_selector ); + + // Compute the style properties for this pseudo-selector + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, null ); + + // Add to the declarations array + $pseudo_declarations[ $combined_selector ] = $declarations; + } + } + + return $pseudo_declarations; + } + /** * Returns a class name by an element name. * @@ -1040,7 +1074,18 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_styles_variations = array(); if ( ! empty( $style_variation_names ) ) { - $schema_styles_variations = array_fill_keys( $style_variation_names, $block_style_variation_styles ); + foreach ( $style_variation_names as $variation_name ) { + $variation_schema = $block_style_variation_styles; + + // Add pseudo-selectors to variations for blocks that support them + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { + foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { + $variation_schema[ $pseudo_selector ] = $styles_non_top_level; + } + } + + $schema_styles_variations[ $variation_name ] = $variation_schema; + } } $schema_styles_blocks[ $block ]['variations'] = $schema_styles_variations; @@ -2680,7 +2725,11 @@ protected static function get_style_nodes( $theme_json, $selectors = array(), $o return $nodes; } - $block_nodes = static::get_block_nodes( $theme_json, $selectors, $options ); + $block_options = $options; + if ( ! isset( $block_options['include_block_style_variations'] ) ) { + $block_options['include_block_style_variations'] = true; + } + $block_nodes = static::get_block_nodes( $theme_json, $selectors, $block_options ); foreach ( $block_nodes as $block_node ) { $nodes[] = $block_node; } @@ -2947,6 +2996,12 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { } // Compute declarations for remaining styles not covered by feature level selectors. $style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); + + // Process pseudo-selectors for this variation (e.g., :hover, :focus) + $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? $block_metadata['path'][2] : null ); + $variation_pseudo_declarations = static::process_pseudo_selectors( $style_variation_node, $style_variation['selector'], $settings, $block_name ); + $style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations ); + // Store custom CSS for the style variation. if ( isset( $style_variation_node['css'] ) ) { $style_variation_custom_css[ $style_variation['selector'] ] = $this->process_blocks_custom_css( $style_variation_node['css'], $style_variation['selector'] ); diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 23d387ad06cf80..8fe3729ef8be45 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -6172,13 +6172,13 @@ public function test_block_variation_pseudo_selectors_are_processed() { 'styles' => array( 'blocks' => array( 'core/button' => array( - 'color' => array( + 'color' => array( 'text' => 'white', 'background' => 'blue', ), 'variations' => array( 'outline' => array( - 'color' => array( + 'color' => array( 'text' => 'currentColor', 'background' => 'transparent', ), From 82ed4a3c3f7d74946870e249347e632e302fe2fb Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Tue, 2 Sep 2025 10:59:43 +0200 Subject: [PATCH 06/18] remove redundant comments --- lib/class-wp-theme-json-gutenberg.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 890206923d013d..41bec749377e08 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -702,21 +702,14 @@ protected static function schema_in_root_and_per_origin( $schema ) { private static function process_pseudo_selectors( $node, $base_selector, $settings, $block_name ) { $pseudo_declarations = array(); - // Check if this block supports pseudo-selectors if ( ! isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) { return $pseudo_declarations; } - // Process each valid pseudo-selector for this block foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) { if ( isset( $node[ $pseudo_selector ] ) ) { - // Create the combined selector (base + pseudo-selector) $combined_selector = static::append_to_selector( $base_selector, $pseudo_selector ); - - // Compute the style properties for this pseudo-selector $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, null ); - - // Add to the declarations array $pseudo_declarations[ $combined_selector ] = $declarations; } } From a8639486f2dec9978be3257d1f97eb77fb56980c Mon Sep 17 00:00:00 2001 From: MaggieCabrera Date: Tue, 2 Sep 2025 11:20:19 +0200 Subject: [PATCH 07/18] fix php errors --- lib/class-wp-theme-json-gutenberg.php | 22 +++++++++++----------- phpunit/class-wp-theme-json-test.php | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 41bec749377e08..0b3d6e5f280dce 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -701,19 +701,19 @@ protected static function schema_in_root_and_per_origin( $schema ) { */ private static function process_pseudo_selectors( $node, $base_selector, $settings, $block_name ) { $pseudo_declarations = array(); - + if ( ! isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) { return $pseudo_declarations; } - + foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) { if ( isset( $node[ $pseudo_selector ] ) ) { - $combined_selector = static::append_to_selector( $base_selector, $pseudo_selector ); - $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, null ); + $combined_selector = static::append_to_selector( $base_selector, $pseudo_selector ); + $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, null ); $pseudo_declarations[ $combined_selector ] = $declarations; } } - + return $pseudo_declarations; } @@ -1069,14 +1069,14 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n if ( ! empty( $style_variation_names ) ) { foreach ( $style_variation_names as $variation_name ) { $variation_schema = $block_style_variation_styles; - + // Add pseudo-selectors to variations for blocks that support them if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { $variation_schema[ $pseudo_selector ] = $styles_non_top_level; } } - + $schema_styles_variations[ $variation_name ] = $variation_schema; } } @@ -2989,12 +2989,12 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { } // Compute declarations for remaining styles not covered by feature level selectors. $style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); - + // Process pseudo-selectors for this variation (e.g., :hover, :focus) - $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? $block_metadata['path'][2] : null ); + $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? $block_metadata['path'][2] : null ); $variation_pseudo_declarations = static::process_pseudo_selectors( $style_variation_node, $style_variation['selector'], $settings, $block_name ); - $style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations ); - + $style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations ); + // Store custom CSS for the style variation. if ( isset( $style_variation_node['css'] ) ) { $style_variation_custom_css[ $style_variation['selector'] ] = $this->process_blocks_custom_css( $style_variation_node['css'], $style_variation['selector'] ); diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 8fe3729ef8be45..23d387ad06cf80 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -6172,13 +6172,13 @@ public function test_block_variation_pseudo_selectors_are_processed() { 'styles' => array( 'blocks' => array( 'core/button' => array( - 'color' => array( + 'color' => array( 'text' => 'white', 'background' => 'blue', ), 'variations' => array( 'outline' => array( - 'color' => array( + 'color' => array( 'text' => 'currentColor', 'background' => 'transparent', ), From 37cb2fc530934b1f1d7299ff080d2234e5f80a6c Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Mon, 17 Nov 2025 09:25:09 +0000 Subject: [PATCH 08/18] fix duotone test --- phpunit/class-wp-theme-json-test.php | 66 ++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 23d387ad06cf80..b5636618799205 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -6123,7 +6123,73 @@ public function test_merge_incoming_data_duotone_presets_with_block_level_defaul ), ), ), + 'default' + ); + $theme = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'blocks' => array( + 'core/image' => array( + 'color' => array( + 'defaultDuotone' => false, + 'duotone' => array( + array( + 'slug' => 'dark-grayscale', + 'colors' => array( '#000000', '#7f7f7f' ), + 'name' => 'Theme Dark grayscale', + ), + ), + ), + ), + ), + ), + ) + ); + + $expected = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'settings' => array( + 'color' => array( + 'defaultDuotone' => true, + 'duotone' => array( + 'default' => array( + array( + 'slug' => 'dark-grayscale', + 'colors' => array( '#000000', '#7f7f7f' ), + 'name' => 'Default Dark grayscale', + ), + array( + 'slug' => 'blue-orange', + 'colors' => array( '#0000ff', '#ff8800' ), + 'name' => 'Blue Orange', + ), + ), + ), + ), + 'blocks' => array( + 'core/image' => array( + 'color' => array( + 'defaultDuotone' => false, + 'duotone' => array( + 'theme' => array( + array( + 'slug' => 'dark-grayscale', + 'colors' => array( '#000000', '#7f7f7f' ), + 'name' => 'Theme Dark grayscale', + ), + ), + ), + ), + ), + ), + ), ); + + $defaults->merge( $theme ); + $actual = $defaults->get_raw_data(); + + $this->assertEqualSetsWithIndex( $expected, $actual ); } /** From a1cc83fbf798cdec54e7b4f699a00529b49ecccf Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Mon, 17 Nov 2025 09:49:50 +0000 Subject: [PATCH 09/18] fix PHP linter --- phpunit/class-wp-theme-json-test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index b5636618799205..6dc41194626b3b 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -6221,7 +6221,7 @@ public function test_block_pseudo_selectors_are_processed() { ), ), ), - ), + ) ); $expected = ':root :where(.wp-block-button .wp-block-button__link){background-color: blue;color: white;}:root :where(.wp-block-button .wp-block-button__link:hover){background-color: white;color: blue;}:root :where(.wp-block-button .wp-block-button__link:focus){background-color: yellow;color: red;}'; From a76d7d864d382cbc1d8cf2bb63be1c30f6c1ea8d Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Mon, 17 Nov 2025 12:29:34 +0000 Subject: [PATCH 10/18] add backport --- backport-changelog/7.0/10523.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 backport-changelog/7.0/10523.md diff --git a/backport-changelog/7.0/10523.md b/backport-changelog/7.0/10523.md new file mode 100644 index 00000000000000..57922ca2510d43 --- /dev/null +++ b/backport-changelog/7.0/10523.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/10523 + +* https://github.com/WordPress/gutenberg/pull/71418 From f26e38b0348638ae8dcfe4f8f97a9dfe43eae733 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:21:19 +0000 Subject: [PATCH 11/18] Update lib/class-wp-theme-json-gutenberg.php Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- lib/class-wp-theme-json-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 0b3d6e5f280dce..bf20615df36d52 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -1039,7 +1039,7 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_styles_blocks[ $block ] = $styles_non_top_level; $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; - // Add pseudo-selectors for blocks that support them + // Add pseudo-selectors for blocks that support them. if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { $schema_styles_blocks[ $block ][ $pseudo_selector ] = $styles_non_top_level; From a7d75af2f64fa3c948e28ced371d9443fcf6b4a4 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:21:25 +0000 Subject: [PATCH 12/18] Update lib/class-wp-theme-json-gutenberg.php Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- lib/class-wp-theme-json-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index bf20615df36d52..04ed24e073a66d 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -2990,7 +2990,7 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { // Compute declarations for remaining styles not covered by feature level selectors. $style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); - // Process pseudo-selectors for this variation (e.g., :hover, :focus) + // Process pseudo-selectors for this variation (e.g., :hover, :focus). $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? $block_metadata['path'][2] : null ); $variation_pseudo_declarations = static::process_pseudo_selectors( $style_variation_node, $style_variation['selector'], $settings, $block_name ); $style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations ); From 6eb2dba28ba6b765e8a6265ab9834b685f33e012 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:21:31 +0000 Subject: [PATCH 13/18] Update lib/class-wp-theme-json-gutenberg.php Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- lib/class-wp-theme-json-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 04ed24e073a66d..85a27ac0665c02 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -593,7 +593,7 @@ class WP_Theme_JSON_Gutenberg { /** * The valid pseudo-selectors that can be used for blocks. * - * @since 6.1.0 + * @since 7.0.0 * @var array */ const VALID_BLOCK_PSEUDO_SELECTORS = array( From 5c834d4ec484314c134e92a1fdee6f8750621797 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:21:37 +0000 Subject: [PATCH 14/18] Update lib/class-wp-theme-json-gutenberg.php Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- lib/class-wp-theme-json-gutenberg.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 85a27ac0665c02..a2ae717dafdd66 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -3065,8 +3065,8 @@ static function ( $pseudo_selector ) use ( $selector ) { ) { $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); } elseif ( $is_processing_block_pseudo ) { - // Process block pseudo-selector styles - // For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector + // Process block pseudo-selector styles. + // For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector. $block_name = $block_metadata['path'][2]; // 'core/button' $block_data = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() ); $pseudo_data = isset( $block_data[ $block_pseudo_selector ] ) ? $block_data[ $block_pseudo_selector ] : array(); From bd87a59a77a56b36b1c425f054a5504d15b530a5 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:21:43 +0000 Subject: [PATCH 15/18] Update lib/class-wp-theme-json-gutenberg.php Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- lib/class-wp-theme-json-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index a2ae717dafdd66..f74436c69bb197 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -1070,7 +1070,7 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n foreach ( $style_variation_names as $variation_name ) { $variation_schema = $block_style_variation_styles; - // Add pseudo-selectors to variations for blocks that support them + // Add pseudo-selectors to variations for blocks that support them. if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { $variation_schema[ $pseudo_selector ] = $styles_non_top_level; From 005f04ecce11ecf225f21b28250d4b7186ae5bd3 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:32:00 +0000 Subject: [PATCH 16/18] create a helper function to extract the block name --- lib/class-wp-theme-json-gutenberg.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index f74436c69bb197..9309b4030cd23b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -2991,7 +2991,7 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { $style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); // Process pseudo-selectors for this variation (e.g., :hover, :focus). - $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? $block_metadata['path'][2] : null ); + $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? get_block_name_from_metadata_path( $block_metadata ) : null ); $variation_pseudo_declarations = static::process_pseudo_selectors( $style_variation_node, $style_variation['selector'], $settings, $block_name ); $style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations ); @@ -3025,7 +3025,7 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { $is_processing_block_pseudo = false; $block_pseudo_selector = null; if ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 4 ) { - $block_name = $block_metadata['path'][2]; // 'core/button' + $block_name = static::get_block_name_from_metadata_path( $block_metadata ); // 'core/button' $last_path_element = $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ]; // ':hover' if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) && @@ -3067,7 +3067,7 @@ static function ( $pseudo_selector ) use ( $selector ) { } elseif ( $is_processing_block_pseudo ) { // Process block pseudo-selector styles. // For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector. - $block_name = $block_metadata['path'][2]; // 'core/button' + $block_name = static::get_block_name_from_metadata_path( $block_metadata ); // 'core/button' $block_data = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() ); $pseudo_data = isset( $block_data[ $block_pseudo_selector ] ) ? $block_data[ $block_pseudo_selector ] : array(); @@ -4723,4 +4723,19 @@ protected static function get_valid_block_style_variations( $blocks_metadata = a return $valid_variations; } + + /** + * Extracts the block name from the block metadata path. + * + * @since 7.0 + * + * @param array $block_metadata Block metadata. + * @return string|null The block name or null if not found. + */ + private static function get_block_name_from_metadata_path( $block_metadata ) { + if ( isset( $block_metadata['path'] ) ) { + return $block_metadata['path'][2]; + } + } + } From 4604ff9da795faa16d67391f40cd24539e63663c Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 08:34:26 +0000 Subject: [PATCH 17/18] fix PHP linter --- lib/class-wp-theme-json-gutenberg.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 9309b4030cd23b..fd61891d02af91 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -4737,5 +4737,4 @@ private static function get_block_name_from_metadata_path( $block_metadata ) { return $block_metadata['path'][2]; } } - } From bd1437216af8a186ed6cb37c0aa96573952b20a3 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 20 Nov 2025 11:01:54 +0000 Subject: [PATCH 18/18] fix test --- lib/class-wp-theme-json-gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index fd61891d02af91..33049d42274bc9 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -2991,7 +2991,7 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) { $style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); // Process pseudo-selectors for this variation (e.g., :hover, :focus). - $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? get_block_name_from_metadata_path( $block_metadata ) : null ); + $block_name = isset( $block_metadata['name'] ) ? $block_metadata['name'] : ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ? static::get_block_name_from_metadata_path( $block_metadata ) : null ); $variation_pseudo_declarations = static::process_pseudo_selectors( $style_variation_node, $style_variation['selector'], $settings, $block_name ); $style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations );