Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
76ef139
Block Supports: Add PHP helpers for block support classes and styles.
aaronrobertshaw Oct 8, 2026
c9a8b1c
Block Supports: Fall back to the custom color when a preset attribute…
aaronrobertshaw Oct 21, 2026
06e463e
Tests: Expect the custom color for a null preset, and drop the style …
aaronrobertshaw Oct 21, 2026
86e0dbd
Block Supports: Match JS truthiness for color preset attributes.
aaronrobertshaw Oct 21, 2026
07a6415
Block Supports: Mark the classes and styles helpers as added in 7.3.0.
aaronrobertshaw Oct 30, 2026
7bd0b73
Block Supports: Only treat a non-empty string as a color preset slug.
aaronrobertshaw Oct 30, 2026
aebff9e
Block Supports: Return early for non-array attributes in the backgrou…
aaronrobertshaw Oct 30, 2026
4020b55
Block Supports: Return early for non-array attributes in the shadow a…
aaronrobertshaw Nov 2, 2026
ca3113e
Block Supports: Rename the background helper's return array to $attri…
aaronrobertshaw Nov 2, 2026
7272ad7
Merge branch 'trunk' into backport/php-support-style-helpers
westonruter Nov 4, 2026
fd9ccd3
Block Supports: Match the Gutenberg comments in the border and backgr…
aaronrobertshaw Nov 4, 2026
99a9486
Tests: Match the trimmed Gutenberg helper tests and make their data p…
aaronrobertshaw Nov 4, 2026
2bb0144
Block Supports: Add a helper that returns every support's classes and…
aaronrobertshaw Nov 5, 2026
896ba07
Tests: Add the Trac ticket number to the block supports helper test.
aaronrobertshaw Nov 5, 2026
a2621d6
Block Supports: Narrow the docblock types of the block supports helper.
aaronrobertshaw Nov 6, 2026
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
118 changes: 81 additions & 37 deletions src/wp-includes/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,64 @@ function wp_register_background_support( $block_type ) {
}
}

/**
* Returns background classes and inline styles for block attributes, like the JS
* `getBackgroundClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
* @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
*/
function wp_get_background_classes_and_styles( $block_attributes ) {
if ( ! is_array( $block_attributes ) ) {
return array();
}

$background = $block_attributes['style']['background'] ?? null;

if ( ! is_array( $background ) ) {
return array();
}

$background_styles = array(
'backgroundImage' => $background['backgroundImage'] ?? null,
'backgroundSize' => $background['backgroundSize'] ?? null,
'backgroundPosition' => $background['backgroundPosition'] ?? null,
'backgroundRepeat' => $background['backgroundRepeat'] ?? null,
'backgroundAttachment' => $background['backgroundAttachment'] ?? null,
'gradient' => $background['gradient'] ?? null,
'backgroundClip' => $background['backgroundClip'] ?? null,
);

if ( ! empty( $background_styles['backgroundImage'] ) ) {
$background_styles['backgroundSize'] ??= 'cover';
if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) {
$background_styles['backgroundPosition'] = '50% 50%';
}
}

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

if ( empty( $styles['css'] ) ) {
return array();
}

$attributes = array();

$has_background = ! empty( $background_styles['backgroundImage'] ) || ! empty( $background_styles['gradient'] );

// A text clip paints the glyphs, not the box, so there is no block background.
if ( $has_background && 'text' !== $background_styles['backgroundClip'] ) {
$attributes['class'] = 'has-background';
}

$attributes['style'] = $styles['css'];

return $attributes;
}

/**
* Renders the background styles to the block wrapper.
* This block support uses the `render_block` hook to ensure that
Expand Down Expand Up @@ -91,61 +149,47 @@ function wp_render_background_support( $block_content, $block ) {
return $block_content;
}

$background_styles = array();

if ( ! $skip_background_image ) {
$background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null;
$background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null;
$background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null;
$background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null;
$background_styles['backgroundAttachment'] = $block_attributes['style']['background']['backgroundAttachment'] ?? null;

if ( ! empty( $background_styles['backgroundImage'] ) ) {
$background_styles['backgroundSize'] ??= 'cover';

// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) {
$background_styles['backgroundPosition'] = '50% 50%';
}
// unset() fatals on a string, and the helper ignores one anyway.
if ( is_array( $block_attributes['style']['background'] ) ) {
if ( $skip_background_image ) {
unset(
$block_attributes['style']['background']['backgroundImage'],
$block_attributes['style']['background']['backgroundSize'],
$block_attributes['style']['background']['backgroundPosition'],
$block_attributes['style']['background']['backgroundRepeat'],
$block_attributes['style']['background']['backgroundAttachment']
);
}
}

if ( ! $skip_background_gradient ) {
$background_styles['gradient'] = $block_attributes['style']['background']['gradient'] ?? null;
}
if ( $skip_background_gradient ) {
unset( $block_attributes['style']['background']['gradient'] );
}

if ( ! $skip_background_clip ) {
$background_styles['backgroundClip'] = $block_attributes['style']['background']['backgroundClip'] ?? null;
// Serialized values only, so a skipped clip cannot drop `has-background`.
if ( $skip_background_clip ) {
unset( $block_attributes['style']['background']['backgroundClip'] );
}
}

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

if ( ! empty( $styles['css'] ) ) {
if ( ! empty( $styles['style'] ) ) {
// Inject background styles to the first element, presuming it's the wrapper, if it exists.
$tags = new WP_HTML_Tag_Processor( $block_content );

if ( $tags->next_tag() ) {
$existing_style = $tags->get_attribute( 'style' );
if ( is_string( $existing_style ) && '' !== $existing_style ) {
$separator = str_ends_with( $existing_style, ';' ) ? '' : ';';
$updated_style = "{$existing_style}{$separator}{$styles['css']}";
$updated_style = "{$existing_style}{$separator}{$styles['style']}";
} else {
$updated_style = $styles['css'];
$updated_style = $styles['style'];
}

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

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

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

Expand Down
151 changes: 93 additions & 58 deletions src/wp-includes/block-supports/border.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,76 +52,111 @@ function wp_apply_border_support( $block_type, $block_attributes ) {
return array();
}

$border_block_styles = array();
$has_border_color_support = wp_has_border_feature_support( $block_type, 'color' );
$has_border_width_support = wp_has_border_feature_support( $block_type, 'width' );

// Border radius.
if (
wp_has_border_feature_support( $block_type, 'radius' ) &&
isset( $block_attributes['style']['border']['radius'] ) &&
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'radius' )
) {
$border_radius = $block_attributes['style']['border']['radius'];

if ( is_numeric( $border_radius ) ) {
$border_radius .= 'px';
}
$has_border_color_support = wp_has_border_feature_support( $block_type, 'color' );
$has_border_radius_support = wp_has_border_feature_support( $block_type, 'radius' );
$has_border_style_support = wp_has_border_feature_support( $block_type, 'style' );
$has_border_width_support = wp_has_border_feature_support( $block_type, 'width' );
$skip_color = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' );
$skip_radius = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'radius' );
$skip_style = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' );
$skip_width = wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' );

// The unset() calls below fail on a non-array style or border.
if ( ! isset( $block_attributes['style']['border'] ) || ! is_array( $block_attributes['style']['border'] ) ) {
unset( $block_attributes['style'] );
}

if ( ! $has_border_radius_support || $skip_radius ) {
unset( $block_attributes['style']['border']['radius'] );
}

$border_block_styles['radius'] = $border_radius;
if ( ! $has_border_style_support || $skip_style ) {
unset( $block_attributes['style']['border']['style'] );
}

// Border style.
if (
wp_has_border_feature_support( $block_type, 'style' ) &&
isset( $block_attributes['style']['border']['style'] ) &&
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' )
) {
$border_block_styles['style'] = $block_attributes['style']['border']['style'];
if ( ! $has_border_width_support || $skip_width ) {
unset( $block_attributes['style']['border']['width'] );
}

// Border width.
if (
$has_border_width_support &&
isset( $block_attributes['style']['border']['width'] ) &&
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' )
) {
$border_width = $block_attributes['style']['border']['width'];
if ( ! $has_border_color_support || $skip_color ) {
unset( $block_attributes['style']['border']['color'], $block_attributes['borderColor'] );
}

// Side values follow skipped serialization only, as on trunk.
foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
$has_side = isset( $block_attributes['style']['border'][ $side ] ) && is_array( $block_attributes['style']['border'][ $side ] );

if ( ! $has_side || ( ! $has_border_color_support && ! $has_border_width_support ) ) {
unset( $block_attributes['style']['border'][ $side ] );
continue;
}

if ( $skip_width ) {
unset( $block_attributes['style']['border'][ $side ]['width'] );
}

// This check handles original unitless implementation.
if ( is_numeric( $border_width ) ) {
$border_width .= 'px';
if ( $skip_color ) {
unset( $block_attributes['style']['border'][ $side ]['color'] );
}

$border_block_styles['width'] = $border_width;
}

// Border color.
if (
$has_border_color_support &&
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' )
) {
$preset_border_color = array_key_exists( 'borderColor', $block_attributes ) ? "var:preset|color|{$block_attributes['borderColor']}" : null;
$custom_border_color = $block_attributes['style']['border']['color'] ?? null;
$border_block_styles['color'] = $preset_border_color ? $preset_border_color : $custom_border_color;
}

// Generates styles for individual border sides.
if ( $has_border_color_support || $has_border_width_support ) {
foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
$border = $block_attributes['style']['border'][ $side ] ?? null;
$border_side_values = array(
'width' => isset( $border['width'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' ) ? $border['width'] : null,
'color' => isset( $border['color'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' ) ? $border['color'] : null,
'style' => isset( $border['style'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' ) ? $border['style'] : null,
);
$border_block_styles[ $side ] = $border_side_values;
if ( $skip_style ) {
unset( $block_attributes['style']['border'][ $side ]['style'] );
}
}

// Collect classes and styles.
return wp_get_border_classes_and_styles( $block_attributes );
}

/**
* Returns border classes and inline styles for block attributes, like the JS
* `getBorderClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
* @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
*/
function wp_get_border_classes_and_styles( $block_attributes ) {
if ( ! is_array( $block_attributes ) ) {
return array();
}

$border = isset( $block_attributes['style']['border'] ) && is_array( $block_attributes['style']['border'] )
? $block_attributes['style']['border']
: array();
$border_styles = array();

// Unitless radius and width are from the original implementation.
if ( isset( $border['radius'] ) ) {
$border_styles['radius'] = is_numeric( $border['radius'] ) ? "{$border['radius']}px" : $border['radius'];
}

if ( isset( $border['style'] ) ) {
$border_styles['style'] = $border['style'];
}

if ( isset( $border['width'] ) ) {
$border_styles['width'] = is_numeric( $border['width'] ) ? "{$border['width']}px" : $border['width'];
}

// Preset slugs are strings in theme.json and the editor.
$preset_slug = $block_attributes['borderColor'] ?? null;
$border_styles['color'] = is_string( $preset_slug ) && '' !== $preset_slug ? "var:preset|color|{$preset_slug}" : ( $border['color'] ?? null );

foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
if ( ! isset( $border[ $side ] ) || ! is_array( $border[ $side ] ) ) {
continue;
}
$border_styles[ $side ] = array(
'width' => $border[ $side ]['width'] ?? null,
'color' => $border[ $side ]['color'] ?? null,
'style' => $border[ $side ]['style'] ?? null,
);
}

$attributes = array();
$styles = wp_style_engine_get_styles( array( 'border' => $border_block_styles ) );
$styles = wp_style_engine_get_styles( array( 'border' => $border_styles ) );

if ( ! empty( $styles['classnames'] ) ) {
$attributes['class'] = $styles['classnames'];
Expand Down
Loading
Loading