Skip to content

Commit 0c5ea30

Browse files
Add background-clip block support infrastructure
Introduces the underlying support for a new backgroundClip property in the block supports system, style engine, and theme.json schema without exposing any editor UI controls. The setting is not an appearance tools opt-in. Themes that want the control must set settings.background.backgroundClip explicitly.
1 parent d47bb85 commit 0c5ea30

23 files changed

Lines changed: 477 additions & 21 deletions

File tree

‎docs/reference-guides/theme-json-reference/theme-json-living.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Settings related to background.
6262
| backgroundImage | Allow users to set a background image. | `boolean` | `false` |
6363
| backgroundSize | Allow users to set values related to the size of a background image, including size, position, and repeat controls. | `boolean` | `false` |
6464
| gradient | Allow users to set a gradient background. | `boolean` | `false` |
65+
| backgroundClip | Allow users to set background clip. Pass true for all methods, or an array of CSS values: ["text", "padding-box", "border-box", "content-box"]. | `boolean`, `[ string ]` | |
6566

6667
---
6768

‎lib/block-supports/background.php‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ function gutenberg_render_background_support( $block_content, $block ) {
4444
$block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
4545
$has_background_image_support = block_has_support( $block_type, array( 'background', 'backgroundImage' ), false );
4646
$has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false );
47+
$has_background_clip_support = block_has_support( $block_type, array( 'background', 'backgroundClip' ), false );
4748

4849
if (
49-
( ! $has_background_image_support && ! $has_background_gradient_support ) ||
50+
( ! $has_background_image_support && ! $has_background_gradient_support && ! $has_background_clip_support ) ||
5051
! isset( $block_attributes['style']['background'] )
5152
) {
5253
return $block_content;
@@ -55,8 +56,9 @@ function gutenberg_render_background_support( $block_content, $block ) {
5556
// Check serialization skip for each feature individually.
5657
$skip_background_image = ! $has_background_image_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' );
5758
$skip_background_gradient = ! $has_background_gradient_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'gradient' );
59+
$skip_background_clip = ! $has_background_clip_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' );
5860

59-
if ( $skip_background_image && $skip_background_gradient ) {
61+
if ( $skip_background_image && $skip_background_gradient && $skip_background_clip ) {
6062
return $block_content;
6163
}
6264

@@ -81,6 +83,10 @@ function gutenberg_render_background_support( $block_content, $block ) {
8183
$background_styles['gradient'] = $block_attributes['style']['background']['gradient'] ?? null;
8284
}
8385

86+
if ( ! $skip_background_clip ) {
87+
$background_styles['backgroundClip'] = $block_attributes['style']['background']['backgroundClip'] ?? null;
88+
}
89+
8490
$styles = gutenberg_style_engine_get_styles( array( 'background' => $background_styles ) );
8591

8692
if ( ! empty( $styles['css'] ) ) {
@@ -97,7 +103,13 @@ function gutenberg_render_background_support( $block_content, $block ) {
97103
}
98104

99105
$tags->set_attribute( 'style', $updated_style );
100-
$tags->add_class( 'has-background' );
106+
// Skip has-background when the gradient is used as a text fill via
107+
// background-clip: text, as the visual effect is on the text, not the background.
108+
$is_text_gradient = isset( $block_attributes['style']['background']['backgroundClip'] )
109+
&& 'text' === $block_attributes['style']['background']['backgroundClip'];
110+
if ( ! $is_text_gradient ) {
111+
$tags->add_class( 'has-background' );
112+
}
101113
}
102114

103115
return $tags->get_updated_html();

‎lib/class-wp-theme-json-gutenberg.php‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ class WP_Theme_JSON_Gutenberg {
255255
'background-repeat' => array( 'background', 'backgroundRepeat' ),
256256
'background-size' => array( 'background', 'backgroundSize' ),
257257
'background-attachment' => array( 'background', 'backgroundAttachment' ),
258+
'background-clip' => array( 'background', 'backgroundClip' ),
258259
'border-radius' => array( 'border', 'radius' ),
259260
'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
260261
'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
@@ -399,6 +400,7 @@ class WP_Theme_JSON_Gutenberg {
399400
'appearanceTools' => null,
400401
'useRootPaddingAwareAlignments' => null,
401402
'background' => array(
403+
'backgroundClip' => null,
402404
'backgroundImage' => null,
403405
'backgroundSize' => null,
404406
'gradient' => null,
@@ -537,6 +539,7 @@ class WP_Theme_JSON_Gutenberg {
537539
*/
538540
const VALID_STYLES = array(
539541
'background' => array(
542+
'backgroundClip' => null,
540543
'backgroundImage' => null,
541544
'backgroundAttachment' => null,
542545
'backgroundPosition' => null,
@@ -3126,6 +3129,32 @@ protected static function compute_style_properties( $styles, $settings = array()
31263129
'name' => $css_property,
31273130
'value' => $value,
31283131
);
3132+
3133+
// When background-clip is set, add vendor-prefixed properties for
3134+
// cross-browser support. For 'text', this clips the background to
3135+
// the text and makes it visible via transparent fill. For box-model
3136+
// values, unset the fill color to cancel any inherited text gradient.
3137+
if ( 'background-clip' === $css_property ) {
3138+
if ( 'text' === $value ) {
3139+
$declarations[] = array(
3140+
'name' => '-webkit-background-clip',
3141+
'value' => 'text',
3142+
);
3143+
$declarations[] = array(
3144+
'name' => '-webkit-text-fill-color',
3145+
'value' => 'transparent',
3146+
);
3147+
} else {
3148+
$declarations[] = array(
3149+
'name' => '-webkit-background-clip',
3150+
'value' => 'unset',
3151+
);
3152+
$declarations[] = array(
3153+
'name' => '-webkit-text-fill-color',
3154+
'value' => 'unset',
3155+
);
3156+
}
3157+
}
31293158
}
31303159

31313160
// If a variable value is added to the root, the corresponding property should be removed.

‎lib/compat/wordpress-7.2/kses.php‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
/**
3+
* Compatibility shims for KSES (content filtering) for WordPress 7.2.
4+
*
5+
* @package gutenberg
6+
*/
7+
8+
/**
9+
* Adds background-clip and its vendor-prefixed companions to the list of safe
10+
* CSS properties.
11+
*
12+
* The {@see safecss_filter_attr()} function only keeps allowlisted CSS
13+
* properties. Without these entries the declarations generated by the
14+
* background clip block support are stripped, leaving the style engine with an
15+
* empty `css` string.
16+
*
17+
* @param string[] $attr Array of allowed CSS attributes.
18+
* @return string[] Modified array of allowed CSS attributes.
19+
*/
20+
function gutenberg_add_background_clip_to_safe_style_css( array $attr ): array {
21+
$background_clip_properties = array(
22+
'background-clip',
23+
'-webkit-background-clip',
24+
'-webkit-text-fill-color',
25+
);
26+
27+
foreach ( $background_clip_properties as $property ) {
28+
if ( ! in_array( $property, $attr, true ) ) {
29+
$attr[] = $property;
30+
}
31+
}
32+
33+
return $attr;
34+
}
35+
add_filter( 'safe_style_css', 'gutenberg_add_background_clip_to_safe_style_css' );

‎lib/load.php‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ function gutenberg_is_experiment_enabled( $name ) {
127127
require __DIR__ . '/compat/wordpress-7.1/preload.php';
128128
require __DIR__ . '/compat/wordpress-7.1/icons.php';
129129

130+
// WordPress 7.2 compat.
131+
require __DIR__ . '/compat/wordpress-7.2/kses.php';
132+
130133
// Experimental features.
131134
require __DIR__ . '/experimental/block-editor-settings-mobile.php';
132135
require __DIR__ . '/experimental/blocks.php';

‎packages/block-editor/src/components/global-styles/hooks.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ export function useSettingsForBlockElement(
188188
[ 'backgroundImage', 'backgroundImage' ],
189189
[ 'backgroundSize', 'backgroundSize' ],
190190
[ 'backgroundGradient', 'gradient' ],
191+
[ 'backgroundClip', 'backgroundClip' ],
191192
].forEach( ( [ styleKey, settingKey ] ) => {
192193
if ( ! supportedStyles.includes( styleKey ) ) {
193194
updatedSettings.background = {

‎packages/block-editor/src/hooks/background.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export function hasBackgroundSupport( blockName, feature = 'any' ) {
4949
!! support?.backgroundImage ||
5050
!! support?.backgroundSize ||
5151
!! support?.backgroundRepeat ||
52-
!! support?.gradient
52+
!! support?.gradient ||
53+
!! support?.backgroundClip
5354
);
5455
}
5556

‎packages/block-editor/src/hooks/style.jsx‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,16 @@ export function getInlineStyles( styles = {} ) {
8484
// The goal is to move everything to server side generated engine styles
8585
// This is temporary as we absorb more and more styles into the engine.
8686
getCSSRules( styles ).forEach( ( rule ) => {
87-
output[ rule.key ] = rule.value;
87+
// Vendor-prefixed CSS properties start with a dash (e.g. `-webkit-background-clip`).
88+
// React style objects require camelCase keys without the leading dash
89+
// (e.g. `WebkitBackgroundClip`), so convert them here.
90+
const key = rule.key.startsWith( '-' )
91+
? rule.key
92+
.slice( 1 )
93+
.replace( /-([a-z])/g, ( _, c ) => c.toUpperCase() )
94+
.replace( /^[a-z]/, ( c ) => c.toUpperCase() )
95+
: rule.key;
96+
output[ key ] = rule.value;
8897
} );
8998

9099
return output;

‎packages/block-editor/src/hooks/utils.jsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ export function useBlockSettings( name, parentLayout ) {
228228
backgroundImage,
229229
backgroundSize,
230230
gradient,
231+
backgroundClip,
231232
customFontFamilies,
232233
defaultFontFamilies,
233234
themeFontFamilies,
@@ -292,6 +293,7 @@ export function useBlockSettings( name, parentLayout ) {
292293
'background.backgroundImage',
293294
'background.backgroundSize',
294295
'background.gradient',
296+
'background.backgroundClip',
295297
'typography.fontFamilies.custom',
296298
'typography.fontFamilies.default',
297299
'typography.fontFamilies.theme',
@@ -360,6 +362,7 @@ export function useBlockSettings( name, parentLayout ) {
360362
backgroundImage,
361363
backgroundSize,
362364
gradient,
365+
backgroundClip,
363366
},
364367
color: {
365368
palette: {
@@ -449,6 +452,7 @@ export function useBlockSettings( name, parentLayout ) {
449452
backgroundImage,
450453
backgroundSize,
451454
gradient,
455+
backgroundClip,
452456
customFontFamilies,
453457
defaultFontFamilies,
454458
themeFontFamilies,

‎packages/blocks/src/api/constants.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
5959
support: [ 'background', 'gradient' ],
6060
useEngine: true,
6161
},
62+
backgroundClip: {
63+
value: [ 'background', 'backgroundClip' ],
64+
support: [ 'background', 'backgroundClip' ],
65+
useEngine: true,
66+
},
6267
borderColor: {
6368
value: [ 'border', 'color' ],
6469
support: [ '__experimentalBorder', 'color' ],

0 commit comments

Comments
 (0)