Skip to content

Commit e9f34bd

Browse files
committed
Editor: Add support for pseudo elements for the block and its variations on theme.json.
Adds support for pseudo elements on the core/button block for ( ':hover', ':focus', ':focus-visible', ':active' ) at the theme.json level. This is also allowing the block's variations to control the same pseudo elements, so now we can style hover for the outline variation too. Example usage: {{{ "styles": { "blocks": { "core/button": { "color": { "background": "blue" }, ":hover": { "color": { "background": "green" } }, ":focus": { "color": { "background": "purple" } }, "variations": { "outline": { ":hover": { "color": { "background": "pink" } } } } } } } }}} Reviewed by palak678, getdave, MaggieCabrera. Props MaggieCabrera, scruffian, palak678. joedolson, getdave, mikachan. Fixes #64263. git-svn-id: https://develop.svn.wordpress.org/trunk@61607 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 9d9a0f1 commit e9f34bd

3 files changed

Lines changed: 322 additions & 5 deletions

File tree

src/wp-includes/class-wp-theme-json.php

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,16 @@ class WP_Theme_JSON {
609609
'button' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':focus-visible', ':active' ),
610610
);
611611

612+
/**
613+
* The valid pseudo-selectors that can be used for blocks.
614+
*
615+
* @since 7.0
616+
* @var array
617+
*/
618+
const VALID_BLOCK_PSEUDO_SELECTORS = array(
619+
'core/button' => array( ':hover', ':focus', ':focus-visible', ':active' ),
620+
);
621+
612622
/**
613623
* The valid elements that can be found under styles.
614624
*
@@ -699,6 +709,35 @@ protected static function schema_in_root_and_per_origin( $schema ) {
699709
return $schema_in_root_and_per_origin;
700710
}
701711

712+
713+
/**
714+
* Processes pseudo-selectors for any node (block or variation).
715+
*
716+
* @param array $node The node data (block or variation).
717+
* @param string $base_selector The base selector.
718+
* @param array $settings The theme settings.
719+
* @param string $block_name The block name.
720+
* @return array Array of pseudo-selector declarations.
721+
*/
722+
private static function process_pseudo_selectors( $node, $base_selector, $settings, $block_name ) {
723+
$pseudo_declarations = array();
724+
725+
if ( ! isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) {
726+
return $pseudo_declarations;
727+
}
728+
729+
foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) {
730+
if ( isset( $node[ $pseudo_selector ] ) ) {
731+
$combined_selector = static::append_to_selector( $base_selector, $pseudo_selector );
732+
$declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, null );
733+
$pseudo_declarations[ $combined_selector ] = $declarations;
734+
}
735+
}
736+
737+
return $pseudo_declarations;
738+
}
739+
740+
702741
/**
703742
* Returns a class name by an element name.
704743
*
@@ -1018,6 +1057,13 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
10181057
$schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
10191058
$schema_styles_blocks[ $block ] = $styles_non_top_level;
10201059
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
1060+
1061+
// Add pseudo-selectors for blocks that support them
1062+
if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
1063+
foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) {
1064+
$schema_styles_blocks[ $block ][ $pseudo_selector ] = $styles_non_top_level;
1065+
}
1066+
}
10211067
}
10221068

10231069
$block_style_variation_styles = static::VALID_STYLES;
@@ -1040,7 +1086,18 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
10401086

10411087
$schema_styles_variations = array();
10421088
if ( ! empty( $style_variation_names ) ) {
1043-
$schema_styles_variations = array_fill_keys( $style_variation_names, $block_style_variation_styles );
1089+
foreach ( $style_variation_names as $variation_name ) {
1090+
$variation_schema = $block_style_variation_styles;
1091+
1092+
// Add pseudo-selectors to variations for blocks that support them
1093+
if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
1094+
foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) {
1095+
$variation_schema[ $pseudo_selector ] = $styles_non_top_level;
1096+
}
1097+
}
1098+
1099+
$schema_styles_variations[ $variation_name ] = $variation_schema;
1100+
}
10441101
}
10451102

10461103
$schema_styles_blocks[ $block ]['variations'] = $schema_styles_variations;
@@ -2742,6 +2799,23 @@ private static function get_block_nodes( $theme_json, $selectors = array(), $opt
27422799
'variations' => $variation_selectors,
27432800
'css' => $selector,
27442801
);
2802+
2803+
// Handle any pseudo selectors for the block.
2804+
if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] ) ) {
2805+
foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] as $pseudo_selector ) {
2806+
if ( isset( $theme_json['styles']['blocks'][ $name ][ $pseudo_selector ] ) ) {
2807+
$nodes[] = array(
2808+
'name' => $name,
2809+
'path' => array( 'styles', 'blocks', $name, $pseudo_selector ),
2810+
'selector' => static::append_to_selector( $selector, $pseudo_selector ),
2811+
'selectors' => $feature_selectors,
2812+
'duotone' => $duotone_selector,
2813+
'variations' => $variation_selectors,
2814+
'css' => static::append_to_selector( $selector, $pseudo_selector ),
2815+
);
2816+
}
2817+
}
2818+
}
27452819
}
27462820

27472821
if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
@@ -2838,6 +2912,12 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) {
28382912

28392913
// Compute declarations for remaining styles not covered by feature level selectors.
28402914
$style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json );
2915+
2916+
// Process pseudo-selectors for this variation (e.g., :hover, :focus)
2917+
$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 );
2918+
$variation_pseudo_declarations = static::process_pseudo_selectors( $style_variation_node, $style_variation['selector'], $settings, $block_name );
2919+
$style_variation_declarations = array_merge( $style_variation_declarations, $variation_pseudo_declarations );
2920+
28412921
// Store custom CSS for the style variation.
28422922
if ( isset( $style_variation_node['css'] ) ) {
28432923
$style_variation_custom_css[ $style_variation['selector'] ] = $this->process_blocks_custom_css( $style_variation_node['css'], $style_variation['selector'] );
@@ -2872,6 +2952,23 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) {
28722952
$element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ];
28732953
}
28742954

2955+
/*
2956+
* Check if we're processing a block pseudo-selector.
2957+
* $block_metadata['path'] = array( 'styles', 'blocks', 'core/button', ':hover' );
2958+
*/
2959+
$is_processing_block_pseudo = false;
2960+
$block_pseudo_selector = null;
2961+
if ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 4 ) {
2962+
$block_name = $block_metadata['path'][2]; // 'core/button'
2963+
$last_path_element = $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ]; // ':hover'
2964+
2965+
if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) &&
2966+
in_array( $last_path_element, static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ], true ) ) {
2967+
$is_processing_block_pseudo = true;
2968+
$block_pseudo_selector = $last_path_element;
2969+
}
2970+
}
2971+
28752972
/*
28762973
* Check for allowed pseudo classes (e.g. ":hover") from the $selector ("a:hover").
28772974
* This also resets the array keys.
@@ -2901,6 +2998,14 @@ static function ( $pseudo_selector ) use ( $selector ) {
29012998
&& in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true )
29022999
) {
29033000
$declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding );
3001+
} elseif ( $is_processing_block_pseudo ) {
3002+
// Process block pseudo-selector styles
3003+
// For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector
3004+
$block_name = $block_metadata['path'][2]; // 'core/button'
3005+
$block_data = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() );
3006+
$pseudo_data = isset( $block_data[ $block_pseudo_selector ] ) ? $block_data[ $block_pseudo_selector ] : array();
3007+
3008+
$declarations = static::compute_style_properties( $pseudo_data, $settings, null, $this->theme_json, $selector, $use_root_padding );
29043009
} else {
29053010
$declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding );
29063011
}

src/wp-includes/global-styles-and-settings.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ function wp_add_global_styles_for_blocks() {
276276
foreach ( $block_nodes as $metadata ) {
277277

278278
if ( $can_use_cached ) {
279-
// Use the block name as the key for cached CSS data. Otherwise, use a hash of the metadata.
280-
$cache_node_key = $metadata['name'] ?? md5( wp_json_encode( $metadata ) );
279+
// Generate a unique cache key based on the full metadata to ensure pseudo-selectors and other variations get unique keys.
280+
$cache_node_key = md5( wp_json_encode( $metadata ) );
281281

282282
if ( isset( $cached['blocks'][ $cache_node_key ] ) ) {
283283
$block_css = $cached['blocks'][ $cache_node_key ];

0 commit comments

Comments
 (0)