Commit 12fd268986 for wordpress.org

commit 12fd2689863299e255366d484c184f96e4530af4
Author: isabel_brison <isabel_brison@git.wordpress.org>
Date:   Wed Oct 7 22:54:51 2026 +0000

    Editor: normalize block gap values in layout and fix grid axial gap.

    Ensures incorrect gap values don’t cause errors, fixes grid responsive column sizing when different vertical and horizontal gap values are present and ensures layouts that don’t support axial gap still get correct values.

    Props isabel_brison, andrewserong.
    Fixes #66263.

    Built from https://develop.svn.wordpress.org/trunk@64238


    git-svn-id: http://core.svn.wordpress.org/trunk@63389 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/block-supports/layout.php b/wp-includes/block-supports/layout.php
index dc9dc0768c..0162ae8d5a 100644
--- a/wp-includes/block-supports/layout.php
+++ b/wp-includes/block-supports/layout.php
@@ -80,16 +80,36 @@ function wp_get_layout_container_values( $layout ) {
  *
  * @since 7.1.0
  *
- * @param string|array|null $gap_value Block gap value.
- * @return string|array|null Sanitized block gap value.
+ * Numeric zero is converted to a string because it is valid CSS without a unit.
+ * Other non-string values are rejected.
+ *
+ * @param mixed $gap_value Block gap value.
+ * @return string|string[]|null Sanitized block gap value.
  */
 function wp_sanitize_block_gap_value( $gap_value ) {
 	if ( is_array( $gap_value ) ) {
 		foreach ( $gap_value as $key => $value ) {
-			$gap_value[ $key ] = ! is_scalar( $value ) || ( $value && preg_match( '%[\\\(&=}]|/\*%', (string) $value ) ) ? null : $value;
+			$sanitized_value = wp_sanitize_block_gap_value( $value );
+			if ( ! is_string( $sanitized_value ) ) {
+				unset( $gap_value[ $key ] );
+				continue;
+			}
+			$gap_value[ $key ] = $sanitized_value;
 		}

-		return $gap_value;
+		return empty( $gap_value ) ? null : $gap_value;
+	}
+
+	if ( ( is_int( $gap_value ) || is_float( $gap_value ) ) && 0.0 === (float) $gap_value ) {
+		return '0';
+	}
+
+	if ( ! is_string( $gap_value ) ) {
+		return null;
+	}
+
+	if ( '' === trim( $gap_value ) ) {
+		return null;
 	}

 	return $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
@@ -490,15 +510,18 @@ function wp_register_layout_support( $block_type ) {
  * @since 7.1.0 Added options array with options to process responsive styles.
  * @access private
  *
- * @param string               $selector                      CSS selector.
- * @param array                $layout                        Layout object. The one that is passed has already checked
- *                                                            the existence of default block layout.
- * @param bool                 $has_block_gap_support         Optional. Whether the theme has support for the block gap. Default false.
- * @param string|string[]|null $gap_value                     Optional. The block gap value to apply. Default null.
- * @param bool                 $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false.
- * @param string|array         $fallback_gap_value            Optional. The block gap value to apply. If it's an array expected properties are "top" and/or "left". Default '0.5em'.
- * @param array|null           $block_spacing                 Optional. Custom spacing set on the block. Default null.
- * @param array                $options                       {
+ * @param string                         $selector                      CSS selector.
+ * @param array                          $layout                        Layout object. The one that is passed has already checked
+ *                                                                       the existence of default block layout.
+ * @param bool                           $has_block_gap_support         Optional. Whether the theme has support for the block gap. Default false.
+ * @param string|string[]|int|float|null $gap_value                     Optional. The block gap value to apply. Only zero is accepted as a
+ *                                                                       numeric value. Default null.
+ * @param bool                           $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the
+ *                                                                       editor. Default false.
+ * @param string|string[]|int|float|null $fallback_gap_value            Optional. The fallback block gap value to apply. Only zero is accepted
+ *                                                                       as a numeric value. Default '0.5em'.
+ * @param array|null                     $block_spacing                 Optional. Custom spacing set on the block. Default null.
+ * @param array                          $options                       {
  *     Optional. Extra options for internal callers. Default empty array.
  *
  *     @type array       $viewport_overrides     An array of layout property overrides for the sake of style generation,
@@ -509,6 +532,10 @@ function wp_register_layout_support( $block_type ) {
  * @return string CSS styles on success. Else, empty string.
  */
 function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null, $options = array() ) {
+	// Normalize here as well as at external data boundaries because this function has direct callers.
+	$gap_value          = wp_sanitize_block_gap_value( $gap_value );
+	$fallback_gap_value = wp_sanitize_block_gap_value( $fallback_gap_value ) ?? '0.5em';
+
 	$base_layout             = is_array( $layout ) ? $layout : array();
 	$viewport_overrides      = $options['viewport_overrides'] ?? null;
 	$layout_for_styles       = null === $viewport_overrides ? $base_layout : array_replace( $base_layout, $viewport_overrides );
@@ -857,7 +884,7 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
 		$row_count         = is_numeric( $row_count_attr ) ? (int) $row_count_attr : null;

 		/*
-		 * If the gap value is an array, we use the "left" value because it represents the vertical gap, which
+		 * If the gap value is an array, we use the "left" value because it represents the horizontal gap, which
 		 * is the relevant one for computation of responsive grid columns.
 		 */
 		if ( is_array( $fallback_gap_value ) ) {
@@ -886,10 +913,12 @@ function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false
 					$slug            = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) );
 					$process_value   = "var(--wp--preset--spacing--$slug)";
 				}
+				if ( ! is_array( $gap_value ) || 'left' === $gap_side ) {
+					$responsive_gap_value = $process_value;
+				}
 				$combined_gap_value .= "$process_value ";
 			}
-			$gap_value            = trim( $combined_gap_value );
-			$responsive_gap_value = $gap_value;
+			$gap_value = trim( $combined_gap_value );
 		}

 		// Ensure 0 values have a unit so they work in calc().
@@ -1196,7 +1225,9 @@ function wp_render_layout_support_flag( $block_content, $block ) {
 	if ( ! current_theme_supports( 'disable-layout-styles' ) ) {

 		$gap_value          = wp_sanitize_block_gap_value( $style_attr['spacing']['blockGap'] ?? null );
-		$fallback_gap_value = $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ?? '0.5em';
+		$fallback_gap_value = wp_sanitize_block_gap_value(
+			$block_type->supports['spacing']['blockGap']['__experimentalDefault'] ?? null
+		) ?? '0.5em';
 		$block_spacing      = $style_attr['spacing'] ?? null;

 		/*
@@ -1230,7 +1261,19 @@ function wp_render_layout_support_flag( $block_content, $block ) {
 			}
 		}

-		$global_block_gap_value = $variation_block_gap_value ?? $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? $global_styles['spacing']['blockGap'] ?? null;
+		$global_block_gap_candidates = array(
+			$variation_block_gap_value,
+			$global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? null,
+			$global_styles['spacing']['blockGap'] ?? null,
+		);
+		$global_block_gap_value      = null;
+		foreach ( $global_block_gap_candidates as $candidate_gap_value ) {
+			$candidate_gap_value = wp_sanitize_block_gap_value( $candidate_gap_value );
+			if ( null !== $candidate_gap_value ) {
+				$global_block_gap_value = $candidate_gap_value;
+				break;
+			}
+		}

 		if ( null !== $global_block_gap_value ) {
 			$fallback_gap_value = $global_block_gap_value;
diff --git a/wp-includes/class-wp-theme-json.php b/wp-includes/class-wp-theme-json.php
index 9be92d1fcc..837499fb63 100644
--- a/wp-includes/class-wp-theme-json.php
+++ b/wp-includes/class-wp-theme-json.php
@@ -2317,7 +2317,8 @@ class WP_Theme_JSON {
 		 * Default layout gap styles will be skipped for themes that do not explicitly opt-in to blockGap with a `true` or `false` value.
 		 */
 		if ( $has_block_gap_support || $has_fallback_gap_support ) {
-			$block_gap_value = null;
+			$block_gap_value     = null;
+			$block_gap_row_value = null;
 			// Use a fallback gap value if block gap support is not available.
 			if ( ! $has_block_gap_support ) {
 				$block_gap_value = static::ROOT_BLOCK_SELECTOR === $selector ? '0.5em' : null;
@@ -2327,16 +2328,27 @@ class WP_Theme_JSON {
 			} else {
 				$block_gap_value = static::get_property_value( $node, array( 'spacing', 'blockGap' ) );
 			}
+			$block_gap_row_value = $block_gap_value;

 			// Support split row / column values and concatenate to a shorthand value.
 			if ( is_array( $block_gap_value ) ) {
-				if ( isset( $block_gap_value['top'] ) && isset( $block_gap_value['left'] ) ) {
-					$gap_row         = static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) );
-					$gap_column      = static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) );
-					$block_gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column;
+				$has_block_gap_row_value    = isset( $block_gap_value['top'] );
+				$has_block_gap_column_value = isset( $block_gap_value['left'] );
+
+				if ( $has_block_gap_row_value || $has_block_gap_column_value ) {
+					$block_gap_row_value    = $has_block_gap_row_value
+						? static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) )
+						: '0';
+					$block_gap_column_value = $has_block_gap_column_value
+						? static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) )
+						: '0';
+					$block_gap_value        = $block_gap_row_value === $block_gap_column_value
+						? $block_gap_row_value
+						: $block_gap_row_value . ' ' . $block_gap_column_value;
 				} else {
-					// Skip outputting gap value if not all sides are provided.
-					$block_gap_value = null;
+					// Skip outputting a gap value if neither supported axis is provided.
+					$block_gap_value     = null;
+					$block_gap_row_value = null;
 				}
 			}

@@ -2348,8 +2360,11 @@ class WP_Theme_JSON {
 						continue;
 					}

-					$class_name    = $layout_definition['className'] ?? false;
-					$spacing_rules = $layout_definition['spacingStyles'] ?? array();
+					$class_name       = $layout_definition['className'] ?? false;
+					$spacing_rules    = $layout_definition['spacingStyles'] ?? array();
+					$layout_gap_value = in_array( $layout_definition_key, array( 'default', 'constrained' ), true )
+						? $block_gap_row_value
+						: $block_gap_value;

 					if (
 						! empty( $class_name ) &&
@@ -2364,7 +2379,7 @@ class WP_Theme_JSON {
 							) {
 								// Iterate over each of the styling rules and substitute non-string values such as `null` with the real `blockGap` value.
 								foreach ( $spacing_rule['rules'] as $css_property => $css_value ) {
-									$current_css_value = is_string( $css_value ) ? $css_value : $block_gap_value;
+									$current_css_value = is_string( $css_value ) ? $css_value : $layout_gap_value;
 									if ( static::is_safe_css_declaration( $css_property, $current_css_value ) ) {
 										$declarations[] = array(
 											'name'  => $css_property,
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 751a83830a..359e131acc 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-alpha-64237';
+$wp_version = '7.2-alpha-64238';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.