From 733837f963d72c38b291ad3db75cce3b8beb81a7 Mon Sep 17 00:00:00 2001 From: Matthew Reishus Date: Mon, 31 Aug 2026 15:06:47 +0000 Subject: [PATCH 1/2] Theme JSON: Avoid rebuilding identical block schemas during sanitization --- src/wp-includes/class-wp-theme-json.php | 26 ++++--- tests/phpunit/tests/theme/wpThemeJson.php | 88 +++++++++++++++++++++++ 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index c20960bd0f9cc..f7e6621f1d7e5 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -1330,6 +1330,7 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_styles_blocks = array(); $schema_settings_blocks = array(); + $breakpoint_states = array_keys( $responsive_media_queries ); /* * Generate a schema for blocks. @@ -1340,21 +1341,30 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n * * As each variation needs both a `blocks` schema and responsive `blocks` schemas * for further nested inner `blocks`, the overall schema is generated in multiple passes. + * + * All blocks start with the same style schema. Build that common schema + * once, then add block-specific pseudo and custom states below. */ + $responsive_block_schema = $styles_non_top_level; + $responsive_block_schema['elements'] = $schema_styles_elements; + + $common_block_schema = $styles_non_top_level; + $common_block_schema['elements'] = $schema_styles_elements; + + foreach ( $breakpoint_states as $breakpoint_state ) { + $common_block_schema[ $breakpoint_state ] = $responsive_block_schema; + } + foreach ( $valid_block_names as $block ) { $schema_settings_blocks[ $block ] = static::VALID_SETTINGS; // `viewport` and `blockVisibility` are global-only settings and cannot be set per block for now. unset( $schema_settings_blocks[ $block ]['viewport'] ); unset( $schema_settings_blocks[ $block ]['blockVisibility'] ); - $schema_styles_blocks[ $block ] = $styles_non_top_level; - $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; - - // Add responsive breakpoint states for all blocks. - foreach ( array_keys( $responsive_media_queries ) as $breakpoint_state ) { - $schema_styles_blocks[ $block ][ $breakpoint_state ] = $styles_non_top_level; - $schema_styles_blocks[ $block ][ $breakpoint_state ]['elements'] = $schema_styles_elements; + $schema_styles_blocks[ $block ] = $common_block_schema; - if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { + // Add responsive pseudo-selectors only to blocks that support them. + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) { + foreach ( $breakpoint_states as $breakpoint_state ) { foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) { $schema_styles_blocks[ $block ][ $breakpoint_state ][ $pseudo_selector ] = $styles_non_top_level; } diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index c2cda7bb158d0..407489e6d26f2 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -8242,6 +8242,94 @@ public function test_blocks_without_pseudo_support_ignore_pseudo_selectors() { $this->assertStringNotContainsString( 'p:hover{', $theme_json->get_stylesheet( array( 'styles' ) ) ); } + /** + * Tests that responsive pseudo-states are kept only for supported blocks. + * + * @covers WP_Theme_JSON::sanitize + * + * @ticket 66003 + */ + public function test_sanitize_keeps_responsive_pseudo_states_on_supported_blocks_only() { + $theme_json = new WP_Theme_JSON( + array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + '@mobile' => array( + ':hover' => array( + 'color' => array( + 'text' => 'blue', + ), + ), + ), + ), + 'core/paragraph' => array( + 'color' => array( + 'text' => 'black', + ), + '@mobile' => array( + 'color' => array( + 'text' => 'green', + ), + ':hover' => array( + 'color' => array( + 'text' => 'red', + ), + ), + ), + ), + 'core/navigation-link' => array( + '-current' => array( + 'color' => array( + 'text' => 'purple', + ), + ), + ), + ), + ), + ) + ); + + $actual = $theme_json->get_raw_data(); + + $expected = array( + 'version' => WP_Theme_JSON::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + '@mobile' => array( + ':hover' => array( + 'color' => array( + 'text' => 'blue', + ), + ), + ), + ), + 'core/paragraph' => array( + 'color' => array( + 'text' => 'black', + ), + '@mobile' => array( + 'color' => array( + 'text' => 'green', + ), + ), + ), + 'core/navigation-link' => array( + '-current' => array( + 'color' => array( + 'text' => 'purple', + ), + ), + ), + ), + ), + ); + + $this->assertEqualSetsWithIndex( $expected, $actual ); + } + /** * Test that block pseudo selectors work with elements within blocks. */ From bb8f45d61ad3355b2a93d08b944be3bb5b1dc69e Mon Sep 17 00:00:00 2001 From: Matthew Reishus Date: Thu, 3 Sep 2026 14:14:56 +0000 Subject: [PATCH 2/2] Pull out $common_block_settings from loop --- src/wp-includes/class-wp-theme-json.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index f7e6621f1d7e5..8697ad5fc5a13 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -1332,6 +1332,13 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n $schema_settings_blocks = array(); $breakpoint_states = array_keys( $responsive_media_queries ); + $common_block_settings = static::VALID_SETTINGS; + // `viewport` and `blockVisibility` are global-only settings and cannot be set per block for now. + unset( + $common_block_settings['viewport'], + $common_block_settings['blockVisibility'] + ); + /* * Generate a schema for blocks. * - Block styles can contain `elements`, `variations`, and responsive breakpoint state definitions. @@ -1356,11 +1363,8 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n } foreach ( $valid_block_names as $block ) { - $schema_settings_blocks[ $block ] = static::VALID_SETTINGS; - // `viewport` and `blockVisibility` are global-only settings and cannot be set per block for now. - unset( $schema_settings_blocks[ $block ]['viewport'] ); - unset( $schema_settings_blocks[ $block ]['blockVisibility'] ); - $schema_styles_blocks[ $block ] = $common_block_schema; + $schema_settings_blocks[ $block ] = $common_block_settings; + $schema_styles_blocks[ $block ] = $common_block_schema; // Add responsive pseudo-selectors only to blocks that support them. if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {