From 76ef1397930704e1a83ec0cbaef59c1999ecb49e Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 8 Oct 2026 15:13:29 +0800
Subject: [PATCH 01/14] Block Supports: Add PHP helpers for block support
classes and styles.
---
src/wp-includes/block-supports/background.php | 114 +++++++----
src/wp-includes/block-supports/border.php | 150 +++++++++------
src/wp-includes/block-supports/colors.php | 101 ++++++----
src/wp-includes/block-supports/shadow.php | 23 ++-
src/wp-includes/block-supports/spacing.php | 57 ++++--
tests/phpunit/tests/block-supports/border.php | 177 ++++++++++++++++++
tests/phpunit/tests/block-supports/colors.php | 74 ++++++++
.../phpunit/tests/block-supports/spacing.php | 63 +++++++
.../wpGetBackgroundClassesAndStyles.php | 172 +++++++++++++++++
.../wpGetBorderClassesAndStyles.php | 131 +++++++++++++
.../wpGetColorClassesAndStyles.php | 118 ++++++++++++
.../wpGetShadowClassesAndStyles.php | 67 +++++++
.../wpGetSpacingClassesAndStyles.php | 112 +++++++++++
.../wpRenderBackgroundSupport.php | 18 ++
14 files changed, 1229 insertions(+), 148 deletions(-)
create mode 100644 tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
create mode 100644 tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
create mode 100644 tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
create mode 100644 tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
create mode 100644 tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
diff --git a/src/wp-includes/block-supports/background.php b/src/wp-includes/block-supports/background.php
index 8d464cc789ee4..47b361295e3bc 100644
--- a/src/wp-includes/block-supports/background.php
+++ b/src/wp-includes/block-supports/background.php
@@ -34,6 +34,60 @@ 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.2.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 ) {
+ $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();
+ }
+
+ $classes_and_styles = 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'] ) {
+ $classes_and_styles['class'] = 'has-background';
+ }
+
+ $classes_and_styles['style'] = $styles['css'];
+
+ return $classes_and_styles;
+}
+
/**
* Renders the background styles to the block wrapper.
* This block support uses the `render_block` hook to ensure that
@@ -91,36 +145,31 @@ 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%';
- }
+ // The helper ignores a non-array background, so only an array needs filtering.
+ 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 );
@@ -128,24 +177,15 @@ function wp_render_background_support( $block_content, $block ) {
$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'] );
}
}
diff --git a/src/wp-includes/block-supports/border.php b/src/wp-includes/block-supports/border.php
index 89a3ef175e234..e5afd5518998a 100644
--- a/src/wp-includes/block-supports/border.php
+++ b/src/wp-includes/block-supports/border.php
@@ -52,76 +52,110 @@ 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 helper reads only `style.border`, so a non-array value counts as no border styles.
+ 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'] );
+ }
+
+ // Sides are filtered by skipped serialization only, not by feature support.
+ 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.2.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_border_color = array_key_exists( 'borderColor', $block_attributes ) ? "var:preset|color|{$block_attributes['borderColor']}" : null;
+ $border_styles['color'] = $preset_border_color ? $preset_border_color : ( $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'];
diff --git a/src/wp-includes/block-supports/colors.php b/src/wp-includes/block-supports/colors.php
index 3c3396c587439..01b8b4c80b52b 100644
--- a/src/wp-includes/block-supports/colors.php
+++ b/src/wp-includes/block-supports/colors.php
@@ -98,54 +98,87 @@ function wp_apply_colors_support( WP_Block_Type $block_type, array $block_attrib
( isset( $color_support['background'] ) && $color_support['background'] ) ||
( is_array( $color_support ) && ! isset( $color_support['background'] ) );
$has_gradients_support = $color_support['gradients'] ?? false;
- $color_block_styles = array();
+ $skip_text = wp_should_skip_block_supports_serialization( $block_type, 'color', 'text' );
+ $skip_background = wp_should_skip_block_supports_serialization( $block_type, 'color', 'background' );
+ $skip_gradients = wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' );
- // Text colors.
- if ( $has_text_colors_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'text' ) ) {
- $preset_text_color = array_key_exists( 'textColor', $block_attributes ) ? "var:preset|color|{$block_attributes['textColor']}" : null;
- $custom_text_color = $block_attributes['style']['color']['text'] ?? null;
- $color_block_styles['text'] = $preset_text_color ? $preset_text_color : $custom_text_color;
- }
-
- // Background colors.
- if ( $has_background_colors_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'background' ) ) {
- $preset_background_color = array_key_exists( 'backgroundColor', $block_attributes ) ? "var:preset|color|{$block_attributes['backgroundColor']}" : null;
- $custom_background_color = $block_attributes['style']['color']['background'] ?? null;
- $color_block_styles['background'] = $preset_background_color ? $preset_background_color : $custom_background_color;
- }
-
- // Gradients.
- // Suppress color.gradient CSS when background.gradient is supported and
- // explicitly set. background.php owns CSS generation in that case, and
- // emitting the background shorthand here would conflict with it.
+ // background.php owns the CSS when a background gradient is set, so skip color.gradient.
$has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false );
$has_background_gradient_value = ! empty( $block_attributes['style']['background']['gradient'] );
-
- if (
- $has_gradients_support &&
- ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' ) &&
- ! ( $has_background_gradient_support && $has_background_gradient_value )
- ) {
- $preset_gradient_color = array_key_exists( 'gradient', $block_attributes ) ? "var:preset|gradient|{$block_attributes['gradient']}" : null;
- $custom_gradient_color = $block_attributes['style']['color']['gradient'] ?? null;
- $color_block_styles['gradient'] = $preset_gradient_color ? $preset_gradient_color : $custom_gradient_color;
- }
-
- $attributes = array();
- $styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) );
+ $has_background_gradient = $has_background_gradient_support && $has_background_gradient_value;
/*
* Mirror of the background support: a color clipped to the text paints the
* glyphs, not the box, so only a gradient still adds `has-background`.
+ * Read before the attributes are filtered for the helper.
*/
$clips_to_text = 'text' === ( $block_attributes['style']['background']['backgroundClip'] ?? null ) &&
block_has_support( $block_type, array( 'background', 'backgroundClip' ), false ) &&
! wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundClip' );
- if ( $clips_to_text && ! empty( $styles['classnames'] ) && empty( $color_block_styles['gradient'] ) ) {
- $styles['classnames'] = implode( ' ', array_diff( explode( ' ', $styles['classnames'] ), array( 'has-background' ) ) );
+ // The helper reads only `style.color`, so a non-array value counts as no color styles.
+ if ( ! isset( $block_attributes['style']['color'] ) || ! is_array( $block_attributes['style']['color'] ) ) {
+ unset( $block_attributes['style'] );
+ }
+
+ if ( ! $has_text_colors_support || $skip_text ) {
+ unset( $block_attributes['textColor'], $block_attributes['style']['color']['text'] );
+ }
+
+ if ( ! $has_background_colors_support || $skip_background ) {
+ unset( $block_attributes['backgroundColor'], $block_attributes['style']['color']['background'] );
+ }
+
+ if ( ! $has_gradients_support || $skip_gradients || $has_background_gradient ) {
+ unset( $block_attributes['gradient'], $block_attributes['style']['color']['gradient'] );
}
+ $classes_and_styles = wp_get_color_classes_and_styles( $block_attributes );
+ $has_gradient = ! empty( $block_attributes['gradient'] ) || ! empty( $block_attributes['style']['color']['gradient'] );
+
+ if ( $clips_to_text && ! $has_gradient && ! empty( $classes_and_styles['class'] ) ) {
+ $classes = array_diff( explode( ' ', $classes_and_styles['class'] ), array( 'has-background' ) );
+ if ( empty( $classes ) ) {
+ unset( $classes_and_styles['class'] );
+ } else {
+ $classes_and_styles['class'] = implode( ' ', $classes );
+ }
+ }
+
+ return $classes_and_styles;
+}
+
+/**
+ * Returns color classes and inline styles for block attributes, like the JS
+ * `getColorClassesAndStyles()`. Does not check block support or skipped
+ * serialization.
+ *
+ * @since 7.2.0
+ *
+ * @param array $block_attributes Block attributes.
+ * @return array Array with `class` and `style` keys, each present only when non-empty.
+ */
+function wp_get_color_classes_and_styles( $block_attributes ) {
+ if ( ! is_array( $block_attributes ) ) {
+ return array();
+ }
+
+ $color_block_styles = array();
+ $preset_attributes = array(
+ 'text' => array( 'textColor', 'color' ),
+ 'background' => array( 'backgroundColor', 'color' ),
+ 'gradient' => array( 'gradient', 'gradient' ),
+ );
+
+ foreach ( $preset_attributes as $feature => list( $preset_attribute, $preset_type ) ) {
+ $preset_value = array_key_exists( $preset_attribute, $block_attributes ) ? "var:preset|{$preset_type}|{$block_attributes[ $preset_attribute ]}" : null;
+ $custom_value = $block_attributes['style']['color'][ $feature ] ?? null;
+ $color_block_styles[ $feature ] = $preset_value ?? $custom_value;
+ }
+
+ $attributes = array();
+ $styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) );
+
if ( ! empty( $styles['classnames'] ) ) {
$attributes['class'] = $styles['classnames'];
}
diff --git a/src/wp-includes/block-supports/shadow.php b/src/wp-includes/block-supports/shadow.php
index 272eb87407099..0831681574102 100644
--- a/src/wp-includes/block-supports/shadow.php
+++ b/src/wp-includes/block-supports/shadow.php
@@ -60,13 +60,28 @@ function wp_apply_shadow_support( $block_type, $block_attributes ) {
return array();
}
- $shadow_block_styles = array();
+ return wp_get_shadow_classes_and_styles( $block_attributes );
+}
- $custom_shadow = $block_attributes['style']['shadow'] ?? null;
- $shadow_block_styles['shadow'] = $custom_shadow;
+/**
+ * Returns shadow classes and inline styles for block attributes, like the JS
+ * `getShadowClassesAndStyles()`. Does not check block support or skipped
+ * serialization.
+ *
+ * @since 7.2.0
+ *
+ * @param array $block_attributes Block attributes.
+ * @return array Array with `class` and `style` keys, each present only when non-empty.
+ */
+function wp_get_shadow_classes_and_styles( $block_attributes ) {
+ $shadow = $block_attributes['style']['shadow'] ?? null;
$attributes = array();
- $styles = wp_style_engine_get_styles( $shadow_block_styles );
+ $styles = wp_style_engine_get_styles( array( 'shadow' => $shadow ) );
+
+ if ( ! empty( $styles['classnames'] ) ) {
+ $attributes['class'] = $styles['classnames'];
+ }
if ( ! empty( $styles['css'] ) ) {
$attributes['style'] = $styles['css'];
diff --git a/src/wp-includes/block-supports/spacing.php b/src/wp-includes/block-supports/spacing.php
index a778eebeb475a..e7064007360bc 100644
--- a/src/wp-includes/block-supports/spacing.php
+++ b/src/wp-includes/block-supports/spacing.php
@@ -49,28 +49,55 @@ function wp_apply_spacing_support( $block_type, $block_attributes ) {
return array();
}
- $attributes = array();
+ if ( ! isset( $block_attributes['style']['spacing'] ) || ! is_array( $block_attributes['style']['spacing'] ) ) {
+ return array();
+ }
+
$has_padding_support = block_has_support( $block_type, array( 'spacing', 'padding' ), false );
$has_margin_support = block_has_support( $block_type, array( 'spacing', 'margin' ), false );
- $block_styles = $block_attributes['style'] ?? null;
+ $skip_padding = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'padding' );
+ $skip_margin = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'margin' );
- if ( ! $block_styles ) {
- return $attributes;
+ if ( ! $has_padding_support || $skip_padding ) {
+ unset( $block_attributes['style']['spacing']['padding'] );
}
- $skip_padding = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'padding' );
- $skip_margin = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'margin' );
- $spacing_block_styles = array(
- 'padding' => null,
- 'margin' => null,
- );
- if ( $has_padding_support && ! $skip_padding ) {
- $spacing_block_styles['padding'] = $block_styles['spacing']['padding'] ?? null;
+ if ( ! $has_margin_support || $skip_margin ) {
+ unset( $block_attributes['style']['spacing']['margin'] );
+ }
+
+ return wp_get_spacing_classes_and_styles( $block_attributes );
+}
+
+/**
+ * Returns spacing classes and inline styles for block attributes, like the JS
+ * `getSpacingClassesAndStyles()`. Does not check block support or skipped
+ * serialization.
+ *
+ * @since 7.2.0
+ *
+ * @param array $block_attributes Block attributes.
+ * @return array Array with `class` and `style` keys, each present only when non-empty.
+ */
+function wp_get_spacing_classes_and_styles( $block_attributes ) {
+ $spacing = $block_attributes['style']['spacing'] ?? null;
+ if ( ! is_array( $spacing ) ) {
+ return array();
}
- if ( $has_margin_support && ! $skip_margin ) {
- $spacing_block_styles['margin'] = $block_styles['spacing']['margin'] ?? null;
+
+ $styles = wp_style_engine_get_styles(
+ array(
+ 'spacing' => array(
+ 'padding' => $spacing['padding'] ?? null,
+ 'margin' => $spacing['margin'] ?? null,
+ ),
+ )
+ );
+ $attributes = array();
+
+ if ( ! empty( $styles['classnames'] ) ) {
+ $attributes['class'] = $styles['classnames'];
}
- $styles = wp_style_engine_get_styles( array( 'spacing' => $spacing_block_styles ) );
if ( ! empty( $styles['css'] ) ) {
$attributes['style'] = $styles['css'];
diff --git a/tests/phpunit/tests/block-supports/border.php b/tests/phpunit/tests/block-supports/border.php
index cd1c1def70e26..2bab64e9741c6 100644
--- a/tests/phpunit/tests/block-supports/border.php
+++ b/tests/phpunit/tests/block-supports/border.php
@@ -21,6 +21,35 @@ public function tear_down() {
parent::tear_down();
}
+ /**
+ * Registers a new block for testing border support.
+ *
+ * @param string $block_name Name for the test block.
+ * @param array $supports Array defining block support configuration.
+ * @return WP_Block_Type The block type for the newly registered test block.
+ */
+ private function register_bordered_block_with_support( $block_name, $supports = array() ) {
+ $this->test_block_name = $block_name;
+ register_block_type(
+ $this->test_block_name,
+ array(
+ 'api_version' => 3,
+ 'attributes' => array(
+ 'borderColor' => array(
+ 'type' => 'string',
+ ),
+ 'style' => array(
+ 'type' => 'object',
+ ),
+ ),
+ 'supports' => $supports,
+ )
+ );
+ $registry = WP_Block_Type_Registry::get_instance();
+
+ return $registry->get_registered( $this->test_block_name );
+ }
+
/**
* @ticket 55505
*/
@@ -159,4 +188,152 @@ public function test_radius_with_individual_skipped_serialization_block_supports
$this->assertSame( $expected, $actual );
}
+
+ /**
+ * @ticket 66269
+ */
+ public function test_border_color_preset_with_skipped_color_serialization() {
+ $block_type = $this->register_bordered_block_with_support(
+ 'test/border-color-preset-with-skipped-color-serialization',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ 'width' => true,
+ '__experimentalSkipSerialization' => array( 'color' ),
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'borderColor' => 'red',
+ 'style' => array( 'border' => array( 'width' => '1px' ) ),
+ );
+
+ $this->assertSame(
+ array( 'style' => 'border-width:1px;' ),
+ wp_apply_border_support( $block_type, $block_attrs )
+ );
+ }
+
+ /**
+ * @ticket 66269
+ */
+ public function test_split_borders_with_color_support_only_keeps_side_style() {
+ $block_type = $this->register_bordered_block_with_support(
+ 'test/split-borders-with-color-support-only',
+ array(
+ '__experimentalBorder' => array(
+ 'color' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'style' => 'solid',
+ 'top' => array(
+ 'color' => '#72aee6',
+ 'style' => 'dashed',
+ ),
+ ),
+ ),
+ );
+ $actual = wp_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'style' => 'border-top-color:#72aee6;border-top-style:dashed;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * @ticket 66269
+ */
+ public function test_split_borders_with_width_support_only_keeps_side_color() {
+ $block_type = $this->register_bordered_block_with_support(
+ 'test/split-borders-with-width-support-only',
+ array(
+ '__experimentalBorder' => array(
+ 'width' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'borderColor' => 'red',
+ 'style' => array(
+ 'border' => array(
+ 'top' => array(
+ 'color' => '#72aee6',
+ 'width' => '2px',
+ ),
+ ),
+ ),
+ );
+ $actual = wp_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'style' => 'border-top-width:2px;border-top-color:#72aee6;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * @ticket 66269
+ */
+ public function test_split_borders_without_color_or_width_support_are_dropped() {
+ $block_type = $this->register_bordered_block_with_support(
+ 'test/split-borders-without-color-or-width-support',
+ array(
+ '__experimentalBorder' => array(
+ 'radius' => true,
+ 'style' => true,
+ ),
+ )
+ );
+ $block_attrs = array(
+ 'style' => array(
+ 'border' => array(
+ 'radius' => '5px',
+ 'top' => array(
+ 'style' => 'dashed',
+ 'width' => '2px',
+ ),
+ ),
+ ),
+ );
+ $actual = wp_apply_border_support( $block_type, $block_attrs );
+ $expected = array(
+ 'style' => 'border-radius:5px;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * @ticket 66269
+ */
+ public function test_non_array_border_and_sides_are_ignored() {
+ $block_type = $this->register_bordered_block_with_support(
+ 'test/non-array-border-and-sides',
+ array(
+ '__experimentalBorder' => true,
+ )
+ );
+
+ $this->assertSame( array(), wp_apply_border_support( $block_type, array( 'style' => 'invalid' ) ) );
+ $this->assertSame( array(), wp_apply_border_support( $block_type, array( 'style' => array( 'border' => 'invalid' ) ) ) );
+ $this->assertSame(
+ array( 'style' => 'border-bottom-width:1px;' ),
+ wp_apply_border_support(
+ $block_type,
+ array(
+ 'style' => array(
+ 'border' => array(
+ 'top' => 'invalid',
+ 'bottom' => array( 'width' => '1px' ),
+ ),
+ ),
+ )
+ )
+ );
+ }
}
diff --git a/tests/phpunit/tests/block-supports/colors.php b/tests/phpunit/tests/block-supports/colors.php
index 243a9a9721c55..86a22402c9cb4 100644
--- a/tests/phpunit/tests/block-supports/colors.php
+++ b/tests/phpunit/tests/block-supports/colors.php
@@ -407,4 +407,78 @@ public static function data_background_color_has_background_with_a_clip() {
),
);
}
+
+ /**
+ * @ticket 66269
+ */
+ public function test_color_support_true_applies_text_and_background_colors() {
+ $this->test_block_name = 'test/color-support-true';
+ register_block_type(
+ $this->test_block_name,
+ array(
+ 'api_version' => 3,
+ 'attributes' => array(
+ 'textColor' => array(
+ 'type' => 'string',
+ ),
+ 'backgroundColor' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'supports' => array(
+ 'color' => true,
+ ),
+ )
+ );
+
+ $registry = WP_Block_Type_Registry::get_instance();
+ $block_type = $registry->get_registered( $this->test_block_name );
+
+ $block_atts = array(
+ 'textColor' => 'contrast',
+ 'backgroundColor' => 'base',
+ );
+
+ $actual = wp_apply_colors_support( $block_type, $block_atts );
+ $expected = array(
+ 'class' => 'has-text-color has-contrast-color has-background has-base-background-color',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * @ticket 66269
+ */
+ public function test_color_text_support_disabled_skips_text_color() {
+ $this->test_block_name = 'test/color-text-support-disabled';
+ register_block_type(
+ $this->test_block_name,
+ array(
+ 'api_version' => 3,
+ 'attributes' => array(
+ 'textColor' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'supports' => array(
+ 'color' => array(
+ 'text' => false,
+ ),
+ ),
+ )
+ );
+
+ $registry = WP_Block_Type_Registry::get_instance();
+ $block_type = $registry->get_registered( $this->test_block_name );
+
+ $block_atts = array(
+ 'textColor' => 'contrast',
+ );
+
+ $actual = wp_apply_colors_support( $block_type, $block_atts );
+ $expected = array();
+
+ $this->assertSame( $expected, $actual );
+ }
}
diff --git a/tests/phpunit/tests/block-supports/spacing.php b/tests/phpunit/tests/block-supports/spacing.php
index 2cfcd76d99af8..ac50818491655 100644
--- a/tests/phpunit/tests/block-supports/spacing.php
+++ b/tests/phpunit/tests/block-supports/spacing.php
@@ -164,4 +164,67 @@ public function test_margin_with_individual_skipped_serialization_block_supports
$this->assertSame( $expected, $actual );
}
+
+ /**
+ * @ticket 66269
+ */
+ public function test_unsupported_padding_is_not_applied() {
+ $this->test_block_name = 'test/unsupported-padding-is-not-applied';
+ register_block_type(
+ $this->test_block_name,
+ array(
+ 'api_version' => 3,
+ 'attributes' => array(
+ 'style' => array(
+ 'type' => 'object',
+ ),
+ ),
+ 'supports' => array(
+ 'spacing' => array(
+ 'margin' => true,
+ ),
+ ),
+ )
+ );
+ $registry = WP_Block_Type_Registry::get_instance();
+ $block_type = $registry->get_registered( $this->test_block_name );
+ $block_atts = array(
+ 'style' => array(
+ 'spacing' => array(
+ 'margin' => '1px',
+ 'padding' => '2px',
+ ),
+ ),
+ );
+
+ $actual = wp_apply_spacing_support( $block_type, $block_atts );
+ $expected = array(
+ 'style' => 'margin:1px;',
+ );
+
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * @ticket 66269
+ */
+ public function test_spacing_without_style_attribute() {
+ $this->test_block_name = 'test/spacing-without-style-attribute';
+ register_block_type(
+ $this->test_block_name,
+ array(
+ 'api_version' => 3,
+ 'supports' => array(
+ 'spacing' => array(
+ 'margin' => true,
+ 'padding' => true,
+ ),
+ ),
+ )
+ );
+ $registry = WP_Block_Type_Registry::get_instance();
+ $block_type = $registry->get_registered( $this->test_block_name );
+
+ $this->assertSame( array(), wp_apply_spacing_support( $block_type, array() ) );
+ }
}
diff --git a/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
new file mode 100644
index 0000000000000..45ee6e62df673
--- /dev/null
+++ b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
@@ -0,0 +1,172 @@
+assertSame( $expected, wp_get_background_classes_and_styles( $block_attributes ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public function data_get_background_classes_and_styles() {
+ return array(
+ 'custom image gets default size' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ),
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-background',
+ 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;",
+ ),
+ ),
+ 'contain without position is centered' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ),
+ 'backgroundSize' => 'contain',
+ 'backgroundRepeat' => 'no-repeat',
+ 'backgroundAttachment' => 'fixed',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-background',
+ 'style' => "background-image:url('https://example.com/image.jpg');background-position:50% 50%;background-repeat:no-repeat;background-size:contain;background-attachment:fixed;",
+ ),
+ ),
+ 'preset gradient' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'gradient' => 'var:preset|gradient|vivid-cyan-blue-to-vivid-purple',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-background',
+ 'style' => 'background-image:var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple);',
+ ),
+ ),
+ 'gradient clipped to text has no class' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'gradient' => 'linear-gradient(red, blue)',
+ 'backgroundClip' => 'text',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'background-image:linear-gradient(red, blue);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;',
+ ),
+ ),
+ 'clip alone has no class' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'backgroundClip' => 'padding-box',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'background-clip:padding-box;-webkit-text-fill-color:currentColor;',
+ ),
+ ),
+ 'image clipped to text has no class' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ),
+ 'backgroundClip' => 'text',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;",
+ ),
+ ),
+ 'image without url gets default size' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'backgroundImage' => array( 'id' => 1 ),
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-background',
+ 'style' => 'background-size:cover;',
+ ),
+ ),
+ 'numeric position is not output' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ),
+ 'backgroundPosition' => 50,
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-background',
+ 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;",
+ ),
+ ),
+ 'zero gradient outputs nothing' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(
+ 'gradient' => 0,
+ ),
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'empty background' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => array(),
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'no style attribute' => array(
+ 'block_attributes' => array(),
+ 'expected' => array(),
+ ),
+ 'background is not an array' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'background' => 'linear-gradient(red, blue)',
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'attributes are not an array' => array(
+ 'block_attributes' => 'style',
+ 'expected' => array(),
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
new file mode 100644
index 0000000000000..1707f2d71d27e
--- /dev/null
+++ b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
@@ -0,0 +1,131 @@
+assertSame( $expected, wp_get_border_classes_and_styles( $block_attributes ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public function data_get_border_classes_and_styles() {
+ return array(
+ 'custom' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'border' => array(
+ 'radius' => '10px',
+ 'style' => 'solid',
+ 'width' => '2px',
+ 'color' => '#ff0000',
+ 'top' => array(
+ 'style' => 'dashed',
+ 'color' => '#00ff00',
+ 'width' => '3px',
+ ),
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-border-color',
+ 'style' => 'border-color:#ff0000;border-radius:10px;border-style:solid;border-width:2px;border-top-width:3px;border-top-color:#00ff00;border-top-style:dashed;',
+ ),
+ ),
+ 'preset wins over custom' => array(
+ 'block_attributes' => array(
+ 'borderColor' => 'red',
+ 'style' => array(
+ 'border' => array(
+ 'color' => '#ff0000',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-border-color has-red-border-color',
+ ),
+ ),
+ 'preset only' => array(
+ 'block_attributes' => array(
+ 'borderColor' => 'red',
+ ),
+ 'expected' => array(
+ 'class' => 'has-border-color has-red-border-color',
+ ),
+ ),
+ 'numeric' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'border' => array(
+ 'radius' => 5,
+ 'width' => 2,
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'border-radius:5px;border-width:2px;',
+ ),
+ ),
+ 'zero' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'border' => array(
+ 'radius' => 0,
+ 'width' => 0,
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'border-radius:0px;border-width:0px;',
+ ),
+ ),
+ 'empty' => array(
+ 'block_attributes' => array(),
+ 'expected' => array(),
+ ),
+ 'malformed border' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'border' => 'solid',
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'malformed side' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'border' => array(
+ 'top' => '1px solid',
+ ),
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'malformed style' => array(
+ 'block_attributes' => array(
+ 'style' => 'border:1px solid',
+ ),
+ 'expected' => array(),
+ ),
+ 'malformed non-array' => array(
+ 'block_attributes' => 'border',
+ 'expected' => array(),
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
new file mode 100644
index 0000000000000..df998dac26409
--- /dev/null
+++ b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
@@ -0,0 +1,118 @@
+assertSame( $expected, wp_get_color_classes_and_styles( $block_attributes ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public function data_get_color_classes_and_styles() {
+ return array(
+ 'custom' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'color' => array(
+ 'text' => '#d92828',
+ 'background' => '#ffffff',
+ 'gradient' => 'linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%)',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-text-color has-background',
+ 'style' => 'color:#d92828;background-color:#ffffff;background:linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);',
+ ),
+ ),
+ 'preset' => array(
+ 'block_attributes' => array(
+ 'textColor' => 'contrast',
+ 'backgroundColor' => 'base',
+ 'gradient' => 'vivid-cyan-blue-to-vivid-purple',
+ ),
+ 'expected' => array(
+ 'class' => 'has-text-color has-contrast-color has-background has-base-background-color has-vivid-cyan-blue-to-vivid-purple-gradient-background',
+ ),
+ ),
+ 'preset wins over custom' => array(
+ 'block_attributes' => array(
+ 'textColor' => 'contrast',
+ 'style' => array(
+ 'color' => array(
+ 'text' => '#d92828',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-text-color has-contrast-color',
+ ),
+ ),
+ 'null preset wins over custom' => array(
+ 'block_attributes' => array(
+ 'textColor' => null,
+ 'style' => array(
+ 'color' => array(
+ 'text' => '#d92828',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-text-color',
+ ),
+ ),
+ 'numeric' => array(
+ 'block_attributes' => array(
+ 'textColor' => 1,
+ ),
+ 'expected' => array(
+ 'class' => 'has-text-color has-1-color',
+ ),
+ ),
+ 'zero' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'color' => array(
+ 'text' => '0',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'color:0;',
+ ),
+ ),
+ 'empty' => array(
+ 'block_attributes' => array(),
+ 'expected' => array(),
+ ),
+ 'malformed color' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'color' => '#d92828',
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'malformed attributes' => array(
+ 'block_attributes' => 'color',
+ 'expected' => array(),
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
new file mode 100644
index 0000000000000..9034c53b71570
--- /dev/null
+++ b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
@@ -0,0 +1,67 @@
+assertSame( $expected, wp_get_shadow_classes_and_styles( $block_attributes ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public function data_get_shadow_classes_and_styles() {
+ return array(
+ 'custom shadow' => array(
+ 'block_attributes' => array( 'style' => array( 'shadow' => '1px 1px 1px #000' ) ),
+ 'expected' => array( 'style' => 'box-shadow:1px 1px 1px #000;' ),
+ ),
+ 'preset shadow' => array(
+ 'block_attributes' => array( 'style' => array( 'shadow' => 'var:preset|shadow|natural' ) ),
+ 'expected' => array( 'style' => 'box-shadow:var(--wp--preset--shadow--natural);' ),
+ ),
+ 'numeric shadow' => array(
+ 'block_attributes' => array( 'style' => array( 'shadow' => 5 ) ),
+ 'expected' => array(),
+ ),
+ 'zero shadow' => array(
+ 'block_attributes' => array( 'style' => array( 'shadow' => 0 ) ),
+ 'expected' => array(),
+ ),
+ 'empty shadow' => array(
+ 'block_attributes' => array( 'style' => array( 'shadow' => '' ) ),
+ 'expected' => array(),
+ ),
+ 'no style attribute' => array(
+ 'block_attributes' => array(),
+ 'expected' => array(),
+ ),
+ 'non-array style' => array(
+ 'block_attributes' => array( 'style' => 'box-shadow:1px 1px 1px #000' ),
+ 'expected' => array(),
+ ),
+ 'array shadow' => array(
+ 'block_attributes' => array( 'style' => array( 'shadow' => array( '1px 1px 1px #000' ) ) ),
+ 'expected' => array(),
+ ),
+ 'non-array attributes' => array(
+ 'block_attributes' => null,
+ 'expected' => array(),
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
new file mode 100644
index 0000000000000..cc10e9b24e908
--- /dev/null
+++ b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
@@ -0,0 +1,112 @@
+assertSame( $expected, wp_get_spacing_classes_and_styles( $block_attributes ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public function data_get_spacing_classes_and_styles() {
+ return array(
+ 'custom values' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'padding' => array(
+ 'top' => '1px',
+ 'left' => '2em',
+ ),
+ 'margin' => '3px',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'padding-top:1px;padding-left:2em;margin:3px;',
+ ),
+ ),
+ 'presets' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => array(
+ 'padding' => 'var:preset|spacing|40',
+ 'margin' => array( 'bottom' => 'var:preset|spacing|20' ),
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'padding:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--20);',
+ ),
+ ),
+ 'block gap is ignored' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => array( 'blockGap' => '2em' ),
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'numeric values' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => array( 'padding' => array( 'top' => 10 ) ),
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'zero' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => array( 'margin' => '0' ),
+ ),
+ ),
+ 'expected' => array(
+ 'style' => 'margin:0;',
+ ),
+ ),
+ 'empty spacing' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => array(),
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'no style' => array(
+ 'block_attributes' => array(),
+ 'expected' => array(),
+ ),
+ 'malformed spacing' => array(
+ 'block_attributes' => array(
+ 'style' => array(
+ 'spacing' => '10px',
+ ),
+ ),
+ 'expected' => array(),
+ ),
+ 'malformed style' => array(
+ 'block_attributes' => array(
+ 'style' => 'padding:10px',
+ ),
+ 'expected' => array(),
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php b/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php
index 71d8a7aae4431..e8c17767b41b8 100644
--- a/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php
+++ b/tests/phpunit/tests/block-supports/wpRenderBackgroundSupport.php
@@ -86,6 +86,7 @@ public function filter_set_theme_root() {
* @ticket 61858
* @ticket 64974
* @ticket 66094
+ * @ticket 66269
*
* @covers ::wp_render_background_support
*
@@ -284,6 +285,23 @@ public static function data_background_block_support() {
'expected_wrapper' => '
Content
',
'wrapper' => 'Content
',
),
+ 'background gradient is applied without the image when the image skips serialization' => array(
+ 'theme_name' => 'block-theme-child-with-fluid-typography',
+ 'block_name' => 'test/background-image-skipped-gradient-applied',
+ 'background_settings' => array(
+ 'backgroundImage' => true,
+ 'gradient' => true,
+ '__experimentalSkipSerialization' => array( 'backgroundImage' ),
+ ),
+ 'background_style' => array(
+ 'backgroundImage' => array(
+ 'url' => 'https://example.com/image.jpg',
+ ),
+ 'gradient' => 'linear-gradient(135deg,rgb(255,0,0) 0%,rgb(0,0,255) 100%)',
+ ),
+ 'expected_wrapper' => 'Content
',
+ 'wrapper' => 'Content
',
+ ),
'background image style is not applied if the block does not support background image' => array(
'theme_name' => 'block-theme-child-with-fluid-typography',
'block_name' => 'test/background-rules-are-not-output',
From c9a8b1cf2edea9c66f61b3e29eb981c81cbad2ab Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 21 Oct 2026 15:01:33 +0800
Subject: [PATCH 02/14] Block Supports: Fall back to the custom color when a
preset attribute is null or empty.
---
src/wp-includes/block-supports/colors.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/wp-includes/block-supports/colors.php b/src/wp-includes/block-supports/colors.php
index 01b8b4c80b52b..c9b25cc3c64dd 100644
--- a/src/wp-includes/block-supports/colors.php
+++ b/src/wp-includes/block-supports/colors.php
@@ -171,7 +171,8 @@ function wp_get_color_classes_and_styles( $block_attributes ) {
);
foreach ( $preset_attributes as $feature => list( $preset_attribute, $preset_type ) ) {
- $preset_value = array_key_exists( $preset_attribute, $block_attributes ) ? "var:preset|{$preset_type}|{$block_attributes[ $preset_attribute ]}" : null;
+ $preset_slug = $block_attributes[ $preset_attribute ] ?? null;
+ $preset_value = is_string( $preset_slug ) && '' !== $preset_slug ? "var:preset|{$preset_type}|{$preset_slug}" : null;
$custom_value = $block_attributes['style']['color'][ $feature ] ?? null;
$color_block_styles[ $feature ] = $preset_value ?? $custom_value;
}
From 06e463e829c28a0d39139582cad24806b911fdea Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 21 Oct 2026 15:01:33 +0800
Subject: [PATCH 03/14] Tests: Expect the custom color for a null preset, and
drop the style engine cases.
---
.../wpGetColorClassesAndStyles.php | 23 ++-----------------
1 file changed, 2 insertions(+), 21 deletions(-)
diff --git a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
index df998dac26409..8fdb2711bfa46 100644
--- a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
@@ -64,7 +64,7 @@ public function data_get_color_classes_and_styles() {
'class' => 'has-text-color has-contrast-color',
),
),
- 'null preset wins over custom' => array(
+ 'null preset uses custom text' => array(
'block_attributes' => array(
'textColor' => null,
'style' => array(
@@ -75,26 +75,7 @@ public function data_get_color_classes_and_styles() {
),
'expected' => array(
'class' => 'has-text-color',
- ),
- ),
- 'numeric' => array(
- 'block_attributes' => array(
- 'textColor' => 1,
- ),
- 'expected' => array(
- 'class' => 'has-text-color has-1-color',
- ),
- ),
- 'zero' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'color' => array(
- 'text' => '0',
- ),
- ),
- ),
- 'expected' => array(
- 'style' => 'color:0;',
+ 'style' => 'color:#d92828;',
),
),
'empty' => array(
From 86e0dbd10783048044e3c5a72a399c212c61a1d6 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 21 Oct 2026 15:11:17 +0800
Subject: [PATCH 04/14] Block Supports: Match JS truthiness for color preset
attributes.
---
src/wp-includes/block-supports/colors.php | 13 +++++++++++--
.../block-supports/wpGetColorClassesAndStyles.php | 8 ++++++++
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/src/wp-includes/block-supports/colors.php b/src/wp-includes/block-supports/colors.php
index c9b25cc3c64dd..d6b9264091937 100644
--- a/src/wp-includes/block-supports/colors.php
+++ b/src/wp-includes/block-supports/colors.php
@@ -171,8 +171,17 @@ function wp_get_color_classes_and_styles( $block_attributes ) {
);
foreach ( $preset_attributes as $feature => list( $preset_attribute, $preset_type ) ) {
- $preset_slug = $block_attributes[ $preset_attribute ] ?? null;
- $preset_value = is_string( $preset_slug ) && '' !== $preset_slug ? "var:preset|{$preset_type}|{$preset_slug}" : null;
+ $preset_slug = $block_attributes[ $preset_attribute ] ?? null;
+ if ( true === $preset_slug ) {
+ $preset_slug = 'true';
+ }
+
+ // Mirrors JS truthiness, so '0' is a slug. Arrays cannot form a class name.
+ $has_preset = is_string( $preset_slug )
+ ? '' !== $preset_slug
+ : ( is_int( $preset_slug ) || is_float( $preset_slug ) ) && 0.0 !== (float) $preset_slug;
+
+ $preset_value = $has_preset ? "var:preset|{$preset_type}|{$preset_slug}" : null;
$custom_value = $block_attributes['style']['color'][ $feature ] ?? null;
$color_block_styles[ $feature ] = $preset_value ?? $custom_value;
}
diff --git a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
index 8fdb2711bfa46..52d6e319d9162 100644
--- a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
@@ -78,6 +78,14 @@ public function data_get_color_classes_and_styles() {
'style' => 'color:#d92828;',
),
),
+ 'numeric preset' => array(
+ 'block_attributes' => array(
+ 'textColor' => 1,
+ ),
+ 'expected' => array(
+ 'class' => 'has-text-color has-1-color',
+ ),
+ ),
'empty' => array(
'block_attributes' => array(),
'expected' => array(),
From 07a641523c7593e68e4d235d05a3f0b91c8b316b Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 30 Oct 2026 17:26:31 +0800
Subject: [PATCH 05/14] Block Supports: Mark the classes and styles helpers as
added in 7.3.0.
---
src/wp-includes/block-supports/background.php | 2 +-
src/wp-includes/block-supports/border.php | 2 +-
src/wp-includes/block-supports/colors.php | 2 +-
src/wp-includes/block-supports/shadow.php | 2 +-
src/wp-includes/block-supports/spacing.php | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/wp-includes/block-supports/background.php b/src/wp-includes/block-supports/background.php
index 47b361295e3bc..4d8d1914ba1fa 100644
--- a/src/wp-includes/block-supports/background.php
+++ b/src/wp-includes/block-supports/background.php
@@ -39,7 +39,7 @@ function wp_register_background_support( $block_type ) {
* `getBackgroundClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
- * @since 7.2.0
+ * @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
diff --git a/src/wp-includes/block-supports/border.php b/src/wp-includes/block-supports/border.php
index e5afd5518998a..ea2b39d8e0167 100644
--- a/src/wp-includes/block-supports/border.php
+++ b/src/wp-includes/block-supports/border.php
@@ -112,7 +112,7 @@ function wp_apply_border_support( $block_type, $block_attributes ) {
* `getBorderClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
- * @since 7.2.0
+ * @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
diff --git a/src/wp-includes/block-supports/colors.php b/src/wp-includes/block-supports/colors.php
index d6b9264091937..d03af90cf5f7f 100644
--- a/src/wp-includes/block-supports/colors.php
+++ b/src/wp-includes/block-supports/colors.php
@@ -153,7 +153,7 @@ function wp_apply_colors_support( WP_Block_Type $block_type, array $block_attrib
* `getColorClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
- * @since 7.2.0
+ * @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
diff --git a/src/wp-includes/block-supports/shadow.php b/src/wp-includes/block-supports/shadow.php
index 0831681574102..3e2ad26886f96 100644
--- a/src/wp-includes/block-supports/shadow.php
+++ b/src/wp-includes/block-supports/shadow.php
@@ -68,7 +68,7 @@ function wp_apply_shadow_support( $block_type, $block_attributes ) {
* `getShadowClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
- * @since 7.2.0
+ * @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
diff --git a/src/wp-includes/block-supports/spacing.php b/src/wp-includes/block-supports/spacing.php
index e7064007360bc..03675fc2fecc5 100644
--- a/src/wp-includes/block-supports/spacing.php
+++ b/src/wp-includes/block-supports/spacing.php
@@ -74,7 +74,7 @@ function wp_apply_spacing_support( $block_type, $block_attributes ) {
* `getSpacingClassesAndStyles()`. Does not check block support or skipped
* serialization.
*
- * @since 7.2.0
+ * @since 7.3.0
*
* @param array $block_attributes Block attributes.
* @return array Array with `class` and `style` keys, each present only when non-empty.
From 7bd0b737b5dfda974c170b7b5197b843396e07f4 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 30 Oct 2026 17:27:23 +0800
Subject: [PATCH 06/14] Block Supports: Only treat a non-empty string as a
color preset slug.
---
src/wp-includes/block-supports/border.php | 5 ++--
src/wp-includes/block-supports/colors.php | 11 ++-------
.../wpGetBorderClassesAndStyles.php | 14 +++++++++++
.../wpGetColorClassesAndStyles.php | 24 ++++++++++++-------
4 files changed, 34 insertions(+), 20 deletions(-)
diff --git a/src/wp-includes/block-supports/border.php b/src/wp-includes/block-supports/border.php
index ea2b39d8e0167..6a6a2e4cf1316 100644
--- a/src/wp-includes/block-supports/border.php
+++ b/src/wp-includes/block-supports/border.php
@@ -140,8 +140,9 @@ function wp_get_border_classes_and_styles( $block_attributes ) {
$border_styles['width'] = is_numeric( $border['width'] ) ? "{$border['width']}px" : $border['width'];
}
- $preset_border_color = array_key_exists( 'borderColor', $block_attributes ) ? "var:preset|color|{$block_attributes['borderColor']}" : null;
- $border_styles['color'] = $preset_border_color ? $preset_border_color : ( $border['color'] ?? null );
+ // 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 ] ) ) {
diff --git a/src/wp-includes/block-supports/colors.php b/src/wp-includes/block-supports/colors.php
index d03af90cf5f7f..3afd3545dfa81 100644
--- a/src/wp-includes/block-supports/colors.php
+++ b/src/wp-includes/block-supports/colors.php
@@ -172,16 +172,9 @@ function wp_get_color_classes_and_styles( $block_attributes ) {
foreach ( $preset_attributes as $feature => list( $preset_attribute, $preset_type ) ) {
$preset_slug = $block_attributes[ $preset_attribute ] ?? null;
- if ( true === $preset_slug ) {
- $preset_slug = 'true';
- }
-
- // Mirrors JS truthiness, so '0' is a slug. Arrays cannot form a class name.
- $has_preset = is_string( $preset_slug )
- ? '' !== $preset_slug
- : ( is_int( $preset_slug ) || is_float( $preset_slug ) ) && 0.0 !== (float) $preset_slug;
- $preset_value = $has_preset ? "var:preset|{$preset_type}|{$preset_slug}" : null;
+ // Preset slugs are strings in theme.json and the editor.
+ $preset_value = is_string( $preset_slug ) && '' !== $preset_slug ? "var:preset|{$preset_type}|{$preset_slug}" : null;
$custom_value = $block_attributes['style']['color'][ $feature ] ?? null;
$color_block_styles[ $feature ] = $preset_value ?? $custom_value;
}
diff --git a/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
index 1707f2d71d27e..2e16c536ac289 100644
--- a/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
@@ -68,6 +68,20 @@ public function data_get_border_classes_and_styles() {
'class' => 'has-border-color has-red-border-color',
),
),
+ 'non-string preset' => array(
+ 'block_attributes' => array(
+ 'borderColor' => 1,
+ 'style' => array(
+ 'border' => array(
+ 'color' => '#ff0000',
+ ),
+ ),
+ ),
+ 'expected' => array(
+ 'class' => 'has-border-color',
+ 'style' => 'border-color:#ff0000;',
+ ),
+ ),
'numeric' => array(
'block_attributes' => array(
'style' => array(
diff --git a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
index 52d6e319d9162..f852b6ee74903 100644
--- a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
@@ -26,7 +26,7 @@ public function test_get_color_classes_and_styles( $block_attributes, $expected
*/
public function data_get_color_classes_and_styles() {
return array(
- 'custom' => array(
+ 'custom' => array(
'block_attributes' => array(
'style' => array(
'color' => array(
@@ -41,7 +41,7 @@ public function data_get_color_classes_and_styles() {
'style' => 'color:#d92828;background-color:#ffffff;background:linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);',
),
),
- 'preset' => array(
+ 'preset' => array(
'block_attributes' => array(
'textColor' => 'contrast',
'backgroundColor' => 'base',
@@ -51,7 +51,7 @@ public function data_get_color_classes_and_styles() {
'class' => 'has-text-color has-contrast-color has-background has-base-background-color has-vivid-cyan-blue-to-vivid-purple-gradient-background',
),
),
- 'preset wins over custom' => array(
+ 'preset wins over custom' => array(
'block_attributes' => array(
'textColor' => 'contrast',
'style' => array(
@@ -64,7 +64,7 @@ public function data_get_color_classes_and_styles() {
'class' => 'has-text-color has-contrast-color',
),
),
- 'null preset uses custom text' => array(
+ 'null preset uses custom text' => array(
'block_attributes' => array(
'textColor' => null,
'style' => array(
@@ -78,19 +78,25 @@ public function data_get_color_classes_and_styles() {
'style' => 'color:#d92828;',
),
),
- 'numeric preset' => array(
+ 'non-string preset uses custom' => array(
'block_attributes' => array(
'textColor' => 1,
+ 'style' => array(
+ 'color' => array(
+ 'text' => '#d92828',
+ ),
+ ),
),
'expected' => array(
- 'class' => 'has-text-color has-1-color',
+ 'class' => 'has-text-color',
+ 'style' => 'color:#d92828;',
),
),
- 'empty' => array(
+ 'empty' => array(
'block_attributes' => array(),
'expected' => array(),
),
- 'malformed color' => array(
+ 'malformed color' => array(
'block_attributes' => array(
'style' => array(
'color' => '#d92828',
@@ -98,7 +104,7 @@ public function data_get_color_classes_and_styles() {
),
'expected' => array(),
),
- 'malformed attributes' => array(
+ 'malformed attributes' => array(
'block_attributes' => 'color',
'expected' => array(),
),
From aebff9e349651653e61cafed7b671c490f3ade38 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 30 Oct 2026 17:27:38 +0800
Subject: [PATCH 07/14] Block Supports: Return early for non-array attributes
in the background helper.
---
src/wp-includes/block-supports/background.php | 4 ++++
.../tests/block-supports/wpGetBackgroundClassesAndStyles.php | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/src/wp-includes/block-supports/background.php b/src/wp-includes/block-supports/background.php
index 4d8d1914ba1fa..342705a75653f 100644
--- a/src/wp-includes/block-supports/background.php
+++ b/src/wp-includes/block-supports/background.php
@@ -45,6 +45,10 @@ function wp_register_background_support( $block_type ) {
* @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 ) ) {
diff --git a/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
index 45ee6e62df673..32a9dda36a427 100644
--- a/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
@@ -167,6 +167,10 @@ public function data_get_background_classes_and_styles() {
'block_attributes' => 'style',
'expected' => array(),
),
+ 'attributes are an object' => array(
+ 'block_attributes' => (object) array( 'style' => array() ),
+ 'expected' => array(),
+ ),
);
}
}
From 4020b5570bb0e782113fcb6428d0db7604dae20a Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 2 Nov 2026 11:07:08 +0800
Subject: [PATCH 08/14] Block Supports: Return early for non-array attributes
in the shadow and spacing helpers.
---
src/wp-includes/block-supports/shadow.php | 4 ++++
src/wp-includes/block-supports/spacing.php | 4 ++++
.../tests/block-supports/wpGetShadowClassesAndStyles.php | 4 ++++
.../tests/block-supports/wpGetSpacingClassesAndStyles.php | 8 ++++++++
4 files changed, 20 insertions(+)
diff --git a/src/wp-includes/block-supports/shadow.php b/src/wp-includes/block-supports/shadow.php
index 3e2ad26886f96..852f8aed78cfa 100644
--- a/src/wp-includes/block-supports/shadow.php
+++ b/src/wp-includes/block-supports/shadow.php
@@ -74,6 +74,10 @@ function wp_apply_shadow_support( $block_type, $block_attributes ) {
* @return array Array with `class` and `style` keys, each present only when non-empty.
*/
function wp_get_shadow_classes_and_styles( $block_attributes ) {
+ if ( ! is_array( $block_attributes ) ) {
+ return array();
+ }
+
$shadow = $block_attributes['style']['shadow'] ?? null;
$attributes = array();
diff --git a/src/wp-includes/block-supports/spacing.php b/src/wp-includes/block-supports/spacing.php
index 03675fc2fecc5..3fb52c2dfa44b 100644
--- a/src/wp-includes/block-supports/spacing.php
+++ b/src/wp-includes/block-supports/spacing.php
@@ -80,6 +80,10 @@ function wp_apply_spacing_support( $block_type, $block_attributes ) {
* @return array Array with `class` and `style` keys, each present only when non-empty.
*/
function wp_get_spacing_classes_and_styles( $block_attributes ) {
+ if ( ! is_array( $block_attributes ) ) {
+ return array();
+ }
+
$spacing = $block_attributes['style']['spacing'] ?? null;
if ( ! is_array( $spacing ) ) {
return array();
diff --git a/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
index 9034c53b71570..8bac5a8fc2193 100644
--- a/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
@@ -62,6 +62,10 @@ public function data_get_shadow_classes_and_styles() {
'block_attributes' => null,
'expected' => array(),
),
+ 'object attributes' => array(
+ 'block_attributes' => (object) array( 'style' => array( 'shadow' => '1px 1px 1px #000' ) ),
+ 'expected' => array(),
+ ),
);
}
}
diff --git a/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
index cc10e9b24e908..be10672378d10 100644
--- a/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
@@ -107,6 +107,14 @@ public function data_get_spacing_classes_and_styles() {
),
'expected' => array(),
),
+ 'object attributes' => array(
+ 'block_attributes' => (object) array(
+ 'style' => array(
+ 'spacing' => array( 'margin' => '3px' ),
+ ),
+ ),
+ 'expected' => array(),
+ ),
);
}
}
From ca3113e49d131a7a09e67255f74a85b79d7bbf36 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Mon, 2 Nov 2026 13:04:58 +0800
Subject: [PATCH 09/14] Block Supports: Rename the background helper's return
array to $attributes.
---
src/wp-includes/block-supports/background.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/wp-includes/block-supports/background.php b/src/wp-includes/block-supports/background.php
index 342705a75653f..01a57ea8bae19 100644
--- a/src/wp-includes/block-supports/background.php
+++ b/src/wp-includes/block-supports/background.php
@@ -78,18 +78,18 @@ function wp_get_background_classes_and_styles( $block_attributes ) {
return array();
}
- $classes_and_styles = 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'] ) {
- $classes_and_styles['class'] = 'has-background';
+ $attributes['class'] = 'has-background';
}
- $classes_and_styles['style'] = $styles['css'];
+ $attributes['style'] = $styles['css'];
- return $classes_and_styles;
+ return $attributes;
}
/**
From fd9ccd3b0303f6594e1ec5a7c270a2e6b327acb0 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 4 Nov 2026 23:48:55 +0800
Subject: [PATCH 10/14] Block Supports: Match the Gutenberg comments in the
border and background helpers.
---
src/wp-includes/block-supports/background.php | 2 +-
src/wp-includes/block-supports/border.php | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/wp-includes/block-supports/background.php b/src/wp-includes/block-supports/background.php
index 01a57ea8bae19..51bfb953fb1f1 100644
--- a/src/wp-includes/block-supports/background.php
+++ b/src/wp-includes/block-supports/background.php
@@ -149,7 +149,7 @@ function wp_render_background_support( $block_content, $block ) {
return $block_content;
}
- // The helper ignores a non-array background, so only an array needs filtering.
+ // unset() fatals on a string, and the helper ignores one anyway.
if ( is_array( $block_attributes['style']['background'] ) ) {
if ( $skip_background_image ) {
unset(
diff --git a/src/wp-includes/block-supports/border.php b/src/wp-includes/block-supports/border.php
index 6a6a2e4cf1316..b4c40961cf117 100644
--- a/src/wp-includes/block-supports/border.php
+++ b/src/wp-includes/block-supports/border.php
@@ -61,7 +61,7 @@ function wp_apply_border_support( $block_type, $block_attributes ) {
$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 helper reads only `style.border`, so a non-array value counts as no border styles.
+ // 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'] );
}
@@ -82,7 +82,7 @@ function wp_apply_border_support( $block_type, $block_attributes ) {
unset( $block_attributes['style']['border']['color'], $block_attributes['borderColor'] );
}
- // Sides are filtered by skipped serialization only, not by feature support.
+ // 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 ] );
From 99a948645bf9a5c875f6bb64d945912360348f54 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Wed, 4 Nov 2026 23:48:55 +0800
Subject: [PATCH 11/14] Tests: Match the trimmed Gutenberg helper tests and
make their data providers static.
---
tests/phpunit/tests/block-supports/border.php | 62 -------------
.../phpunit/tests/block-supports/spacing.php | 23 -----
.../wpGetBackgroundClassesAndStyles.php | 91 ++-----------------
.../wpGetBorderClassesAndStyles.php | 63 ++++---------
.../wpGetColorClassesAndStyles.php | 2 +-
.../wpGetShadowClassesAndStyles.php | 18 +---
.../wpGetSpacingClassesAndStyles.php | 60 ++----------
7 files changed, 32 insertions(+), 287 deletions(-)
diff --git a/tests/phpunit/tests/block-supports/border.php b/tests/phpunit/tests/block-supports/border.php
index 2bab64e9741c6..66343c7fd727a 100644
--- a/tests/phpunit/tests/block-supports/border.php
+++ b/tests/phpunit/tests/block-supports/border.php
@@ -214,68 +214,6 @@ public function test_border_color_preset_with_skipped_color_serialization() {
);
}
- /**
- * @ticket 66269
- */
- public function test_split_borders_with_color_support_only_keeps_side_style() {
- $block_type = $this->register_bordered_block_with_support(
- 'test/split-borders-with-color-support-only',
- array(
- '__experimentalBorder' => array(
- 'color' => true,
- ),
- )
- );
- $block_attrs = array(
- 'style' => array(
- 'border' => array(
- 'style' => 'solid',
- 'top' => array(
- 'color' => '#72aee6',
- 'style' => 'dashed',
- ),
- ),
- ),
- );
- $actual = wp_apply_border_support( $block_type, $block_attrs );
- $expected = array(
- 'style' => 'border-top-color:#72aee6;border-top-style:dashed;',
- );
-
- $this->assertSame( $expected, $actual );
- }
-
- /**
- * @ticket 66269
- */
- public function test_split_borders_with_width_support_only_keeps_side_color() {
- $block_type = $this->register_bordered_block_with_support(
- 'test/split-borders-with-width-support-only',
- array(
- '__experimentalBorder' => array(
- 'width' => true,
- ),
- )
- );
- $block_attrs = array(
- 'borderColor' => 'red',
- 'style' => array(
- 'border' => array(
- 'top' => array(
- 'color' => '#72aee6',
- 'width' => '2px',
- ),
- ),
- ),
- );
- $actual = wp_apply_border_support( $block_type, $block_attrs );
- $expected = array(
- 'style' => 'border-top-width:2px;border-top-color:#72aee6;',
- );
-
- $this->assertSame( $expected, $actual );
- }
-
/**
* @ticket 66269
*/
diff --git a/tests/phpunit/tests/block-supports/spacing.php b/tests/phpunit/tests/block-supports/spacing.php
index ac50818491655..b0eb614bb66a1 100644
--- a/tests/phpunit/tests/block-supports/spacing.php
+++ b/tests/phpunit/tests/block-supports/spacing.php
@@ -204,27 +204,4 @@ public function test_unsupported_padding_is_not_applied() {
$this->assertSame( $expected, $actual );
}
-
- /**
- * @ticket 66269
- */
- public function test_spacing_without_style_attribute() {
- $this->test_block_name = 'test/spacing-without-style-attribute';
- register_block_type(
- $this->test_block_name,
- array(
- 'api_version' => 3,
- 'supports' => array(
- 'spacing' => array(
- 'margin' => true,
- 'padding' => true,
- ),
- ),
- )
- );
- $registry = WP_Block_Type_Registry::get_instance();
- $block_type = $registry->get_registered( $this->test_block_name );
-
- $this->assertSame( array(), wp_apply_spacing_support( $block_type, array() ) );
- }
}
diff --git a/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
index 32a9dda36a427..48b2c1cc4f622 100644
--- a/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetBackgroundClassesAndStyles.php
@@ -24,9 +24,9 @@ public function test_get_background_classes_and_styles( $block_attributes, $expe
*
* @return array[]
*/
- public function data_get_background_classes_and_styles() {
+ public static function data_get_background_classes_and_styles() {
return array(
- 'custom image gets default size' => array(
+ 'custom image gets default size' => array(
'block_attributes' => array(
'style' => array(
'background' => array(
@@ -39,7 +39,7 @@ public function data_get_background_classes_and_styles() {
'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;",
),
),
- 'contain without position is centered' => array(
+ 'contain without position is centered' => array(
'block_attributes' => array(
'style' => array(
'background' => array(
@@ -55,33 +55,7 @@ public function data_get_background_classes_and_styles() {
'style' => "background-image:url('https://example.com/image.jpg');background-position:50% 50%;background-repeat:no-repeat;background-size:contain;background-attachment:fixed;",
),
),
- 'preset gradient' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'background' => array(
- 'gradient' => 'var:preset|gradient|vivid-cyan-blue-to-vivid-purple',
- ),
- ),
- ),
- 'expected' => array(
- 'class' => 'has-background',
- 'style' => 'background-image:var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple);',
- ),
- ),
- 'gradient clipped to text has no class' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'background' => array(
- 'gradient' => 'linear-gradient(red, blue)',
- 'backgroundClip' => 'text',
- ),
- ),
- ),
- 'expected' => array(
- 'style' => 'background-image:linear-gradient(red, blue);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;',
- ),
- ),
- 'clip alone has no class' => array(
+ 'clip alone has no class' => array(
'block_attributes' => array(
'style' => array(
'background' => array(
@@ -93,7 +67,7 @@ public function data_get_background_classes_and_styles() {
'style' => 'background-clip:padding-box;-webkit-text-fill-color:currentColor;',
),
),
- 'image clipped to text has no class' => array(
+ 'image clipped to text has no class' => array(
'block_attributes' => array(
'style' => array(
'background' => array(
@@ -106,56 +80,7 @@ public function data_get_background_classes_and_styles() {
'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;",
),
),
- 'image without url gets default size' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'background' => array(
- 'backgroundImage' => array( 'id' => 1 ),
- ),
- ),
- ),
- 'expected' => array(
- 'class' => 'has-background',
- 'style' => 'background-size:cover;',
- ),
- ),
- 'numeric position is not output' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'background' => array(
- 'backgroundImage' => array( 'url' => 'https://example.com/image.jpg' ),
- 'backgroundPosition' => 50,
- ),
- ),
- ),
- 'expected' => array(
- 'class' => 'has-background',
- 'style' => "background-image:url('https://example.com/image.jpg');background-size:cover;",
- ),
- ),
- 'zero gradient outputs nothing' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'background' => array(
- 'gradient' => 0,
- ),
- ),
- ),
- 'expected' => array(),
- ),
- 'empty background' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'background' => array(),
- ),
- ),
- 'expected' => array(),
- ),
- 'no style attribute' => array(
- 'block_attributes' => array(),
- 'expected' => array(),
- ),
- 'background is not an array' => array(
+ 'background is not an array' => array(
'block_attributes' => array(
'style' => array(
'background' => 'linear-gradient(red, blue)',
@@ -163,11 +88,11 @@ public function data_get_background_classes_and_styles() {
),
'expected' => array(),
),
- 'attributes are not an array' => array(
+ 'attributes are a string' => array(
'block_attributes' => 'style',
'expected' => array(),
),
- 'attributes are an object' => array(
+ 'attributes are an object' => array(
'block_attributes' => (object) array( 'style' => array() ),
'expected' => array(),
),
diff --git a/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
index 2e16c536ac289..fc0c880ec0027 100644
--- a/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetBorderClassesAndStyles.php
@@ -24,9 +24,9 @@ public function test_get_border_classes_and_styles( $block_attributes, $expected
*
* @return array[]
*/
- public function data_get_border_classes_and_styles() {
+ public static function data_get_border_classes_and_styles() {
return array(
- 'custom' => array(
+ 'custom' => array(
'block_attributes' => array(
'style' => array(
'border' => array(
@@ -47,7 +47,7 @@ public function data_get_border_classes_and_styles() {
'style' => 'border-color:#ff0000;border-radius:10px;border-style:solid;border-width:2px;border-top-width:3px;border-top-color:#00ff00;border-top-style:dashed;',
),
),
- 'preset wins over custom' => array(
+ 'preset wins over custom' => array(
'block_attributes' => array(
'borderColor' => 'red',
'style' => array(
@@ -60,15 +60,21 @@ public function data_get_border_classes_and_styles() {
'class' => 'has-border-color has-red-border-color',
),
),
- 'preset only' => array(
+ 'empty preset keeps custom' => array(
'block_attributes' => array(
- 'borderColor' => 'red',
+ 'borderColor' => '',
+ 'style' => array(
+ 'border' => array(
+ 'color' => '#ff0000',
+ ),
+ ),
),
'expected' => array(
- 'class' => 'has-border-color has-red-border-color',
+ 'class' => 'has-border-color',
+ 'style' => 'border-color:#ff0000;',
),
),
- 'non-string preset' => array(
+ 'non-string preset' => array(
'block_attributes' => array(
'borderColor' => 1,
'style' => array(
@@ -82,7 +88,7 @@ public function data_get_border_classes_and_styles() {
'style' => 'border-color:#ff0000;',
),
),
- 'numeric' => array(
+ 'numeric' => array(
'block_attributes' => array(
'style' => array(
'border' => array(
@@ -95,48 +101,11 @@ public function data_get_border_classes_and_styles() {
'style' => 'border-radius:5px;border-width:2px;',
),
),
- 'zero' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'border' => array(
- 'radius' => 0,
- 'width' => 0,
- ),
- ),
- ),
- 'expected' => array(
- 'style' => 'border-radius:0px;border-width:0px;',
- ),
- ),
- 'empty' => array(
+ 'empty' => array(
'block_attributes' => array(),
'expected' => array(),
),
- 'malformed border' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'border' => 'solid',
- ),
- ),
- 'expected' => array(),
- ),
- 'malformed side' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'border' => array(
- 'top' => '1px solid',
- ),
- ),
- ),
- 'expected' => array(),
- ),
- 'malformed style' => array(
- 'block_attributes' => array(
- 'style' => 'border:1px solid',
- ),
- 'expected' => array(),
- ),
- 'malformed non-array' => array(
+ 'malformed non-array' => array(
'block_attributes' => 'border',
'expected' => array(),
),
diff --git a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
index f852b6ee74903..f8626aaa6fd9f 100644
--- a/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetColorClassesAndStyles.php
@@ -24,7 +24,7 @@ public function test_get_color_classes_and_styles( $block_attributes, $expected
*
* @return array[]
*/
- public function data_get_color_classes_and_styles() {
+ public static function data_get_color_classes_and_styles() {
return array(
'custom' => array(
'block_attributes' => array(
diff --git a/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
index 8bac5a8fc2193..21d2156bc2797 100644
--- a/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetShadowClassesAndStyles.php
@@ -24,7 +24,7 @@ public function test_get_shadow_classes_and_styles( $block_attributes, $expected
*
* @return array[]
*/
- public function data_get_shadow_classes_and_styles() {
+ public static function data_get_shadow_classes_and_styles() {
return array(
'custom shadow' => array(
'block_attributes' => array( 'style' => array( 'shadow' => '1px 1px 1px #000' ) ),
@@ -34,30 +34,14 @@ public function data_get_shadow_classes_and_styles() {
'block_attributes' => array( 'style' => array( 'shadow' => 'var:preset|shadow|natural' ) ),
'expected' => array( 'style' => 'box-shadow:var(--wp--preset--shadow--natural);' ),
),
- 'numeric shadow' => array(
- 'block_attributes' => array( 'style' => array( 'shadow' => 5 ) ),
- 'expected' => array(),
- ),
- 'zero shadow' => array(
- 'block_attributes' => array( 'style' => array( 'shadow' => 0 ) ),
- 'expected' => array(),
- ),
'empty shadow' => array(
'block_attributes' => array( 'style' => array( 'shadow' => '' ) ),
'expected' => array(),
),
- 'no style attribute' => array(
- 'block_attributes' => array(),
- 'expected' => array(),
- ),
'non-array style' => array(
'block_attributes' => array( 'style' => 'box-shadow:1px 1px 1px #000' ),
'expected' => array(),
),
- 'array shadow' => array(
- 'block_attributes' => array( 'style' => array( 'shadow' => array( '1px 1px 1px #000' ) ) ),
- 'expected' => array(),
- ),
'non-array attributes' => array(
'block_attributes' => null,
'expected' => array(),
diff --git a/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
index be10672378d10..c0a08b56df722 100644
--- a/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetSpacingClassesAndStyles.php
@@ -24,9 +24,9 @@ public function test_get_spacing_classes_and_styles( $block_attributes, $expecte
*
* @return array[]
*/
- public function data_get_spacing_classes_and_styles() {
+ public static function data_get_spacing_classes_and_styles() {
return array(
- 'custom values' => array(
+ 'custom values' => array(
'block_attributes' => array(
'style' => array(
'spacing' => array(
@@ -42,7 +42,7 @@ public function data_get_spacing_classes_and_styles() {
'style' => 'padding-top:1px;padding-left:2em;margin:3px;',
),
),
- 'presets' => array(
+ 'presets' => array(
'block_attributes' => array(
'style' => array(
'spacing' => array(
@@ -55,45 +55,7 @@ public function data_get_spacing_classes_and_styles() {
'style' => 'padding:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--20);',
),
),
- 'block gap is ignored' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'spacing' => array( 'blockGap' => '2em' ),
- ),
- ),
- 'expected' => array(),
- ),
- 'numeric values' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'spacing' => array( 'padding' => array( 'top' => 10 ) ),
- ),
- ),
- 'expected' => array(),
- ),
- 'zero' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'spacing' => array( 'margin' => '0' ),
- ),
- ),
- 'expected' => array(
- 'style' => 'margin:0;',
- ),
- ),
- 'empty spacing' => array(
- 'block_attributes' => array(
- 'style' => array(
- 'spacing' => array(),
- ),
- ),
- 'expected' => array(),
- ),
- 'no style' => array(
- 'block_attributes' => array(),
- 'expected' => array(),
- ),
- 'malformed spacing' => array(
+ 'malformed spacing' => array(
'block_attributes' => array(
'style' => array(
'spacing' => '10px',
@@ -101,18 +63,8 @@ public function data_get_spacing_classes_and_styles() {
),
'expected' => array(),
),
- 'malformed style' => array(
- 'block_attributes' => array(
- 'style' => 'padding:10px',
- ),
- 'expected' => array(),
- ),
- 'object attributes' => array(
- 'block_attributes' => (object) array(
- 'style' => array(
- 'spacing' => array( 'margin' => '3px' ),
- ),
- ),
+ 'object attributes' => array(
+ 'block_attributes' => (object) array( 'style' => array( 'spacing' => array( 'margin' => '3px' ) ) ),
'expected' => array(),
),
);
From 2bb0144a74ace626c5287724aed4e22d73c817ee Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 5 Nov 2026 08:56:20 +0800
Subject: [PATCH 12/14] Block Supports: Add a helper that returns every
support's classes and styles.
Adds wp_get_block_supports_classes_and_styles(), which combines the background, color, border, shadow and spacing helpers in the style engine's order. An optional list limits it to the supports a block applies to one element.
---
src/wp-includes/block-supports/utils.php | 55 +++++++++++++
.../wpGetBlockSupportsClassesAndStyles.php | 79 +++++++++++++++++++
2 files changed, 134 insertions(+)
create mode 100644 tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
diff --git a/src/wp-includes/block-supports/utils.php b/src/wp-includes/block-supports/utils.php
index c668586674de4..c0d34bcb980d7 100644
--- a/src/wp-includes/block-supports/utils.php
+++ b/src/wp-includes/block-supports/utils.php
@@ -33,3 +33,58 @@ function wp_should_skip_block_supports_serialization( $block_type, $feature_set,
return $skip_serialization;
}
+
+/**
+ * Returns the combined classes and inline styles of the background, color,
+ * border, shadow and spacing block supports for block attributes. Does not
+ * check block support or skipped serialization.
+ *
+ * @since 7.3.0
+ *
+ * @param array $block_attributes Block attributes.
+ * @param array|null $supports Optional. Supports to include: any of `background`, `color`,
+ * `border`, `shadow` and `spacing`. Default null, for all of them.
+ * @return array Array with `class` and `style` keys, each present only when non-empty.
+ */
+function wp_get_block_supports_classes_and_styles( $block_attributes, $supports = null ) {
+ // Follows the style engine's group order.
+ $getters = array(
+ 'background' => 'wp_get_background_classes_and_styles',
+ 'color' => 'wp_get_color_classes_and_styles',
+ 'border' => 'wp_get_border_classes_and_styles',
+ 'shadow' => 'wp_get_shadow_classes_and_styles',
+ 'spacing' => 'wp_get_spacing_classes_and_styles',
+ );
+
+ if ( is_array( $supports ) ) {
+ $getters = array_intersect_key( $getters, array_flip( $supports ) );
+ }
+
+ $classes = array();
+ $styles = '';
+
+ foreach ( $getters as $getter ) {
+ $classes_and_styles = $getter( $block_attributes );
+
+ if ( ! empty( $classes_and_styles['class'] ) ) {
+ $classes[] = $classes_and_styles['class'];
+ }
+
+ if ( ! empty( $classes_and_styles['style'] ) ) {
+ $styles .= $classes_and_styles['style'];
+ }
+ }
+
+ $attributes = array();
+
+ if ( ! empty( $classes ) ) {
+ // Background and color can both add `has-background`.
+ $attributes['class'] = implode( ' ', array_unique( explode( ' ', implode( ' ', $classes ) ) ) );
+ }
+
+ if ( '' !== $styles ) {
+ $attributes['style'] = $styles;
+ }
+
+ return $attributes;
+}
diff --git a/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
new file mode 100644
index 0000000000000..7049ce007f15d
--- /dev/null
+++ b/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
@@ -0,0 +1,79 @@
+assertSame( $expected, wp_get_block_supports_classes_and_styles( $block_attributes, $supports ) );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public static function data_get_block_supports_classes_and_styles() {
+ $block_attributes = array(
+ 'backgroundColor' => 'contrast',
+ 'borderColor' => 'accent-2',
+ 'style' => array(
+ 'background' => array( 'gradient' => 'linear-gradient(#000,#fff)' ),
+ 'border' => array( 'width' => '2px' ),
+ 'shadow' => '1px 1px 1px #000',
+ 'spacing' => array( 'padding' => array( 'top' => '10px' ) ),
+ ),
+ );
+
+ return array(
+ 'all supports' => array(
+ 'block_attributes' => $block_attributes,
+ 'supports' => null,
+ 'expected' => array(
+ 'class' => 'has-background has-contrast-background-color has-border-color has-accent-2-border-color',
+ 'style' => 'background-image:linear-gradient(#000,#fff);border-width:2px;box-shadow:1px 1px 1px #000;padding-top:10px;',
+ ),
+ ),
+ 'selected supports' => array(
+ 'block_attributes' => $block_attributes,
+ 'supports' => array( 'shadow', 'border' ),
+ 'expected' => array(
+ 'class' => 'has-border-color has-accent-2-border-color',
+ 'style' => 'border-width:2px;box-shadow:1px 1px 1px #000;',
+ ),
+ ),
+ 'unknown support' => array(
+ 'block_attributes' => $block_attributes,
+ 'supports' => array( 'typography' ),
+ 'expected' => array(),
+ ),
+ 'no supports' => array(
+ 'block_attributes' => $block_attributes,
+ 'supports' => array(),
+ 'expected' => array(),
+ ),
+ 'empty attributes' => array(
+ 'block_attributes' => array(),
+ 'supports' => null,
+ 'expected' => array(),
+ ),
+ 'non-array attributes' => array(
+ 'block_attributes' => null,
+ 'supports' => null,
+ 'expected' => array(),
+ ),
+ );
+ }
+}
From 896ba07a249d6592269a435dd154b189fd587546 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Thu, 5 Nov 2026 09:03:05 +0800
Subject: [PATCH 13/14] Tests: Add the Trac ticket number to the block supports
helper test.
---
.../tests/block-supports/wpGetBlockSupportsClassesAndStyles.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
index 7049ce007f15d..4515940eb74b1 100644
--- a/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
@@ -8,7 +8,7 @@ class Tests_Block_Supports_WpGetBlockSupportsClassesAndStyles extends WP_UnitTes
/**
* Tests that block support classes and styles are combined from block attributes.
*
- * @ticket XXXXX
+ * @ticket 66485
*
* @dataProvider data_get_block_supports_classes_and_styles
*
From a2621d606eb04813d04d239d4c6b9775a1097d90 Mon Sep 17 00:00:00 2001
From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com>
Date: Fri, 6 Nov 2026 17:17:07 +0800
Subject: [PATCH 14/14] Block Supports: Narrow the docblock types of the block
supports helper.
---
src/wp-includes/block-supports/utils.php | 11 +++++++----
.../wpGetBlockSupportsClassesAndStyles.php | 10 +++++-----
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/src/wp-includes/block-supports/utils.php b/src/wp-includes/block-supports/utils.php
index c0d34bcb980d7..677690731a982 100644
--- a/src/wp-includes/block-supports/utils.php
+++ b/src/wp-includes/block-supports/utils.php
@@ -41,10 +41,13 @@ function wp_should_skip_block_supports_serialization( $block_type, $feature_set,
*
* @since 7.3.0
*
- * @param array $block_attributes Block attributes.
- * @param array|null $supports Optional. Supports to include: any of `background`, `color`,
- * `border`, `shadow` and `spacing`. Default null, for all of them.
- * @return array Array with `class` and `style` keys, each present only when non-empty.
+ * @param array $block_attributes Block attributes.
+ * @param string[]|null $supports Optional. Supports to include: any of `background`, `color`,
+ * `border`, `shadow` and `spacing`. Unknown names are ignored.
+ * Default null, for all of them.
+ * @return array Array with `class` and `style` keys, each present only when non-empty.
+ *
+ * @phpstan-return array{ class?: non-empty-string, style?: non-empty-string }
*/
function wp_get_block_supports_classes_and_styles( $block_attributes, $supports = null ) {
// Follows the style engine's group order.
diff --git a/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php b/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
index 4515940eb74b1..a60a9270d706e 100644
--- a/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
+++ b/tests/phpunit/tests/block-supports/wpGetBlockSupportsClassesAndStyles.php
@@ -12,18 +12,18 @@ class Tests_Block_Supports_WpGetBlockSupportsClassesAndStyles extends WP_UnitTes
*
* @dataProvider data_get_block_supports_classes_and_styles
*
- * @param mixed $block_attributes Block attributes.
- * @param array|null $supports Supports to include.
- * @param array $expected Expected classes and styles.
+ * @param array|null $block_attributes Block attributes, or null to check the guard.
+ * @param string[]|null $supports Supports to include.
+ * @param array $expected Expected classes and styles.
*/
public function test_get_block_supports_classes_and_styles( $block_attributes, $supports, $expected ) {
$this->assertSame( $expected, wp_get_block_supports_classes_and_styles( $block_attributes, $supports ) );
}
/**
- * Data provider.
+ * Data provider for {@see self::test_get_block_supports_classes_and_styles()}.
*
- * @return array[]
+ * @return array|null, supports: string[]|null, expected: array{ class?: non-empty-string, style?: non-empty-string } }>
*/
public static function data_get_block_supports_classes_and_styles() {
$block_attributes = array(