Skip to content
Draft
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
4 changes: 2 additions & 2 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ Introduce new sections and organize content to help visitors (and search engines

- **Name:** core/heading
- **Category:** text
- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign)
- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, background (backgroundClip, backgroundImage, gradient), className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign)
- **Attributes:** content, level, levelOptions, placeholder

## Home Link
Expand Down Expand Up @@ -606,7 +606,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c

- **Name:** core/paragraph
- **Category:** text
- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign, textColumns, textIndent), ~~className~~
- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, background (backgroundClip, gradient), color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign, textColumns, textIndent), ~~className~~
- **Attributes:** content, direction, dropCap, placeholder

## Pattern Placeholder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Settings related to background.
| backgroundImage | Allow users to set a background image. | `boolean` | `false` |
| backgroundSize | Allow users to set values related to the size of a background image, including size, position, and repeat controls. | `boolean` | `false` |
| gradient | Allow users to set a gradient background. | `boolean` | `false` |
| 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 ]` | |

---

Expand Down
18 changes: 15 additions & 3 deletions lib/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ function gutenberg_render_background_support( $block_content, $block ) {
$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 @@ -55,8 +56,9 @@ function gutenberg_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 @@ -81,6 +83,10 @@ function gutenberg_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 = gutenberg_style_engine_get_styles( array( 'background' => $background_styles ) );

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

$tags->set_attribute( 'style', $updated_style );
$tags->add_class( 'has-background' );
// Skip has-background when the gradient is used as a text fill via
// background-clip: text, as the visual effect is on the text, not the background.
$is_text_gradient = isset( $block_attributes['style']['background']['backgroundClip'] )
&& 'text' === $block_attributes['style']['background']['backgroundClip'];
if ( ! $is_text_gradient ) {
$tags->add_class( 'has-background' );
}
}

return $tags->get_updated_html();
Expand Down
7 changes: 6 additions & 1 deletion lib/block-supports/colors.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ function gutenberg_apply_colors_support( $block_type, $block_attributes ) {
}

$attributes = array();
$styles = gutenberg_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) );
$styles = gutenberg_style_engine_get_styles(
array( 'color' => $color_block_styles ),
array(
'convert_vars_to_classnames' => true,
)
);

if ( ! empty( $styles['classnames'] ) ) {
$attributes['class'] = $styles['classnames'];
Expand Down
29 changes: 29 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ class WP_Theme_JSON_Gutenberg {
'background-repeat' => array( 'background', 'backgroundRepeat' ),
'background-size' => array( 'background', 'backgroundSize' ),
'background-attachment' => array( 'background', 'backgroundAttachment' ),
'background-clip' => array( 'background', 'backgroundClip' ),
'border-radius' => array( 'border', 'radius' ),
'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
Expand Down Expand Up @@ -396,6 +397,7 @@ class WP_Theme_JSON_Gutenberg {
'appearanceTools' => null,
'useRootPaddingAwareAlignments' => null,
'background' => array(
'backgroundClip' => null,
'backgroundImage' => null,
'backgroundSize' => null,
'gradient' => null,
Expand Down Expand Up @@ -526,6 +528,7 @@ class WP_Theme_JSON_Gutenberg {
*/
const VALID_STYLES = array(
'background' => array(
'backgroundClip' => null,
'backgroundImage' => null,
'backgroundAttachment' => null,
'backgroundPosition' => null,
Expand Down Expand Up @@ -2619,6 +2622,32 @@ protected static function compute_style_properties( $styles, $settings = array()
'name' => $css_property,
'value' => $value,
);

// When background-clip is set, add vendor-prefixed properties for
// cross-browser support. For 'text', this clips the background to
// the text and makes it visible via transparent fill. For box-model
// values, unset the fill color to cancel any inherited text gradient.
if ( 'background-clip' === $css_property ) {
if ( 'text' === $value ) {
$declarations[] = array(
'name' => '-webkit-background-clip',
'value' => 'text',
);
$declarations[] = array(
'name' => '-webkit-text-fill-color',
'value' => 'transparent',
);
} else {
$declarations[] = array(
'name' => '-webkit-background-clip',
'value' => 'unset',
);
$declarations[] = array(
'name' => '-webkit-text-fill-color',
'value' => 'unset',
);
}
}
}

// If a variable value is added to the root, the corresponding property should be removed.
Expand Down
26 changes: 26 additions & 0 deletions lib/compat/wordpress-7.0/kses.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,29 @@ function gutenberg_add_display_to_safe_style_css( $attr ) {
return $attr;
}
add_filter( 'safe_style_css', 'gutenberg_add_display_to_safe_style_css' );

/**
* Add background-clip and related vendor-prefixed properties to the list of
* safe CSS properties. Without these, `safecss_filter_attr()` strips the
* declarations generated by the background-clip block support, which causes
* the style engine to return an empty `css` string.
*
* @param string[] $attr List of allowed CSS attributes.
* @return string[] Modified list of allowed CSS attributes.
*/
function gutenberg_add_background_clip_to_safe_style_css( $attr ) {
$background_clip_attrs = array(
'background-clip',
'-webkit-background-clip',
'-webkit-text-fill-color',
);

foreach ( $background_clip_attrs as $background_clip_attr ) {
if ( ! in_array( $background_clip_attr, $attr, true ) ) {
$attr[] = $background_clip_attr;
}
}

return $attr;
}
add_filter( 'safe_style_css', 'gutenberg_add_background_clip_to_safe_style_css' );
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import {
Dropdown,
Button,
__experimentalDropdownContentWrapper as DropdownContentWrapper,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
FlexBlock,
} from '@wordpress/components';
import { useState } from '@wordpress/element';

/**
* External dependencies
*/
import clsx from 'clsx';

export const ALL_BACKGROUND_CLIP_VALUES = [
'border-box',
'padding-box',
'content-box',
'text',
];

const BACKGROUND_CLIP_OPTIONS = [
{
label: __( 'Border box' ),
value: 'border-box',
},
{
label: __( 'Padding box' ),
value: 'padding-box',
},
{
label: __( 'Content box' ),
value: 'content-box',
},
{
label: __( 'Text' ),
value: 'text',
},
];

const BACKGROUND_POPOVER_PROPS = {
placement: 'left-start',
offset: 36,
shift: true,
className: 'block-editor-background-clip-control__popover',
};

function BackgroundClipPreview( { value } ) {
const isText = value === 'text';

return (
<span
aria-hidden="true"
className={ clsx( 'block-editor-background-clip-control__preview', {
'is-text': isText,
'is-padding-box': value === 'padding-box',
'is-content-box': value === 'content-box',
} ) }
>
{ isText && 'Ab' }
</span>
);
}

function BackgroundClipOption( { label, value, isActive, onSelect } ) {
return (
<button
role="option"
aria-label={ label }
aria-selected={ isActive }
className={ clsx( 'block-editor-background-clip-control__option', {
'is-active': isActive,
} ) }
onClick={ onSelect }
>
<BackgroundClipPreview value={ value } />
<span className="block-editor-background-clip-control__option-label">
{ label }
</span>
</button>
);
}

function BackgroundClipToggle( { value, toggleProps } ) {
const activeOption = BACKGROUND_CLIP_OPTIONS.find(
( option ) => option.value === value
);
const label = activeOption ? activeOption.label : __( 'Border box' );

return (
<Button __next40pxDefaultSize { ...toggleProps }>
<HStack className="block-editor-background-clip-control__toggle-inner">
<BackgroundClipPreview value={ value || 'border-box' } />
<FlexBlock>{ label }</FlexBlock>
</HStack>
</Button>
);
}

export default function BackgroundClipControl( {
value,
onChange,
allowedValues,
} ) {
const [ isOpen, setIsOpen ] = useState( false );

const options = allowedValues
? BACKGROUND_CLIP_OPTIONS.filter( ( opt ) =>
allowedValues.includes( opt.value )
)
: BACKGROUND_CLIP_OPTIONS;

return (
<div
className={ clsx(
'block-editor-background-clip-control__container',
{
'is-open': isOpen,
}
) }
>
<Dropdown
popoverProps={ BACKGROUND_POPOVER_PROPS }
renderToggle={ ( { onToggle, isOpen: dropdownIsOpen } ) => {
return (
<BackgroundClipToggle
value={ value }
toggleProps={ {
onClick: () => {
onToggle();
setIsOpen( ! dropdownIsOpen );
},
className:
'block-editor-background-clip-control__dropdown-toggle',
'aria-expanded': dropdownIsOpen,
'aria-label': __( 'Background clip options' ),
} }
/>
);
} }
onClose={ () => setIsOpen( false ) }
renderContent={ () => (
<DropdownContentWrapper
className="block-editor-background-clip-control__dropdown-content-wrapper"
paddingSize="medium"
>
<VStack spacing={ 2 }>
<span className="block-editor-background-clip-control__popover-title">
{ __( 'Background clip' ) }
</span>
<div
role="listbox"
className="block-editor-background-clip-control__options"
aria-label={ __( 'Background clip' ) }
>
{ options.map( ( option ) => (
<BackgroundClipOption
key={ option.value }
label={ option.label }
value={ option.value }
isActive={ value === option.value }
onSelect={ () =>
onChange(
value === option.value
? undefined
: option.value
)
}
/>
) ) }
</div>
</VStack>
</DropdownContentWrapper>
) }
/>
</div>
);
}
Loading
Loading