Skip to content
Merged
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
3 changes: 3 additions & 0 deletions backport-changelog/7.0/10523.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/10523

* https://github.com/WordPress/gutenberg/pull/71418
125 changes: 123 additions & 2 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 7.0.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.
*
Expand Down Expand Up @@ -680,6 +690,33 @@ 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();

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 );
$pseudo_declarations[ $combined_selector ] = $declarations;
}
}

return $pseudo_declarations;
}

/**
* Returns a class name by an element name.
*
Expand Down Expand Up @@ -1001,6 +1038,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;
Expand All @@ -1023,7 +1067,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;
Expand Down Expand Up @@ -2663,7 +2718,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;
}
Expand Down Expand Up @@ -2815,6 +2874,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 ) {
Expand Down Expand Up @@ -2913,6 +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 ? 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 );

// 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'] );
Expand All @@ -2936,6 +3018,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 = 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 ] ) &&
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 @@ -2965,6 +3064,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 = 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();

$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 );
}
Expand Down Expand Up @@ -4616,4 +4723,18 @@ 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];
}
}
}
4 changes: 2 additions & 2 deletions lib/global-styles-and-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 ];
Expand Down
Loading
Loading