Skip to content

Commit 5116af7

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 690e426 commit 5116af7

22 files changed

Lines changed: 452 additions & 20 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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ class WP_Theme_JSON_Gutenberg {
265265
'background-repeat' => array( 'background', 'backgroundRepeat' ),
266266
'background-size' => array( 'background', 'backgroundSize' ),
267267
'background-attachment' => array( 'background', 'backgroundAttachment' ),
268+
'background-clip' => array( 'background', 'backgroundClip' ),
268269
'border-radius' => array( 'border', 'radius' ),
269270
'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
270271
'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
@@ -411,6 +412,7 @@ class WP_Theme_JSON_Gutenberg {
411412
'appearanceTools' => null,
412413
'useRootPaddingAwareAlignments' => null,
413414
'background' => array(
415+
'backgroundClip' => null,
414416
'backgroundImage' => null,
415417
'backgroundSize' => null,
416418
'gradient' => null,
@@ -552,6 +554,7 @@ class WP_Theme_JSON_Gutenberg {
552554
*/
553555
const VALID_STYLES = array(
554556
'background' => array(
557+
'backgroundClip' => null,
555558
'backgroundImage' => null,
556559
'backgroundAttachment' => null,
557560
'backgroundPosition' => null,
@@ -3183,6 +3186,31 @@ protected static function compute_style_properties( $styles, $settings = array()
31833186
'name' => $css_property,
31843187
'value' => $value,
31853188
);
3189+
3190+
// When background-clip is set, add vendor-prefixed properties for
3191+
// cross-browser support. For 'text', this clips the background to
3192+
// the text and makes it visible via transparent fill. For box-model
3193+
// values, only the fill color is reset, to cancel any inherited
3194+
// text gradient. `-webkit-background-clip` is an alias of
3195+
// `background-clip` in Chromium, so resetting it there would
3196+
// discard the value set above.
3197+
if ( 'background-clip' === $css_property ) {
3198+
if ( 'text' === $value ) {
3199+
$declarations[] = array(
3200+
'name' => '-webkit-background-clip',
3201+
'value' => 'text',
3202+
);
3203+
$declarations[] = array(
3204+
'name' => '-webkit-text-fill-color',
3205+
'value' => 'transparent',
3206+
);
3207+
} else {
3208+
$declarations[] = array(
3209+
'name' => '-webkit-text-fill-color',
3210+
'value' => 'unset',
3211+
);
3212+
}
3213+
}
31863214
}
31873215

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

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,32 @@ function gutenberg_add_text_shadow_to_safe_style_css( $attr ) {
2020
return $attr;
2121
}
2222
add_filter( 'safe_style_css', 'gutenberg_add_text_shadow_to_safe_style_css' );
23+
24+
/**
25+
* Adds background-clip and its vendor-prefixed companions to the list of safe
26+
* CSS properties.
27+
*
28+
* The {@see safecss_filter_attr()} function only keeps allowlisted CSS
29+
* properties. Without these entries the declarations generated by the
30+
* background clip block support are stripped, leaving the style engine with an
31+
* empty `css` string.
32+
*
33+
* @param string[] $attr Array of allowed CSS attributes.
34+
* @return string[] Modified array of allowed CSS attributes.
35+
*/
36+
function gutenberg_add_background_clip_to_safe_style_css( array $attr ): array {
37+
$background_clip_properties = array(
38+
'background-clip',
39+
'-webkit-background-clip',
40+
'-webkit-text-fill-color',
41+
);
42+
43+
foreach ( $background_clip_properties as $property ) {
44+
if ( ! in_array( $property, $attr, true ) ) {
45+
$attr[] = $property;
46+
}
47+
}
48+
49+
return $attr;
50+
}
51+
add_filter( 'safe_style_css', 'gutenberg_add_background_clip_to_safe_style_css' );

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export function useSettingsForBlockElement(
189189
[ 'backgroundImage', 'backgroundImage' ],
190190
[ 'backgroundSize', 'backgroundSize' ],
191191
[ 'backgroundGradient', 'gradient' ],
192+
[ 'backgroundClip', 'backgroundClip' ],
192193
].forEach( ( [ styleKey, settingKey ] ) => {
193194
if ( ! supportedStyles.includes( styleKey ) ) {
194195
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,
@@ -297,6 +298,7 @@ export function useBlockSettings( name, parentLayout ) {
297298
'background.backgroundImage',
298299
'background.backgroundSize',
299300
'background.gradient',
301+
'background.backgroundClip',
300302
'typography.fontFamilies.custom',
301303
'typography.fontFamilies.default',
302304
'typography.fontFamilies.theme',
@@ -370,6 +372,7 @@ export function useBlockSettings( name, parentLayout ) {
370372
backgroundImage,
371373
backgroundSize,
372374
gradient,
375+
backgroundClip,
373376
},
374377
color: {
375378
palette: {
@@ -466,6 +469,7 @@ export function useBlockSettings( name, parentLayout ) {
466469
backgroundImage,
467470
backgroundSize,
468471
gradient,
472+
backgroundClip,
469473
customFontFamilies,
470474
defaultFontFamilies,
471475
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' ],

‎packages/global-styles-engine/CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### New Features
66

77
- Add text shadow settings and presets ([#79584](https://github.com/WordPress/gutenberg/pull/79584)).
8+
- Accept `background.backgroundClip` as a valid theme.json setting ([#77141](https://github.com/WordPress/gutenberg/pull/77141)).
89

910
## 1.22.0 (2026-09-10)
1011

0 commit comments

Comments
 (0)