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
43 changes: 39 additions & 4 deletions src/wp-includes/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,39 @@ function wp_register_background_support( $block_type ) {
* @since 6.6.0 Removed requirement for `backgroundImage.source`. A file/url is the default.
* @since 6.7.0 Added support for `backgroundAttachment` output.
* @since 7.1.0 Added support for `background.gradient` output.
* @since 7.2.0 Added support for `background.backgroundClip` output.
*
* @access private
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*
* @phpstan-param array{
* blockName: string,
* attrs: array{
* style?: array{
* background?: array<string, mixed>,
* ...<string, mixed>
* },
* ...<string, mixed>
* },
* ...
* } $block
*/
function wp_render_background_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
if ( ! $block_type ) {
return $block_content;
}

$block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
$has_background_image_support = block_has_support( $block_type, array( 'background', 'backgroundImage' ), false );
$has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false );
$has_background_clip_support = block_has_support( $block_type, array( 'background', 'backgroundClip' ), false );

if (
( ! $has_background_image_support && ! $has_background_gradient_support ) ||
( ! $has_background_image_support && ! $has_background_gradient_support && ! $has_background_clip_support ) ||
! isset( $block_attributes['style']['background'] )
) {
return $block_content;
Expand All @@ -67,8 +85,9 @@ function wp_render_background_support( $block_content, $block ) {
// Check serialization skip for each feature individually.
$skip_background_image = ! $has_background_image_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' );
$skip_background_gradient = ! $has_background_gradient_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'gradient' );
$skip_background_clip = ! $has_background_clip_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' );

if ( $skip_background_image && $skip_background_gradient ) {
if ( $skip_background_image && $skip_background_gradient && $skip_background_clip ) {
return $block_content;
}

Expand All @@ -95,6 +114,10 @@ function wp_render_background_support( $block_content, $block ) {
$background_styles['gradient'] = $block_attributes['style']['background']['gradient'] ?? null;
}

if ( ! $skip_background_clip ) {
$background_styles['backgroundClip'] = $block_attributes['style']['background']['backgroundClip'] ?? null;
}

$styles = wp_style_engine_get_styles( array( 'background' => $background_styles ) );

if ( ! empty( $styles['css'] ) ) {
Expand All @@ -111,7 +134,19 @@ function wp_render_background_support( $block_content, $block ) {
}

$tags->set_attribute( 'style', $updated_style );
$tags->add_class( 'has-background' );

/*
* A background clipped to the text paints the glyphs rather than the
* block's box, so there is no block background to announce. Read the
* clip from the serialized styles so an unsupported or skipped value
* left in the block's attributes cannot suppress the class.
*/
$background_clip = $background_styles['backgroundClip'] ?? null;
$has_background = ! empty( $background_styles['backgroundImage'] ) || ! empty( $background_styles['gradient'] );

if ( $has_background && 'text' !== $background_clip ) {
$tags->add_class( 'has-background' );
}
}

return $tags->get_updated_html();
Expand Down
21 changes: 17 additions & 4 deletions src/wp-includes/block-supports/colors.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ function wp_register_colors_support( $block_type ) {
* @since 5.6.0
* @since 6.1.0 Implemented the style engine to generate CSS and classnames.
* @since 7.1.0 Suppresses color.gradient when background.gradient is supported and set.
* @since 7.2.0 Skips `has-background` for a background color clipped to the text.
* @access private
*
* @param WP_Block_Type $block_type Block type.
* @param array $block_attributes Block attributes.
* @return array Colors CSS classes and inline styles.
* @param WP_Block_Type $block_type Block type.
* @param array<string, mixed> $block_attributes Block attributes.
* @return array<string, mixed> Colors CSS classes and inline styles.
*/
function wp_apply_colors_support( $block_type, $block_attributes ) {
function wp_apply_colors_support( WP_Block_Type $block_type, array $block_attributes ): array {
$color_support = $block_type->supports['color'] ?? false;

if (
Expand Down Expand Up @@ -133,6 +134,18 @@ function wp_apply_colors_support( $block_type, $block_attributes ) {
$attributes = array();
$styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) );

/*
* Mirror of the background support: a color clipped to the text paints the
* glyphs, not the box, so only a gradient still adds `has-background`.
*/
$clips_to_text = 'text' === ( $block_attributes['style']['background']['backgroundClip'] ?? null ) &&
block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ) &&
! wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' );

if ( $clips_to_text && ! empty( $styles['classnames'] ) && empty( $color_block_styles['gradient'] ) ) {
$styles['classnames'] = implode( ' ', array_diff( explode( ' ', $styles['classnames'] ), array( 'has-background' ) ) );
}
Comment thread
aaronrobertshaw marked this conversation as resolved.

if ( ! empty( $styles['classnames'] ) ) {
$attributes['class'] = $styles['classnames'];
}
Expand Down
151 changes: 136 additions & 15 deletions src/wp-includes/block-supports/states.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,96 @@ function wp_get_state_declarations_with_background_resets( $declarations ) {
return $declarations;
}

/**
* Merges one state style object over another.
*
* Mirror of `mergeStyleObjects` in packages/block-editor/src/hooks/style.jsx.
*
* @since 7.2.0
*
* @param array<string, mixed> $target Styles underneath.
* @param array<string, mixed> $source Styles layered over them.
* @return array<string, mixed> Merged style object.
*/
function wp_merge_state_style_objects( $target, $source ) {
$target = is_array( $target ) ? $target : array();
$source = is_array( $source ) ? $source : array();
$merged = $target;

foreach ( $source as $key => $value ) {
$merged[ $key ] = is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] )
? wp_merge_state_style_objects( $merged[ $key ], $value )
: $value;
}

return $merged;
}

/**
* Checks whether a state paints its own background.
*
* @since 7.2.0
* @access private
*
* @param array<string, mixed> $state_style State style object.
* @return bool Whether the state sets a gradient or background image.
*/
function wp_state_paints_own_background( array $state_style ): bool {
return ! empty( $state_style['background']['gradient'] ) ||
! empty( $state_style['background']['backgroundImage'] ) ||
! empty( $state_style['color']['gradient'] );
}

/**
* Returns the declarations that unclip a text clip when a state sets a different clip.
*
* Mirror of `getStateTextClipEscapeCSS` in packages/block-editor/src/hooks/style.jsx.
*
* @since 7.2.0
*
* @param array<string, mixed> $state_style State style object.
* @param string $base_clip Background clip of the styles the state layers over.
* @return array<string, string> CSS declarations, empty when no escape is needed.
Comment thread
aaronrobertshaw marked this conversation as resolved.
*
* @phpstan-return array<non-falsy-string, non-falsy-string>
*/
function wp_get_state_text_clip_escape( array $state_style, string $base_clip ): array {
$state_clip = $state_style['background']['backgroundClip'] ?? '';
if ( ! in_array( $state_clip, WP_Style_Engine::VALID_BACKGROUND_CLIP_VALUES, true ) ) {
$state_clip = '';
}
if ( empty( $state_clip ) || 'text' === $state_clip || 'text' !== $base_clip || wp_state_paints_own_background( $state_style ) ) {
return array();
}
return array( 'background-image' => 'unset' );
}

/**
* Returns the declarations that refill the letters when a state sets a text color but no clip.
*
* The fill is added even without a known text clip, since a block instance
* cannot see a clip set in global styles. It does nothing when no clip applies.
*
* Mirror of `getStateTextFillResetCSS` in packages/block-editor/src/hooks/style.jsx.
*
* @since 7.2.0
*
* @param array<string, mixed> $state_style State style object.
* @return array<string, string> CSS declarations, empty when no reset is needed.
Comment thread
aaronrobertshaw marked this conversation as resolved.
*
* @phpstan-return array<non-falsy-string, non-falsy-string>
*/
function wp_get_state_text_fill_reset( array $state_style ): array {
$state_clip = $state_style['background']['backgroundClip'] ?? '';
if ( ! in_array( $state_clip, WP_Style_Engine::VALID_BACKGROUND_CLIP_VALUES, true ) ) {
$state_clip = '';
}
if ( ! empty( $state_clip ) || empty( $state_style['color']['text'] ) || wp_state_paints_own_background( $state_style ) ) {
return array();
}
return array( '-webkit-text-fill-color' => 'currentColor' );
}

/**
* Adds fallback dimension styles for aspectRatio and height block-support values.
*
Expand Down Expand Up @@ -365,14 +455,16 @@ function wp_get_block_state_element_selectors( $root_selector ) {
*
* @since 7.1.0
* @since 7.2.0 Added fallback dimension styles.
*
* @param array $css_rules Style rules.
* @param string $state Pseudo-state selector.
* @param string|null $selector Block, feature, or element selector.
* @param array $style Style object.
* @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
* Added the `$base_style` parameter.
*
* @param array[] $css_rules Style rules.
* @param string $state Pseudo-state selector.
* @param string|null $selector Block, feature, or element selector.
* @param array<string, mixed> $style Style object.
* @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
* @param array<string, mixed>|null $base_style Optional styles the state layers over.
*/
function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style, $rules_group = null ) {
function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style, $rules_group = null, $base_style = null ) {
if ( empty( $style ) || ! is_array( $style ) ) {
return;
}
Expand All @@ -389,6 +481,16 @@ function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style,
$declarations['text-align'] = $text_align;
}

// Only block states pass a base; element states match the editor and skip it.
if ( null !== $base_style ) {
$base_clip = $base_style['background']['backgroundClip'] ?? '';
$declarations = array_merge(
$declarations,
wp_get_state_text_clip_escape( $style, is_string( $base_clip ) ? $base_clip : '' ),
Comment thread
aaronrobertshaw marked this conversation as resolved.
wp_get_state_text_fill_reset( $style )
);
}

if ( empty( $declarations ) ) {
return;
}
Expand All @@ -407,13 +509,20 @@ function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style,
* Builds compiled state style rules, preserving the selector each rule targets.
*
* @since 7.1.0
* @since 7.2.0 Added the `$base_style` parameter.
*
* @param array $state_styles Map of state to style array.
* @param WP_Block_Type $block_type Block type.
* @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
* @param array<string, mixed> $state_styles Map of state to style array.
* @param WP_Block_Type $block_type Block type.
* @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
* @param array<string, mixed>|null $base_style Optional styles the state layers over.
* @return array[] State style rules.
*/
function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null ) {
function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null, $base_style = null ) {
// Only a block that supports the clip can sit under a text clip to escape.
if ( ! block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ) ) {
$base_style = null;
}

$css_rules = array();
$block_selectors = is_array( $block_type->selectors )
? $block_type->selectors
Expand All @@ -430,7 +539,8 @@ function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_grou
$state,
$group['selector'],
$group['style'],
$rules_group
$rules_group,
$base_style
);
}
}
Expand Down Expand Up @@ -576,6 +686,10 @@ function wp_render_block_states_support( $block_content, $block ) {

$supported_pseudo_states = WP_Theme_JSON::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ?? array();
$css_rules = array();
$default_root_style = wp_get_root_state_style(
$style,
array_merge( array( 'elements' ), $supported_pseudo_states )
);
$viewport_settings = wp_get_global_settings( array( 'viewport' ) );
$responsive_media_queries = WP_Theme_JSON::get_viewport_media_queries( $viewport_settings );

Expand All @@ -588,7 +702,9 @@ function wp_render_block_states_support( $block_content, $block ) {
$css_rules,
wp_get_block_state_style_rules(
array( $pseudo_state => $style[ $pseudo_state ] ),
$block_type
$block_type,
null,
$default_root_style
)
);
}
Expand All @@ -609,7 +725,8 @@ function wp_render_block_states_support( $block_content, $block ) {
wp_get_block_state_style_rules(
array( '' => $root_state_style ),
$block_type,
$media_query
$media_query,
$default_root_style
)
);
}
Expand Down Expand Up @@ -675,7 +792,11 @@ function wp_render_block_states_support( $block_content, $block ) {
wp_get_block_state_style_rules(
array( $pseudo_state => $style[ $breakpoint ][ $pseudo_state ] ),
$block_type,
$media_query
$media_query,
wp_merge_state_style_objects(
$default_root_style,
$root_state_style
)
)
);
}
Expand Down
Loading
Loading