Changeset 64590
- Timestamp:
- 10/20/2026 06:09:21 AM (36 hours ago)
- Location:
- trunk
- Files:
-
- 11 edited
-
src/wp-includes/block-supports/background.php (modified) (5 diffs)
-
src/wp-includes/block-supports/colors.php (modified) (2 diffs)
-
src/wp-includes/block-supports/states.php (modified) (9 diffs)
-
src/wp-includes/class-wp-theme-json.php (modified) (13 diffs)
-
src/wp-includes/kses.php (modified) (3 diffs)
-
src/wp-includes/style-engine/class-wp-style-engine.php (modified) (4 diffs)
-
tests/phpunit/tests/block-supports/colors.php (modified) (1 diff)
-
tests/phpunit/tests/block-supports/states.php (modified) (2 diffs)
-
tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php (modified) (2 diffs)
-
tests/phpunit/tests/style-engine/styleEngine.php (modified) (2 diffs)
-
tests/phpunit/tests/theme/wpThemeJson.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/block-supports/background.php
r63529 r64590 45 45 * @since 6.7.0 Added support for `backgroundAttachment` output. 46 46 * @since 7.1.0 Added support for `background.gradient` output. 47 * @since 7.2.0 Added support for `background.backgroundClip` output. 47 48 * 48 49 * @access private … … 51 52 * @param array $block Block object. 52 53 * @return string Filtered block content. 54 * 55 * @phpstan-param array{ 56 * blockName: string, 57 * attrs: array{ 58 * style?: array{ 59 * background?: array<string, mixed>, 60 * ...<string, mixed> 61 * }, 62 * ...<string, mixed> 63 * }, 64 * ... 65 * } $block 53 66 */ 54 67 function wp_render_background_support( $block_content, $block ) { 55 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); 68 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); 69 if ( ! $block_type ) { 70 return $block_content; 71 } 72 56 73 $block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array(); 57 74 $has_background_image_support = block_has_support( $block_type, array( 'background', 'backgroundImage' ), false ); 58 75 $has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false ); 76 $has_background_clip_support = block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ); 59 77 60 78 if ( 61 ( ! $has_background_image_support && ! $has_background_gradient_support ) ||79 ( ! $has_background_image_support && ! $has_background_gradient_support && ! $has_background_clip_support ) || 62 80 ! isset( $block_attributes['style']['background'] ) 63 81 ) { … … 68 86 $skip_background_image = ! $has_background_image_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' ); 69 87 $skip_background_gradient = ! $has_background_gradient_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'gradient' ); 88 $skip_background_clip = ! $has_background_clip_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' ); 70 89 71 if ( $skip_background_image && $skip_background_gradient ) {90 if ( $skip_background_image && $skip_background_gradient && $skip_background_clip ) { 72 91 return $block_content; 73 92 } … … 96 115 } 97 116 117 if ( ! $skip_background_clip ) { 118 $background_styles['backgroundClip'] = $block_attributes['style']['background']['backgroundClip'] ?? null; 119 } 120 98 121 $styles = wp_style_engine_get_styles( array( 'background' => $background_styles ) ); 99 122 … … 112 135 113 136 $tags->set_attribute( 'style', $updated_style ); 114 $tags->add_class( 'has-background' ); 137 138 /* 139 * A background clipped to the text paints the glyphs rather than the 140 * block's box, so there is no block background to announce. Read the 141 * clip from the serialized styles so an unsupported or skipped value 142 * left in the block's attributes cannot suppress the class. 143 */ 144 $background_clip = $background_styles['backgroundClip'] ?? null; 145 $has_background = ! empty( $background_styles['backgroundImage'] ) || ! empty( $background_styles['gradient'] ); 146 147 if ( $has_background && 'text' !== $background_clip ) { 148 $tags->add_class( 'has-background' ); 149 } 115 150 } 116 151 -
trunk/src/wp-includes/block-supports/colors.php
r63529 r64590 75 75 * @since 6.1.0 Implemented the style engine to generate CSS and classnames. 76 76 * @since 7.1.0 Suppresses color.gradient when background.gradient is supported and set. 77 * @since 7.2.0 Skips `has-background` for a background color clipped to the text. 77 78 * @access private 78 79 * 79 * @param WP_Block_Type $block_type Block type.80 * @param array $block_attributes Block attributes.81 * @return array Colors CSS classes and inline styles.80 * @param WP_Block_Type $block_type Block type. 81 * @param array<string, mixed> $block_attributes Block attributes. 82 * @return array<string, mixed> Colors CSS classes and inline styles. 82 83 */ 83 function wp_apply_colors_support( $block_type, $block_attributes ){84 function wp_apply_colors_support( WP_Block_Type $block_type, array $block_attributes ): array { 84 85 $color_support = $block_type->supports['color'] ?? false; 85 86 … … 134 135 $styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) ); 135 136 137 /* 138 * Mirror of the background support: a color clipped to the text paints the 139 * glyphs, not the box, so only a gradient still adds `has-background`. 140 */ 141 $clips_to_text = 'text' === ( $block_attributes['style']['background']['backgroundClip'] ?? null ) && 142 block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ) && 143 ! wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' ); 144 145 if ( $clips_to_text && ! empty( $styles['classnames'] ) && empty( $color_block_styles['gradient'] ) ) { 146 $styles['classnames'] = implode( ' ', array_diff( explode( ' ', $styles['classnames'] ), array( 'has-background' ) ) ); 147 } 148 136 149 if ( ! empty( $styles['classnames'] ) ) { 137 150 $attributes['class'] = $styles['classnames']; -
trunk/src/wp-includes/block-supports/states.php
r64322 r64590 146 146 147 147 return $declarations; 148 } 149 150 /** 151 * Merges one state style object over another. 152 * 153 * Mirror of `mergeStyleObjects` in packages/block-editor/src/hooks/style.jsx. 154 * 155 * @since 7.2.0 156 * 157 * @param array<string, mixed> $target Styles underneath. 158 * @param array<string, mixed> $source Styles layered over them. 159 * @return array<string, mixed> Merged style object. 160 */ 161 function wp_merge_state_style_objects( $target, $source ) { 162 $target = is_array( $target ) ? $target : array(); 163 $source = is_array( $source ) ? $source : array(); 164 $merged = $target; 165 166 foreach ( $source as $key => $value ) { 167 $merged[ $key ] = is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) 168 ? wp_merge_state_style_objects( $merged[ $key ], $value ) 169 : $value; 170 } 171 172 return $merged; 173 } 174 175 /** 176 * Checks whether a state paints its own background. 177 * 178 * @since 7.2.0 179 * @access private 180 * 181 * @param array<string, mixed> $state_style State style object. 182 * @return bool Whether the state sets a gradient or background image. 183 */ 184 function wp_state_paints_own_background( array $state_style ): bool { 185 return ! empty( $state_style['background']['gradient'] ) || 186 ! empty( $state_style['background']['backgroundImage'] ) || 187 ! empty( $state_style['color']['gradient'] ); 188 } 189 190 /** 191 * Returns the declarations that unclip a text clip when a state sets a different clip. 192 * 193 * Mirror of `getStateTextClipEscapeCSS` in packages/block-editor/src/hooks/style.jsx. 194 * 195 * @since 7.2.0 196 * 197 * @param array<string, mixed> $state_style State style object. 198 * @param string $base_clip Background clip of the styles the state layers over. 199 * @return array<string, string> CSS declarations, empty when no escape is needed. 200 * 201 * @phpstan-return array<non-falsy-string, non-falsy-string> 202 */ 203 function wp_get_state_text_clip_escape( array $state_style, string $base_clip ): array { 204 $state_clip = $state_style['background']['backgroundClip'] ?? ''; 205 if ( ! in_array( $state_clip, WP_Style_Engine::VALID_BACKGROUND_CLIP_VALUES, true ) ) { 206 $state_clip = ''; 207 } 208 if ( empty( $state_clip ) || 'text' === $state_clip || 'text' !== $base_clip || wp_state_paints_own_background( $state_style ) ) { 209 return array(); 210 } 211 return array( 'background-image' => 'unset' ); 212 } 213 214 /** 215 * Returns the declarations that refill the letters when a state sets a text color but no clip. 216 * 217 * The fill is added even without a known text clip, since a block instance 218 * cannot see a clip set in global styles. It does nothing when no clip applies. 219 * 220 * Mirror of `getStateTextFillResetCSS` in packages/block-editor/src/hooks/style.jsx. 221 * 222 * @since 7.2.0 223 * 224 * @param array<string, mixed> $state_style State style object. 225 * @return array<string, string> CSS declarations, empty when no reset is needed. 226 * 227 * @phpstan-return array<non-falsy-string, non-falsy-string> 228 */ 229 function wp_get_state_text_fill_reset( array $state_style ): array { 230 $state_clip = $state_style['background']['backgroundClip'] ?? ''; 231 if ( ! in_array( $state_clip, WP_Style_Engine::VALID_BACKGROUND_CLIP_VALUES, true ) ) { 232 $state_clip = ''; 233 } 234 if ( ! empty( $state_clip ) || empty( $state_style['color']['text'] ) || wp_state_paints_own_background( $state_style ) ) { 235 return array(); 236 } 237 return array( '-webkit-text-fill-color' => 'currentColor' ); 148 238 } 149 239 … … 366 456 * @since 7.1.0 367 457 * @since 7.2.0 Added fallback dimension styles. 368 * 369 * @param array $css_rules Style rules. 370 * @param string $state Pseudo-state selector. 371 * @param string|null $selector Block, feature, or element selector. 372 * @param array $style Style object. 373 * @param string|null $rules_group Optional CSS grouping rule, e.g. a media query. 374 */ 375 function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style, $rules_group = null ) { 458 * Added the `$base_style` parameter. 459 * 460 * @param array[] $css_rules Style rules. 461 * @param string $state Pseudo-state selector. 462 * @param string|null $selector Block, feature, or element selector. 463 * @param array<string, mixed> $style Style object. 464 * @param string|null $rules_group Optional CSS grouping rule, e.g. a media query. 465 * @param array<string, mixed>|null $base_style Optional styles the state layers over. 466 */ 467 function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style, $rules_group = null, $base_style = null ) { 376 468 if ( empty( $style ) || ! is_array( $style ) ) { 377 469 return; … … 390 482 } 391 483 484 // Only block states pass a base; element states match the editor and skip it. 485 if ( null !== $base_style ) { 486 $base_clip = $base_style['background']['backgroundClip'] ?? ''; 487 $declarations = array_merge( 488 $declarations, 489 wp_get_state_text_clip_escape( $style, is_string( $base_clip ) ? $base_clip : '' ), 490 wp_get_state_text_fill_reset( $style ) 491 ); 492 } 493 392 494 if ( empty( $declarations ) ) { 393 495 return; … … 408 510 * 409 511 * @since 7.1.0 410 * 411 * @param array $state_styles Map of state to style array. 412 * @param WP_Block_Type $block_type Block type. 413 * @param string|null $rules_group Optional CSS grouping rule, e.g. a media query. 512 * @since 7.2.0 Added the `$base_style` parameter. 513 * 514 * @param array<string, mixed> $state_styles Map of state to style array. 515 * @param WP_Block_Type $block_type Block type. 516 * @param string|null $rules_group Optional CSS grouping rule, e.g. a media query. 517 * @param array<string, mixed>|null $base_style Optional styles the state layers over. 414 518 * @return array[] State style rules. 415 519 */ 416 function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null ) { 520 function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null, $base_style = null ) { 521 // Only a block that supports the clip can sit under a text clip to escape. 522 if ( ! block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ) ) { 523 $base_style = null; 524 } 525 417 526 $css_rules = array(); 418 527 $block_selectors = is_array( $block_type->selectors ) … … 431 540 $group['selector'], 432 541 $group['style'], 433 $rules_group 542 $rules_group, 543 $base_style 434 544 ); 435 545 } … … 577 687 $supported_pseudo_states = WP_Theme_JSON::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ?? array(); 578 688 $css_rules = array(); 689 $default_root_style = wp_get_root_state_style( 690 $style, 691 array_merge( array( 'elements' ), $supported_pseudo_states ) 692 ); 579 693 $viewport_settings = wp_get_global_settings( array( 'viewport' ) ); 580 694 $responsive_media_queries = WP_Theme_JSON::get_viewport_media_queries( $viewport_settings ); … … 589 703 wp_get_block_state_style_rules( 590 704 array( $pseudo_state => $style[ $pseudo_state ] ), 591 $block_type 705 $block_type, 706 null, 707 $default_root_style 592 708 ) 593 709 ); … … 610 726 array( '' => $root_state_style ), 611 727 $block_type, 612 $media_query 728 $media_query, 729 $default_root_style 613 730 ) 614 731 ); … … 676 793 array( $pseudo_state => $style[ $breakpoint ][ $pseudo_state ] ), 677 794 $block_type, 678 $media_query 795 $media_query, 796 wp_merge_state_style_objects( 797 $default_root_style, 798 $root_state_style 799 ) 679 800 ) 680 801 ); -
trunk/src/wp-includes/class-wp-theme-json.php
r64491 r64590 260 260 * Added `text-indent` property. 261 261 * @since 7.1.0 Added `min-width` and `text-shadow`. 262 * @since 7.2.0 Added `background-clip`. 262 263 * @var array 263 264 */ … … 271 272 'background-size' => array( 'background', 'backgroundSize' ), 272 273 'background-attachment' => array( 'background', 'backgroundAttachment' ), 274 'background-clip' => array( 'background', 'backgroundClip' ), 273 275 'border-radius' => array( 'border', 'radius' ), 274 276 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), … … 429 431 * Added support for `typography.textIndent`. 430 432 * @since 7.1.0 Added `viewport` property. 433 * @since 7.2.0 Added `background.backgroundClip` property. 431 434 * Added support for `background.gradient`, `dimensions.minWidth` and `blockVisibility.allowEditing`. 432 435 * @since 7.2.0 Added support for `typography.textShadow`, `typography.textShadowPresets`, … … 438 441 'useRootPaddingAwareAlignments' => null, 439 442 'background' => array( 443 'backgroundClip' => null, 440 444 'backgroundImage' => null, 441 445 'backgroundSize' => null, … … 583 587 * @since 7.1.0 Added support for `background.gradient`,`dimensions.minWidth`, 584 588 * and `typography.textShadow`. 589 * @since 7.2.0 Added support for `background.backgroundClip`. 585 590 * @var array 586 591 */ 587 592 const VALID_STYLES = array( 588 593 'background' => array( 594 'backgroundClip' => null, 589 595 'backgroundImage' => null, 590 596 'backgroundPosition' => null, … … 1001 1007 } 1002 1008 1009 // Nearest first: this node, then its variation's block. 1010 $base_styles = array( $node ); 1011 foreach ( array( $style_variation['path'] ?? null, $block_metadata['path'] ?? null ) as $path ) { 1012 $layer = $path ? _wp_array_get( $this->theme_json, $path ) : null; 1013 if ( is_array( $layer ) ) { 1014 $base_styles[] = $layer; 1015 } 1016 } 1017 1003 1018 foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) { 1004 1019 if ( isset( $node[ $pseudo_selector ] ) ) { … … 1022 1037 $combined_selector = static::append_to_selector( $base_selector, $pseudo_selector ); 1023 1038 $declarations = static::compute_style_properties( $pseudo_node, $settings, null, null ); 1039 $declarations = self::get_state_declarations_with_text_clip_escapes( $declarations, $pseudo_node, $base_styles ); 1024 1040 $add_declarations( $combined_selector, $declarations ); 1025 1041 } … … 3225 3241 'value' => $value, 3226 3242 ); 3243 3244 /* 3245 * Background clipping needs vendor prefixed properties for 3246 * cross-browser support. The `text` value clips the background to 3247 * the text and makes it visible through a transparent fill. The box 3248 * values only reset the fill color, to cancel any inherited text 3249 * clipping. `-webkit-background-clip` is an alias of 3250 * `background-clip` in Chromium, so resetting it would discard the 3251 * value set above. Values outside the set the style engine accepts 3252 * get neither, so a typo cannot reset the fill color. 3253 */ 3254 if ( 'background-clip' === $css_property ) { 3255 if ( 'text' === $value ) { 3256 $declarations[] = array( 3257 'name' => '-webkit-background-clip', 3258 'value' => 'text', 3259 ); 3260 $declarations[] = array( 3261 'name' => '-webkit-text-fill-color', 3262 'value' => 'transparent', 3263 ); 3264 } elseif ( in_array( $value, array( 'border-box', 'padding-box', 'content-box' ), true ) ) { 3265 $declarations[] = array( 3266 'name' => '-webkit-text-fill-color', 3267 'value' => 'currentColor', 3268 ); 3269 } 3270 } 3227 3271 } 3228 3272 … … 3923 3967 3924 3968 /** 3969 * Returns the node keys that hold a state's styles: a breakpoint, a pseudo 3970 * state or a custom state. 3971 * 3972 * @since 7.2.0 3973 * 3974 * @param array<string, mixed> $settings Settings from theme.json. 3975 * @return string[] Style-state keys. 3976 * 3977 * @phpstan-return list<non-falsy-string> 3978 */ 3979 private static function get_style_state_keys( array $settings ): array { 3980 $keys = array_keys( static::get_viewport_media_queries( $settings['viewport'] ?? null ) ); 3981 3982 foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS as $pseudo_selectors ) { 3983 $keys = array_merge( $keys, $pseudo_selectors ); 3984 } 3985 3986 foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS as $pseudo_selectors ) { 3987 $keys = array_merge( $keys, $pseudo_selectors ); 3988 } 3989 3990 foreach ( static::VALID_BLOCK_CUSTOM_STATES as $custom_states ) { 3991 $keys = array_merge( $keys, $custom_states ); 3992 } 3993 3994 return array_values( array_unique( $keys ) ); 3995 } 3996 3997 /** 3998 * Returns the style objects a state node layers over, nearest first. 3999 * 4000 * Drops the path's state keys (e.g. `@mobile`) from the innermost outward. 4001 * 4002 * @since 7.2.0 4003 * 4004 * @param string[] $path Path to the node within the theme.json tree. 4005 * @param array<string, mixed> $tree The theme.json tree to read from. 4006 * @param array<string, mixed> $settings Settings from theme.json. 4007 * @return array[] Style objects, nearest first. 4008 */ 4009 private static function get_state_base_styles( array $path, array $tree, array $settings ): array { 4010 $state_keys = self::get_style_state_keys( $settings ); 4011 $base_path = $path; 4012 $base_styles = array(); 4013 4014 foreach ( array_reverse( array_keys( $path ) ) as $index ) { 4015 if ( ! in_array( $path[ $index ], $state_keys, true ) ) { 4016 continue; 4017 } 4018 4019 unset( $base_path[ $index ] ); 4020 $layer = _wp_array_get( $tree, array_values( $base_path ) ); 4021 if ( is_array( $layer ) ) { 4022 $base_styles[] = $layer; 4023 } 4024 } 4025 4026 return $base_styles; 4027 } 4028 4029 /** 4030 * Adds the declarations a state needs to escape a text clip set below it. 4031 * 4032 * @since 7.2.0 4033 * 4034 * @param array[] $declarations Declarations generated for the state. 4035 * @param array<string, mixed> $state_style State style object. 4036 * @param array[] $base_styles Style objects the state layers over, nearest first. 4037 * @return array[] Declarations with the escapes applied where needed. 4038 * 4039 * @phpstan-param list<array{ name: non-falsy-string, value: scalar }> $declarations 4040 * @phpstan-return list<array{ name: non-falsy-string, value: scalar }> 4041 */ 4042 private static function get_state_declarations_with_text_clip_escapes( array $declarations, array $state_style, array $base_styles ): array { 4043 $base_clip = ''; 4044 foreach ( $base_styles as $base_style ) { 4045 if ( ! empty( $base_style['background']['backgroundClip'] ) ) { 4046 $base_clip = is_string( $base_style['background']['backgroundClip'] ) ? $base_style['background']['backgroundClip'] : ''; 4047 break; 4048 } 4049 } 4050 4051 if ( 'text' !== $base_clip ) { 4052 return $declarations; 4053 } 4054 4055 $escapes = array_merge( 4056 wp_get_state_text_clip_escape( $state_style, $base_clip ), 4057 wp_get_state_text_fill_reset( $state_style ) 4058 ); 4059 4060 foreach ( $escapes as $name => $value ) { 4061 $declarations[] = array( 4062 'name' => $name, 4063 'value' => $value, 4064 ); 4065 } 4066 4067 return $declarations; 4068 } 4069 4070 /** 3925 4071 * Gets the CSS rules for a particular block from theme.json. 3926 4072 * … … 4057 4203 // Process base properties for this breakpoint. 4058 4204 $breakpoint_declarations = static::compute_style_properties( $breakpoint_node, $settings, null, $this->theme_json ); 4205 $breakpoint_declarations = self::get_state_declarations_with_text_clip_escapes( 4206 $breakpoint_declarations, 4207 $breakpoint_node, 4208 array( $style_variation_node, $node ) 4209 ); 4059 4210 if ( ! empty( $breakpoint_declarations ) ) { 4060 4211 $base_ruleset = static::to_ruleset( ':root :where(' . $style_variation['selector'] . ')', $breakpoint_declarations ); … … 4181 4332 * Otherwise just compute the styles for the default selector as normal. 4182 4333 */ 4334 $base_styles = self::get_state_base_styles( $block_metadata['path'] ?? array(), $this->theme_json, $settings ); 4335 4183 4336 if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && 4184 4337 isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] ) … … 4186 4339 ) { 4187 4340 $declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json, $selector, $use_root_padding ); 4341 $declarations = self::get_state_declarations_with_text_clip_escapes( 4342 $declarations, 4343 $node[ $pseudo_selector ], 4344 array_merge( array( $node ), $base_styles ) 4345 ); 4188 4346 } else { 4189 4347 /* … … 4193 4351 */ 4194 4352 $declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json, $selector, $use_root_padding ); 4353 $declarations = self::get_state_declarations_with_text_clip_escapes( $declarations, $node, $base_styles ); 4195 4354 } 4196 4355 -
trunk/src/wp-includes/kses.php
r64436 r64590 3693 3693 * and URLs in the SVG element reference properties. 3694 3694 * @since 7.2.0 Added support for CSS anchor positioning properties and `text-shadow`. 3695 * Added support for `background-clip`. 3695 3696 * 3696 3697 * @param string $css A string of CSS rules, decoded from an HTML `style` attribute. … … 3718 3719 * @since 2.8.1 3719 3720 * @since 7.1.0 Added support for SVG presentation attributes. 3721 * @since 7.2.0 Added support for `background-clip` and its vendor prefixed companions. 3720 3722 * 3721 3723 * @param string[] $attr Array of allowed CSS attributes. … … 3732 3734 'background-attachment', 3733 3735 'background-blend-mode', 3736 'background-clip', 3737 '-webkit-background-clip', 3738 '-webkit-text-fill-color', 3734 3739 3735 3740 'border', -
trunk/src/wp-includes/style-engine/class-wp-style-engine.php
r64093 r64590 33 33 final class WP_Style_Engine { 34 34 /** 35 * The values `background-clip` accepts. 36 * 37 * @since 7.2.0 38 * @var string[] 39 */ 40 const VALID_BACKGROUND_CLIP_VALUES = array( 'border-box', 'padding-box', 'content-box', 'text' ); 41 42 /** 35 43 * Style definitions that contain the instructions to parse/output valid Gutenberg styles from a block's attributes. 36 44 * … … 51 59 * @since 6.1.0 52 60 * @since 7.1.0 Added `background.gradient` property. 61 * @since 7.2.0 Added `background.backgroundClip` property. 53 62 * @var array 54 63 */ … … 97 106 'has-background' => true, 98 107 ), 108 ), 109 'backgroundClip' => array( 110 'value_func' => array( self::class, 'get_background_clip_css_declarations' ), 111 'path' => array( 'background', 'backgroundClip' ), 99 112 ), 100 113 ), … … 770 783 771 784 /** 785 * Style value parser that returns the CSS declarations for background clipping. 786 * 787 * For the `text` value the background is clipped to the block's text, which 788 * requires the vendor prefixed properties and a transparent fill color. 789 * 790 * @since 7.2.0 791 * 792 * @param string $style_value A single raw style value from $block_styles array. 793 * @param array $style_definition A single style definition from BLOCK_STYLE_DEFINITIONS_METADATA. 794 * @return array<string, string> An associative array of CSS definitions, mapping properties to values. 795 * 796 * @phpstan-return array<non-falsy-string, non-falsy-string> 797 */ 798 protected static function get_background_clip_css_declarations( $style_value, $style_definition ) { 799 if ( ! in_array( $style_value, static::VALID_BACKGROUND_CLIP_VALUES, true ) ) { 800 return array(); 801 } 802 803 $css_declarations = array( 804 'background-clip' => $style_value, 805 ); 806 807 if ( 'text' === $style_value ) { 808 $css_declarations['-webkit-background-clip'] = 'text'; 809 $css_declarations['-webkit-text-fill-color'] = 'transparent'; 810 } else { 811 /* 812 * Only the fill color is restored. `-webkit-background-clip` is an 813 * alias of `background-clip` in Chromium, so resetting it here would 814 * discard the value set above. The fill color is inherited, so it 815 * needs its initial value rather than `unset`, which would take a 816 * transparent fill from an ancestor clipping to text. 817 */ 818 $css_declarations['-webkit-text-fill-color'] = 'currentColor'; 819 } 820 821 return $css_declarations; 822 } 823 824 /** 772 825 * Returns compiled CSS from CSS declarations. 773 826 * -
trunk/tests/phpunit/tests/block-supports/colors.php
r62718 r64590 300 300 $this->assertSame( $expected, $actual ); 301 301 } 302 303 /** 304 * Registers a block with color and background clip support. 305 * 306 * @param array|bool $background_skip Background serialization skip. 307 * @return WP_Block_Type The registered block type. 308 */ 309 private function register_background_clip_block( $background_skip = false ) { 310 $this->test_block_name = 'test/color-background-clip'; 311 register_block_type( 312 $this->test_block_name, 313 array( 314 'api_version' => 3, 315 'attributes' => array( 316 'backgroundColor' => array( 317 'type' => 'string', 318 ), 319 'gradient' => array( 320 'type' => 'string', 321 ), 322 'style' => array( 323 'type' => 'object', 324 ), 325 ), 326 'supports' => array( 327 'color' => array( 328 'background' => true, 329 'gradients' => true, 330 ), 331 'background' => array( 332 'backgroundClip' => true, 333 '__experimentalSkipSerialization' => $background_skip, 334 ), 335 ), 336 ) 337 ); 338 339 return WP_Block_Type_Registry::get_instance()->get_registered( $this->test_block_name ); 340 } 341 342 /** 343 * @ticket 66094 344 * 345 * @dataProvider data_background_color_has_background_with_a_clip 346 * 347 * @param array $block_atts Block attributes. 348 * @param array|bool $background_skip Background serialization skip. 349 * @param string|null $expected_class Expected class attribute. 350 */ 351 public function test_background_color_has_background_with_a_clip( $block_atts, $background_skip, $expected_class ) { 352 $block_type = $this->register_background_clip_block( $background_skip ); 353 354 $actual = wp_apply_colors_support( $block_type, $block_atts ); 355 356 $this->assertSame( $expected_class, $actual['class'] ?? null ); 357 } 358 359 /** 360 * Data provider. 361 * 362 * @return array 363 */ 364 public function data_background_color_has_background_with_a_clip() { 365 return array( 366 'preset color clipped to the text' => array( 367 array( 368 'backgroundColor' => 'base', 369 'style' => array( 'background' => array( 'backgroundClip' => 'text' ) ), 370 ), 371 false, 372 'has-base-background-color', 373 ), 374 'custom color clipped to the text' => array( 375 array( 376 'style' => array( 377 'color' => array( 'background' => '#ff0000' ), 378 'background' => array( 'backgroundClip' => 'text' ), 379 ), 380 ), 381 false, 382 null, 383 ), 384 'preset color clipped to a box' => array( 385 array( 386 'backgroundColor' => 'base', 387 'style' => array( 'background' => array( 'backgroundClip' => 'padding-box' ) ), 388 ), 389 false, 390 'has-background has-base-background-color', 391 ), 392 'preset color with a skipped text clip' => array( 393 array( 394 'backgroundColor' => 'base', 395 'style' => array( 'background' => array( 'backgroundClip' => 'text' ) ), 396 ), 397 array( 'backgroundClip' ), 398 'has-background has-base-background-color', 399 ), 400 'color gradient over a text clip' => array( 401 array( 402 'gradient' => 'vivid', 403 'style' => array( 'background' => array( 'backgroundClip' => 'text' ) ), 404 ), 405 false, 406 'has-background has-vivid-gradient-background', 407 ), 408 ); 409 } 302 410 } -
trunk/tests/phpunit/tests/block-supports/states.php
r64322 r64590 162 162 163 163 /** 164 * Tests that a state escaping a text clip stops the inherited gradient painting. 165 * 166 * @ticket 66094 167 * @covers ::wp_get_state_text_clip_escape 168 */ 169 public function test_text_clip_escape_unsets_the_background_image_when_a_state_escapes_a_text_clip() { 170 $actual = wp_get_state_text_clip_escape( 171 array( 'background' => array( 'backgroundClip' => 'border-box' ) ), 172 'text' 173 ); 174 175 $this->assertSame( array( 'background-image' => 'unset' ), $actual ); 176 } 177 178 /** 179 * Tests that a background the state paints itself is kept. 180 * 181 * @ticket 66094 182 * @covers ::wp_get_state_text_clip_escape 183 * @covers ::wp_get_state_text_fill_reset 184 * @covers ::wp_state_paints_own_background 185 * 186 * @dataProvider data_state_styles_that_paint_their_own_background 187 * 188 * @param array $state_style State style object. 189 */ 190 public function test_text_clip_helpers_add_nothing_when_the_state_paints_its_own_background( $state_style ) { 191 $this->assertSame( array(), wp_get_state_text_clip_escape( $state_style, 'text' ), 'Expected no text clip escape.' ); 192 $this->assertSame( array(), wp_get_state_text_fill_reset( $state_style ), 'Expected no text fill reset.' ); 193 } 194 195 /** 196 * Data provider. 197 * 198 * @return array[] 199 */ 200 public function data_state_styles_that_paint_their_own_background() { 201 $color = array( 'text' => '#ff0000' ); 202 203 return array( 204 'background gradient' => array( 205 array( 206 'color' => $color, 207 'background' => array( 208 'backgroundClip' => 'padding-box', 209 'gradient' => 'linear-gradient(135deg, #ff0000, #0000ff)', 210 ), 211 ), 212 ), 213 'background image' => array( 214 array( 215 'color' => $color, 216 'background' => array( 217 'backgroundClip' => 'padding-box', 218 'backgroundImage' => array( 'url' => 'image.png' ), 219 ), 220 ), 221 ), 222 'color gradient' => array( 223 array( 224 'color' => array_merge( $color, array( 'gradient' => 'linear-gradient(135deg, #ff0000, #0000ff)' ) ), 225 'background' => array( 'backgroundClip' => 'padding-box' ), 226 ), 227 ), 228 'gradient, no clip' => array( 229 array( 230 'color' => $color, 231 'background' => array( 'gradient' => 'linear-gradient(135deg, #ff0000, #0000ff)' ), 232 ), 233 ), 234 ); 235 } 236 237 /** 238 * Tests that a box clip is left alone when nothing below it clips to text. 239 * 240 * @ticket 66094 241 * @covers ::wp_get_state_text_clip_escape 242 * 243 * @dataProvider data_base_clips_that_are_not_text 244 * 245 * @param string $base_clip Background clip the state layers over. 246 */ 247 public function test_text_clip_escape_adds_nothing_without_a_base_text_clip( $base_clip ) { 248 $actual = wp_get_state_text_clip_escape( 249 array( 'background' => array( 'backgroundClip' => 'padding-box' ) ), 250 $base_clip 251 ); 252 253 $this->assertSame( array(), $actual ); 254 } 255 256 /** 257 * Data provider. 258 * 259 * @return array[] 260 */ 261 public function data_base_clips_that_are_not_text() { 262 return array( 263 'no base clip' => array( '' ), 264 'border-box' => array( 'border-box' ), 265 ); 266 } 267 268 /** 269 * Tests that an empty state clip of any type counts as no clip. 270 * 271 * @ticket 66094 272 * @covers ::wp_get_state_text_clip_escape 273 * @covers ::wp_get_state_text_fill_reset 274 * 275 * @dataProvider data_empty_state_clips 276 * 277 * @param mixed $state_clip Background clip set by the state. 278 */ 279 public function test_text_clip_helpers_treat_an_empty_state_clip_as_no_clip( $state_clip ) { 280 $state_style = array( 281 'color' => array( 'text' => '#ff0000' ), 282 'background' => array( 'backgroundClip' => $state_clip ), 283 ); 284 285 $this->assertSame( array(), wp_get_state_text_clip_escape( $state_style, 'text' ), 'Expected no text clip escape.' ); 286 $this->assertSame( 287 array( '-webkit-text-fill-color' => 'currentColor' ), 288 wp_get_state_text_fill_reset( $state_style ), 289 'Expected the text fill reset.' 290 ); 291 } 292 293 /** 294 * Data provider. 295 * 296 * @return array[] 297 */ 298 public function data_empty_state_clips() { 299 return array( 300 'false' => array( false ), 301 'zero' => array( 0 ), 302 'zero string' => array( '0' ), 303 'empty string' => array( '' ), 304 'empty array' => array( array() ), 305 ); 306 } 307 308 /** 309 * Tests that an invalid state clip over a base text clip does not unset the gradient. 310 * 311 * @ticket 66094 312 * @covers ::wp_get_state_text_clip_escape 313 */ 314 public function test_text_clip_escape_ignores_an_invalid_state_clip() { 315 $actual = wp_get_state_text_clip_escape( 316 array( 'background' => array( 'backgroundClip' => 'borderbox' ) ), 317 'text' 318 ); 319 320 $this->assertSame( array(), $actual ); 321 } 322 323 /** 324 * Tests that a text color with an invalid state clip gets its fill back. 325 * 326 * @ticket 66094 327 * @covers ::wp_get_state_text_fill_reset 328 */ 329 public function test_text_fill_reset_ignores_an_invalid_state_clip() { 330 $actual = wp_get_state_text_fill_reset( 331 array( 332 'color' => array( 'text' => '#ff0000' ), 333 'background' => array( 'backgroundClip' => 'borderbox' ), 334 ) 335 ); 336 337 $this->assertSame( array( '-webkit-text-fill-color' => 'currentColor' ), $actual ); 338 } 339 340 /** 341 * Tests that a state without its own clip, or keeping the text clip, needs no escape. 342 * 343 * @ticket 66094 344 * @covers ::wp_get_state_text_clip_escape 345 */ 346 public function test_text_clip_escape_adds_nothing_when_the_state_keeps_the_text_clip() { 347 $this->assertSame( 348 array(), 349 wp_get_state_text_clip_escape( array( 'background' => array( 'backgroundClip' => 'text' ) ), 'text' ), 350 'Expected no escape for a state that also clips to text.' 351 ); 352 $this->assertSame( 353 array(), 354 wp_get_state_text_clip_escape( array( 'color' => array( 'text' => '#ff0000' ) ), 'text' ), 355 'Expected no escape for a state without a clip.' 356 ); 357 } 358 359 /** 360 * Tests that a text color set without a clip gets its fill back. 361 * 362 * @ticket 66094 363 * @covers ::wp_get_state_text_fill_reset 364 */ 365 public function test_text_fill_reset_refills_a_text_color() { 366 $actual = wp_get_state_text_fill_reset( array( 'color' => array( 'text' => '#ff0000' ) ) ); 367 368 $this->assertSame( array( '-webkit-text-fill-color' => 'currentColor' ), $actual ); 369 } 370 371 /** 372 * Tests that the fill is left to the style engine when the state sets its own clip. 373 * 374 * @ticket 66094 375 * @covers ::wp_get_state_text_fill_reset 376 */ 377 public function test_text_fill_reset_adds_nothing_when_the_state_sets_its_own_clip() { 378 $state_style = array( 379 'color' => array( 'text' => '#ff0000' ), 380 'background' => array( 'backgroundClip' => 'text' ), 381 ); 382 383 $this->assertSame( array(), wp_get_state_text_fill_reset( $state_style ) ); 384 } 385 386 /** 387 * Tests that the fill is left alone when the state sets no text color. 388 * 389 * @ticket 66094 390 * @covers ::wp_get_state_text_fill_reset 391 */ 392 public function test_text_fill_reset_adds_nothing_without_a_text_color() { 393 $this->assertSame( array(), wp_get_state_text_fill_reset( array( 'color' => array( 'background' => '#ff0000' ) ) ) ); 394 } 395 396 /** 397 * Tests that a block state escapes its base text clip. 398 * 399 * @ticket 66094 400 * @covers ::wp_add_block_state_style_rule 401 */ 402 public function test_block_state_rule_escapes_a_base_text_clip() { 403 $css_rules = array(); 404 405 wp_add_block_state_style_rule( 406 $css_rules, 407 ':hover', 408 '.wp-block-test', 409 array( 'background' => array( 'backgroundClip' => 'border-box' ) ), 410 null, 411 array( 'background' => array( 'backgroundClip' => 'text' ) ) 412 ); 413 414 $this->assertSame( 'unset', $css_rules[0]['declarations']['background-image'] ?? null ); 415 } 416 417 /** 418 * Tests that a block state text color gets its fill back over a base box clip, 419 * since a text clip from global styles cannot be seen from the block. 420 * 421 * @ticket 66094 422 * @covers ::wp_add_block_state_style_rule 423 */ 424 public function test_block_state_rule_refills_a_text_color_over_a_base_box_clip() { 425 $css_rules = array(); 426 427 wp_add_block_state_style_rule( 428 $css_rules, 429 ':hover', 430 '.wp-block-test', 431 array( 'color' => array( 'text' => '#ff0000' ) ), 432 null, 433 array( 'background' => array( 'backgroundClip' => 'border-box' ) ) 434 ); 435 436 $declarations = $css_rules[0]['declarations']; 437 $this->assertSame( 'currentColor', $declarations['-webkit-text-fill-color'] ?? null, 'Expected the text fill reset.' ); 438 $this->assertArrayNotHasKey( 'background-image', $declarations, 'Expected no text clip escape.' ); 439 } 440 441 /** 164 442 * Tests that background-image reset is not added when the state also sets a legacy gradient. 165 443 * … … 1015 1293 $this->assertStringContainsString( 1016 1294 '@media (width <= 480px){.' . $matches[0] . '{color:#ff0000 !important;}}', 1295 $actual_stylesheet 1296 ); 1297 } 1298 1299 /** 1300 * Tests that a responsive text color fills the letters again for a block 1301 * that supports the background clip, so a text gradient from global styles 1302 * cannot hide it. 1303 * 1304 * @ticket 66094 1305 */ 1306 public function test_responsive_text_color_fills_text_for_a_block_that_supports_the_clip() { 1307 $this->ensure_block_registered( 1308 'test/responsive-text-clip-state', 1309 array(), 1310 array( 'background' => array( 'backgroundClip' => true ) ) 1311 ); 1312 1313 $block = array( 1314 'blockName' => 'test/responsive-text-clip-state', 1315 'attrs' => array( 1316 'style' => array( 1317 '@mobile' => array( 1318 'color' => array( 1319 'text' => '#ff0000', 1320 ), 1321 ), 1322 ), 1323 ), 1324 ); 1325 1326 $actual = wp_render_block_states_support( '<div class="wp-block-test">Hello</div>', $block ); 1327 preg_match( '/wp-states-[a-f0-9]{8}/', $actual, $matches ); 1328 $actual_stylesheet = wp_style_engine_get_stylesheet_from_context( 'block-supports', array( 'prettify' => false ) ); 1329 1330 $this->assertStringContainsString( 1331 '@media (width <= 480px){.' . $matches[0] . '{color:#ff0000 !important;-webkit-text-fill-color:currentColor !important;}}', 1017 1332 $actual_stylesheet 1018 1333 ); -
trunk/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php
r64449 r64590 71 71 * @ticket 61858 72 72 * @ticket 64974 73 * @ticket 66094 73 74 * 74 75 * @covers ::wp_render_background_support … … 283 284 'wrapper' => '<div>Content</div>', 284 285 ), 286 'background clip alone does not add has-background' => array( 287 'theme_name' => 'block-theme-child-with-fluid-typography', 288 'block_name' => 'test/background-clip-is-output', 289 'background_settings' => array( 290 'backgroundClip' => true, 291 ), 292 'background_style' => array( 293 'backgroundClip' => 'border-box', 294 ), 295 'expected_wrapper' => '<div style="background-clip:border-box;-webkit-text-fill-color:currentColor;">Content</div>', 296 'wrapper' => '<div>Content</div>', 297 ), 298 'background clip text style is applied with vendor prefixes' => array( 299 'theme_name' => 'block-theme-child-with-fluid-typography', 300 'block_name' => 'test/background-clip-text-is-output', 301 'background_settings' => array( 302 'backgroundClip' => true, 303 ), 304 'background_style' => array( 305 'backgroundClip' => 'text', 306 ), 307 'expected_wrapper' => '<div style="background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;">Content</div>', 308 'wrapper' => '<div>Content</div>', 309 ), 310 'background clip alone does not add has-background for block with only backgroundClip support' => array( 311 'theme_name' => 'block-theme-child-with-fluid-typography', 312 'block_name' => 'test/background-clip-only-support', 313 'background_settings' => array( 314 'backgroundClip' => true, 315 ), 316 'background_style' => array( 317 'backgroundClip' => 'padding-box', 318 ), 319 'expected_wrapper' => '<p style="background-clip:padding-box;-webkit-text-fill-color:currentColor;">Content</p>', 320 'wrapper' => '<p>Content</p>', 321 ), 322 'background clip style is not applied if the block does not support it' => array( 323 'theme_name' => 'block-theme-child-with-fluid-typography', 324 'block_name' => 'test/background-clip-not-supported', 325 'background_settings' => array( 326 'backgroundClip' => false, 327 ), 328 'background_style' => array( 329 'backgroundClip' => 'text', 330 ), 331 'expected_wrapper' => '<div>Content</div>', 332 'wrapper' => '<div>Content</div>', 333 ), 334 'background clip keeps has-background when a background is painted into a box' => array( 335 'theme_name' => 'block-theme-child-with-fluid-typography', 336 'block_name' => 'test/background-clip-with-gradient', 337 'background_settings' => array( 338 'gradient' => true, 339 'backgroundClip' => true, 340 ), 341 'background_style' => array( 342 'gradient' => 'linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%)', 343 'backgroundClip' => 'padding-box', 344 ), 345 'expected_wrapper' => '<div class="has-background" style="background-image:linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%);background-clip:padding-box;-webkit-text-fill-color:currentColor;">Content</div>', 346 'wrapper' => '<div>Content</div>', 347 ), 348 'background clipped to text drops has-background from a gradient' => array( 349 'theme_name' => 'block-theme-child-with-fluid-typography', 350 'block_name' => 'test/background-clip-text-with-gradient', 351 'background_settings' => array( 352 'gradient' => true, 353 'backgroundClip' => true, 354 ), 355 'background_style' => array( 356 'gradient' => 'linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%)', 357 'backgroundClip' => 'text', 358 ), 359 'expected_wrapper' => '<div style="background-image:linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;">Content</div>', 360 'wrapper' => '<div>Content</div>', 361 ), 362 'background clipped to text keeps has-background when the block does not support the clip' => array( 363 'theme_name' => 'block-theme-child-with-fluid-typography', 364 'block_name' => 'test/background-clip-text-without-clip-support', 365 'background_settings' => array( 366 'gradient' => true, 367 'backgroundClip' => false, 368 ), 369 'background_style' => array( 370 'gradient' => 'linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%)', 371 'backgroundClip' => 'text', 372 ), 373 'expected_wrapper' => '<div class="has-background" style="background-image:linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%);">Content</div>', 374 'wrapper' => '<div>Content</div>', 375 ), 376 'background clip style is appended to existing styles' => array( 377 'theme_name' => 'block-theme-child-with-fluid-typography', 378 'block_name' => 'test/background-clip-appends-styles', 379 'background_settings' => array( 380 'backgroundClip' => true, 381 ), 382 'background_style' => array( 383 'backgroundClip' => 'content-box', 384 ), 385 'expected_wrapper' => '<div class="wp-block-test" style="color: red;background-clip:content-box;-webkit-text-fill-color:currentColor;">Content</div>', 386 'wrapper' => '<div class="wp-block-test" style="color: red">Content</div>', 387 ), 285 388 ); 286 389 } -
trunk/tests/phpunit/tests/style-engine/styleEngine.php
r62730 r64590 26 26 * @ticket 64974 27 27 * @ticket 65037 28 * @ticket 66094 28 29 * 29 30 * @covers ::wp_style_engine_get_styles … … 668 669 ), 669 670 ), 671 672 'inline_background_clip_border_box' => array( 673 'block_styles' => array( 674 'background' => array( 675 'backgroundClip' => 'border-box', 676 ), 677 ), 678 'options' => array(), 679 'expected_output' => array( 680 'css' => 'background-clip:border-box;-webkit-text-fill-color:currentColor;', 681 'declarations' => array( 682 'background-clip' => 'border-box', 683 '-webkit-text-fill-color' => 'currentColor', 684 ), 685 ), 686 ), 687 688 'inline_background_clip_text_with_vendor_prefixes' => array( 689 'block_styles' => array( 690 'background' => array( 691 'backgroundClip' => 'text', 692 ), 693 ), 694 'options' => array(), 695 'expected_output' => array( 696 'css' => 'background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;', 697 'declarations' => array( 698 'background-clip' => 'text', 699 '-webkit-background-clip' => 'text', 700 '-webkit-text-fill-color' => 'transparent', 701 ), 702 ), 703 ), 704 705 'inline_background_clip_invalid_value' => array( 706 'block_styles' => array( 707 'background' => array( 708 'backgroundClip' => 'invalid-value', 709 ), 710 ), 711 'options' => array(), 712 'expected_output' => array(), 713 ), 670 714 ); 671 715 } -
trunk/tests/phpunit/tests/theme/wpThemeJson.php
r64491 r64590 1369 1369 $this->assertStringContainsString( 1370 1370 '@media (640px < width <= 960px){:root :where(.wp-block-group){color: blue;}}', 1371 $stylesheet 1372 ); 1373 } 1374 1375 /** 1376 * Builds a Paragraph stylesheet with a text gradient and the given states. 1377 * 1378 * @param array $state_styles State styles to merge into the block styles. 1379 * @return string Generated stylesheet. 1380 */ 1381 private function get_text_clip_stylesheet( $state_styles ) { 1382 $theme_json = new WP_Theme_JSON( 1383 array( 1384 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1385 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1386 'styles' => array( 1387 'blocks' => array( 1388 'core/paragraph' => array_merge( 1389 array( 1390 'background' => array( 1391 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1392 'backgroundClip' => 'text', 1393 ), 1394 ), 1395 $state_styles 1396 ), 1397 ), 1398 ), 1399 ) 1400 ); 1401 1402 return $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ); 1403 } 1404 1405 /** 1406 * @ticket 66094 1407 */ 1408 public function test_breakpoint_escaping_a_text_clip_clears_the_inherited_gradient() { 1409 $stylesheet = $this->get_text_clip_stylesheet( 1410 array( 1411 '@mobile' => array( 1412 'background' => array( 'backgroundClip' => 'border-box' ), 1413 ), 1414 ) 1415 ); 1416 1417 $this->assertStringContainsString( 1418 '@media (width <= 480px){:root :where(p){background-clip: border-box;-webkit-text-fill-color: currentColor;background-image: unset;}}', 1419 $stylesheet 1420 ); 1421 } 1422 1423 /** 1424 * @ticket 66094 1425 */ 1426 public function test_breakpoint_painting_its_own_background_keeps_it() { 1427 $stylesheet = $this->get_text_clip_stylesheet( 1428 array( 1429 '@mobile' => array( 1430 'background' => array( 1431 'gradient' => 'linear-gradient(135deg,#00ff00,#ffff00)', 1432 'backgroundClip' => 'border-box', 1433 ), 1434 ), 1435 ) 1436 ); 1437 1438 $this->assertStringNotContainsString( 'background-image: unset', $stylesheet ); 1439 } 1440 1441 /** 1442 * @ticket 66094 1443 */ 1444 public function test_breakpoint_text_color_under_a_text_clip_gets_its_fill_back() { 1445 $stylesheet = $this->get_text_clip_stylesheet( 1446 array( '@mobile' => array( 'color' => array( 'text' => '#00ff00' ) ) ) 1447 ); 1448 1449 $this->assertStringContainsString( 1450 '@media (width <= 480px){:root :where(p){color: #00ff00;-webkit-text-fill-color: currentColor;}}', 1451 $stylesheet 1452 ); 1453 } 1454 1455 /** 1456 * @ticket 66094 1457 */ 1458 public function test_breakpoint_keeping_the_text_clip_needs_no_escape() { 1459 $stylesheet = $this->get_text_clip_stylesheet( 1460 array( 1461 '@mobile' => array( 1462 'background' => array( 1463 'gradient' => 'linear-gradient(135deg,#00ff00,#ffff00)', 1464 'backgroundClip' => 'text', 1465 ), 1466 ), 1467 ) 1468 ); 1469 1470 $this->assertStringNotContainsString( 'background-image: unset', $stylesheet ); 1471 $this->assertStringNotContainsString( '-webkit-text-fill-color: currentColor', $stylesheet ); 1472 } 1473 1474 /** 1475 * @ticket 66094 1476 */ 1477 public function test_breakpoint_element_styles_read_the_element_clip() { 1478 $theme_json = new WP_Theme_JSON( 1479 array( 1480 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1481 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1482 'styles' => array( 1483 'blocks' => array( 1484 'core/group' => array( 1485 'elements' => array( 1486 'link' => array( 1487 'background' => array( 1488 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1489 'backgroundClip' => 'text', 1490 ), 1491 ), 1492 ), 1493 '@mobile' => array( 1494 'elements' => array( 1495 'link' => array( 'color' => array( 'text' => '#00ff00' ) ), 1496 ), 1497 ), 1498 ), 1499 ), 1500 ), 1501 ) 1502 ); 1503 1504 $stylesheet = $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ); 1505 1506 // The state key sits in the middle of this node's path, not at its end. 1507 $this->assertStringContainsString( 1508 '@media (width <= 480px){:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: #00ff00;-webkit-text-fill-color: currentColor;}}', 1509 $stylesheet 1510 ); 1511 } 1512 1513 /** 1514 * @ticket 66094 1515 */ 1516 public function test_breakpoint_variation_styles_read_the_variation_clip() { 1517 register_block_style( 1518 'core/group', 1519 array( 1520 'name' => 'flashy', 1521 'label' => 'Flashy', 1522 ) 1523 ); 1524 1525 $theme_json = new WP_Theme_JSON( 1526 array( 1527 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1528 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1529 'styles' => array( 1530 'blocks' => array( 1531 'core/group' => array( 1532 'variations' => array( 1533 'flashy' => array( 1534 'background' => array( 1535 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1536 'backgroundClip' => 'text', 1537 ), 1538 '@mobile' => array( 1539 'background' => array( 'backgroundClip' => 'border-box' ), 1540 ), 1541 ), 1542 ), 1543 ), 1544 ), 1545 ), 1546 ) 1547 ); 1548 1549 $actual_styles = $theme_json->get_styles_for_block( 1550 array( 1551 'name' => 'core/group', 1552 'path' => array( 'styles', 'blocks', 'core/group' ), 1553 'selector' => '.wp-block-group', 1554 'css' => '.wp-block-group', 1555 'variations' => array( 1556 array( 1557 'path' => array( 'styles', 'blocks', 'core/group', 'variations', 'flashy' ), 1558 'selector' => '.is-style-flashy.wp-block-group', 1559 ), 1560 ), 1561 ) 1562 ); 1563 1564 unregister_block_style( 'core/group', 'flashy' ); 1565 1566 $this->assertStringContainsString( 1567 '@media (width <= 480px){:root :where(.is-style-flashy.wp-block-group){background-clip: border-box;-webkit-text-fill-color: currentColor;background-image: unset;}}', 1568 $actual_styles 1569 ); 1570 } 1571 1572 /** 1573 * @ticket 66094 1574 */ 1575 public function test_breakpoint_variation_styles_read_the_block_clip() { 1576 register_block_style( 1577 'core/group', 1578 array( 1579 'name' => 'flashy', 1580 'label' => 'Flashy', 1581 ) 1582 ); 1583 1584 $theme_json = new WP_Theme_JSON( 1585 array( 1586 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1587 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1588 'styles' => array( 1589 'blocks' => array( 1590 'core/group' => array( 1591 'background' => array( 1592 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1593 'backgroundClip' => 'text', 1594 ), 1595 'variations' => array( 1596 'flashy' => array( 1597 '@mobile' => array( 1598 'background' => array( 'backgroundClip' => 'border-box' ), 1599 ), 1600 ), 1601 ), 1602 ), 1603 ), 1604 ), 1605 ) 1606 ); 1607 1608 $actual_styles = $theme_json->get_styles_for_block( 1609 array( 1610 'name' => 'core/group', 1611 'path' => array( 'styles', 'blocks', 'core/group' ), 1612 'selector' => '.wp-block-group', 1613 'css' => '.wp-block-group', 1614 'variations' => array( 1615 array( 1616 'path' => array( 'styles', 'blocks', 'core/group', 'variations', 'flashy' ), 1617 'selector' => '.is-style-flashy.wp-block-group', 1618 ), 1619 ), 1620 ) 1621 ); 1622 1623 unregister_block_style( 'core/group', 'flashy' ); 1624 1625 $this->assertStringContainsString( 1626 '@media (width <= 480px){:root :where(.is-style-flashy.wp-block-group){background-clip: border-box;-webkit-text-fill-color: currentColor;background-image: unset;}}', 1627 $actual_styles 1628 ); 1629 } 1630 1631 /** 1632 * @ticket 66094 1633 */ 1634 public function test_variation_pseudo_state_reads_the_block_clip() { 1635 register_block_style( 1636 'core/button', 1637 array( 1638 'name' => 'flashy', 1639 'label' => 'Flashy', 1640 ) 1641 ); 1642 1643 $theme_json = new WP_Theme_JSON( 1644 array( 1645 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1646 'styles' => array( 1647 'blocks' => array( 1648 'core/button' => array( 1649 'background' => array( 1650 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1651 'backgroundClip' => 'text', 1652 ), 1653 'variations' => array( 1654 'flashy' => array( 1655 ':hover' => array( 1656 'background' => array( 'backgroundClip' => 'border-box' ), 1657 ), 1658 ), 1659 ), 1660 ), 1661 ), 1662 ), 1663 ) 1664 ); 1665 1666 $actual_styles = $theme_json->get_styles_for_block( 1667 array( 1668 'name' => 'core/button', 1669 'path' => array( 'styles', 'blocks', 'core/button' ), 1670 'selector' => '.wp-block-button', 1671 'css' => '.wp-block-button', 1672 'variations' => array( 1673 array( 1674 'path' => array( 'styles', 'blocks', 'core/button', 'variations', 'flashy' ), 1675 'selector' => '.is-style-flashy.wp-block-button', 1676 ), 1677 ), 1678 ) 1679 ); 1680 1681 unregister_block_style( 'core/button', 'flashy' ); 1682 1683 $this->assertStringContainsString( 1684 ':root :where(.is-style-flashy.wp-block-button:hover){background-clip: border-box;-webkit-text-fill-color: currentColor;background-image: unset;}', 1685 $actual_styles 1686 ); 1687 } 1688 1689 /** 1690 * @ticket 66094 1691 */ 1692 public function test_variation_breakpoint_pseudo_state_reads_the_block_clip() { 1693 register_block_style( 1694 'core/button', 1695 array( 1696 'name' => 'flashy', 1697 'label' => 'Flashy', 1698 ) 1699 ); 1700 1701 $theme_json = new WP_Theme_JSON( 1702 array( 1703 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1704 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1705 'styles' => array( 1706 'blocks' => array( 1707 'core/button' => array( 1708 'background' => array( 1709 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1710 'backgroundClip' => 'text', 1711 ), 1712 'variations' => array( 1713 'flashy' => array( 1714 '@mobile' => array( 1715 ':hover' => array( 1716 'background' => array( 'backgroundClip' => 'border-box' ), 1717 ), 1718 ), 1719 ), 1720 ), 1721 ), 1722 ), 1723 ), 1724 ) 1725 ); 1726 1727 $actual_styles = $theme_json->get_styles_for_block( 1728 array( 1729 'name' => 'core/button', 1730 'path' => array( 'styles', 'blocks', 'core/button' ), 1731 'selector' => '.wp-block-button', 1732 'css' => '.wp-block-button', 1733 'variations' => array( 1734 array( 1735 'path' => array( 'styles', 'blocks', 'core/button', 'variations', 'flashy' ), 1736 'selector' => '.is-style-flashy.wp-block-button', 1737 ), 1738 ), 1739 ) 1740 ); 1741 1742 unregister_block_style( 'core/button', 'flashy' ); 1743 1744 $this->assertStringContainsString( 1745 '@media (width <= 480px){:root :where(.is-style-flashy.wp-block-button:hover){background-clip: border-box;-webkit-text-fill-color: currentColor;background-image: unset;}}', 1746 $actual_styles 1747 ); 1748 } 1749 1750 /** 1751 * @ticket 66094 1752 */ 1753 public function test_pseudo_state_reads_the_clip_its_own_breakpoint_sets() { 1754 $theme_json = new WP_Theme_JSON( 1755 array( 1756 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1757 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1758 'styles' => array( 1759 'blocks' => array( 1760 'core/button' => array( 1761 'background' => array( 'backgroundClip' => 'border-box' ), 1762 '@mobile' => array( 1763 'background' => array( 1764 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1765 'backgroundClip' => 'text', 1766 ), 1767 ':hover' => array( 'color' => array( 'text' => '#00ff00' ) ), 1768 ), 1769 ), 1770 ), 1771 ), 1772 ) 1773 ); 1774 1775 $stylesheet = $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ); 1776 1777 $this->assertStringContainsString( 1778 '@media (width <= 480px){:root :where(.wp-block-button .wp-block-button__link:hover){color: #00ff00;-webkit-text-fill-color: currentColor;}}', 1779 $stylesheet 1780 ); 1781 } 1782 1783 /** 1784 * @ticket 66094 1785 */ 1786 public function test_pseudo_state_inside_a_breakpoint_reads_the_breakpoint_clip() { 1787 $theme_json = new WP_Theme_JSON( 1788 array( 1789 'version' => WP_Theme_JSON::LATEST_SCHEMA, 1790 'settings' => array( 'viewport' => array( 'mobile' => '480px' ) ), 1791 'styles' => array( 1792 'blocks' => array( 1793 'core/button' => array( 1794 'background' => array( 1795 'gradient' => 'linear-gradient(135deg,#ff0000,#0000ff)', 1796 'backgroundClip' => 'text', 1797 ), 1798 '@mobile' => array( 1799 'background' => array( 'backgroundClip' => 'border-box' ), 1800 ':hover' => array( 'color' => array( 'text' => '#00ff00' ) ), 1801 ), 1802 ), 1803 ), 1804 ), 1805 ) 1806 ); 1807 1808 $stylesheet = $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ); 1809 1810 // The breakpoint already escaped the clip, so no fill is needed. 1811 $this->assertStringContainsString( 1812 '@media (width <= 480px){:root :where(.wp-block-button .wp-block-button__link:hover){color: #00ff00;}}', 1371 1813 $stylesheet 1372 1814 );
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)