@@ -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 }
0 commit comments