Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 24 additions & 26 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -2885,11 +2885,29 @@ private static function get_block_nodes( $theme_json, $selectors = array(), $opt
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 ] ) ) {
/*
* Append the pseudo-selector to each feature selector so that
* get_feature_declarations_for_node generates CSS scoped to the
* pseudo-state (e.g. '.wp-block-button:hover') rather than the
* default state (e.g. '.wp-block-button').
*/
$pseudo_feature_selectors = array();
foreach ( $feature_selectors ?? array() as $feature => $feature_selector ) {
if ( is_array( $feature_selector ) ) {
$pseudo_feature_selectors[ $feature ] = array();
foreach ( $feature_selector as $subfeature => $subfeature_selector ) {
$pseudo_feature_selectors[ $feature ][ $subfeature ] = static::append_to_selector( $subfeature_selector, $pseudo_selector );
}
} else {
$pseudo_feature_selectors[ $feature ] = static::append_to_selector( $feature_selector, $pseudo_selector );
}
}

$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name, $pseudo_selector ),
'selector' => static::append_to_selector( $selector, $pseudo_selector ),
'selectors' => $feature_selectors,
'selectors' => $pseudo_feature_selectors,
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'css' => static::append_to_selector( $selector, $pseudo_selector ),
Expand Down Expand Up @@ -3046,23 +3064,6 @@ 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.
Expand Down Expand Up @@ -3092,15 +3093,12 @@ 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 = $block_data[ $block_pseudo_selector ] ?? array();

$declarations = static::compute_style_properties( $pseudo_data, $settings, null, $this->theme_json, $selector, $use_root_padding );
} else {
/*
* For block pseudo-selector nodes (e.g. ':hover'), $node has already had any
* feature-selector properties (e.g. writingMode) removed by get_feature_declarations_for_node,
* so those properties are not output twice.
*/
$declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding );
}

Expand Down
32 changes: 32 additions & 0 deletions tests/phpunit/tests/theme/wpThemeJson.php
Original file line number Diff line number Diff line change
Expand Up @@ -6715,6 +6715,38 @@ public function test_merge_incoming_data_unique_slugs_always_preserved() {
$this->assertEqualSetsWithIndex( $expected, $actual );
}

/**
* Tests that when a block with a custom feature selector (e.g. core/button's writingMode
* uses '.wp-block-button' rather than the root '.wp-block-button .wp-block-button__link')
* has pseudo-state styles, the feature selector CSS is scoped to the pseudo-state and not
* output under the block's default-state selector.
*/
public function test_get_stylesheet_pseudo_selector_scopes_feature_selector_css() {
$theme_json = new WP_Theme_JSON(
array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'styles' => array(
'blocks' => array(
'core/button' => array(
':hover' => array(
'typography' => array(
'writingMode' => 'vertical-rl',
),
),
),
),
),
),
'default'
);

$css = $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) );

// writing-mode should be scoped to :hover, not the root block selector.
$this->assertStringContainsString( '.wp-block-button:hover', $css );
$this->assertStringNotContainsString( ':root :where(.wp-block-button){writing-mode', $css );
}

/**
* Test that block pseudo selectors are processed correctly.
*/
Expand Down
Loading