PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
← All changes | lib/class-wp-theme-json-gutenberg.php +238 -110 23.7.0 → 24.1.0 View file →
@@ -124,8 +124,9 @@
124 124 * `prevent_override` value for `color.duotone` to use `color.defaultDuotone`.
125 125 * @since 6.2.0 Added 'shadow' presets.
126 126 * @since 6.6.0 Updated the 'prevent_override' value for font size presets to use 'typography.defaultFontSizes' and spacing size presets to use `spacing.defaultSpacingSizes`.
127 127 * @since 6.6.0 Added `aspectRatios`.
128 + * @since 7.2.0 Added 'textShadow' presets.
128 129 * @var array
129 130 */
130 131 const PRESETS_METADATA = array(
131 132 array(
@@ -186,8 +187,17 @@
186 187 'classes' => array( '.has-$slug-font-family' => 'font-family' ),
187 188 'properties' => array( 'font-family' ),
188 189 ),
189 190 array(
191 + 'path' => array( 'typography', 'textShadowPresets' ),
192 + 'prevent_override' => array( 'typography', 'defaultTextShadowPresets' ),
193 + 'use_default_names' => false,
194 + 'value_key' => 'textShadow',
195 + 'css_vars' => '--wp--preset--text-shadow--$slug',
196 + 'classes' => array( '.has-$slug-text-shadow' => 'text-shadow' ),
197 + 'properties' => array( 'text-shadow' ),
198 + ),
199 + array(
190 200 'path' => array( 'spacing', 'spacingSizes' ),
191 201 'prevent_override' => array( 'spacing', 'defaultSpacingSizes' ),
192 202 'use_default_names' => true,
193 203 'value_key' => 'size',
@@ -392,8 +402,10 @@
392 402 * @since 7.0.0 Added type markers to the schema for boolean values.
393 403 * @since 7.0.0 Added `dimensions.width`, `dimensions.height`, and
394 404 * `typography.textIndent` properties.
395 405 * @since 7.1.0 Added `viewport` property.
406 + * @since 7.2.0 Added `typography.textShadow`, `typography.textShadowPresets`,
407 + * and `typography.defaultTextShadowPresets`.
396 408 * @var array
397 409 */
398 410 const VALID_SETTINGS = array(
399 411 'appearanceTools' => null,
@@ -469,24 +481,27 @@
469 481 'presets' => null,
470 482 'defaultPresets' => null,
471 483 ),
472 484 'typography' => array(
473 - 'fluid' => null,
474 - 'customFontSize' => null,
475 - 'defaultFontSizes' => null,
476 - 'dropCap' => null,
477 - 'fontFamilies' => null,
478 - 'fontSizes' => null,
479 - 'fontStyle' => null,
480 - 'fontWeight' => null,
481 - 'letterSpacing' => null,
482 - 'lineHeight' => null,
483 - 'textAlign' => null,
484 - 'textColumns' => null,
485 - 'textDecoration' => null,
486 - 'textIndent' => null,
487 - 'textTransform' => null,
488 - 'writingMode' => null,
485 + 'fluid' => null,
486 + 'customFontSize' => null,
487 + 'defaultFontSizes' => null,
488 + 'dropCap' => null,
489 + 'fontFamilies' => null,
490 + 'fontSizes' => null,
491 + 'fontStyle' => null,
492 + 'fontWeight' => null,
493 + 'letterSpacing' => null,
494 + 'lineHeight' => null,
495 + 'textAlign' => null,
496 + 'textColumns' => null,
497 + 'textDecoration' => null,
498 + 'textIndent' => null,
499 + 'textTransform' => null,
500 + 'textShadow' => null,
501 + 'defaultTextShadowPresets' => null,
502 + 'textShadowPresets' => null,
503 + 'writingMode' => null,
489 504 ),
490 505 'viewport' => array(
491 506 'mobile' => null,
492 507 'tablet' => null,
@@ -651,9 +666,9 @@
651 666 *
652 667 * @since 7.1.0
653 668 *
654 669 * @param mixed $viewport_settings Viewport settings from theme.json.
655 - * @param array $options {
670 + * @param array $options {
656 671 * Optional. Options for generating media queries.
657 672 *
658 673 * @type bool $include_desktop Whether to include the desktop media query. Default false.
659 674 * }
@@ -842,8 +857,9 @@
842 857 'button' => '.wp-element-button, .wp-block-button__link',
843 858 // The block classes are necessary to target older content that won't use the new class names.
844 859 'caption' => '.wp-element-caption, .wp-block-audio figcaption, .wp-block-embed figcaption, .wp-block-gallery figcaption, .wp-block-image figcaption, .wp-block-table figcaption, .wp-block-video figcaption',
845 860 'cite' => 'cite',
861 + 'label' => 'label',
846 862 'select' => 'select',
847 863 'textInput' => 'textarea, input:where([type=email],[type=number],[type=password],[type=search],[type=text],[type=tel],[type=url])',
848 864 );
849 865
@@ -915,13 +931,15 @@
915 931
916 932 /**
917 933 * Processes pseudo-selectors for any node (block or variation).
918 934 *
919 - * @param array $node The node data (block or variation).
920 - * @param string $base_selector The base selector.
921 - * @param array $settings The theme settings.
922 - * @param string $block_name The block name.
923 - * @param array|null $block_metadata Metadata about the block to get styles for.
935 + * @since 7.0.0
936 + *
937 + * @param array $node The node data (block or variation).
938 + * @param string $base_selector The base selector.
939 + * @param array $settings The theme settings.
940 + * @param string $block_name The block name.
941 + * @param array|null $block_metadata Metadata about the block to get styles for.
924 942 * @param array|null $style_variation Style variation metadata.
925 943 * @return array Array of pseudo-selector declarations.
926 944 */
927 945 private function process_pseudo_selectors( $node, $base_selector, $settings, $block_name, $block_metadata = null, $style_variation = null ) {
@@ -1229,8 +1247,10 @@
1229 1247 *
1230 1248 * @since 5.8.0
1231 1249 * @since 5.9.0 Added the `$valid_block_names` and `$valid_element_name` parameters.
1232 1250 * @since 6.6.0 Extended schema definition to allow enhanced block style variations.
1251 + * @since 7.1.1 Updated schema to allow responsive breakpoint states and pseudo-selectors
1252 + * at the top level of `styles` for block style variation partials.
1233 1253 *
1234 1254 * @param array $input Structure to sanitize.
1235 1255 * @param array $valid_block_names List of valid block names.
1236 1256 * @param array $valid_element_names List of valid element names.
@@ -1294,9 +1314,17 @@
1294 1314 }
1295 1315
1296 1316 $schema_styles_blocks = array();
1297 1317 $schema_settings_blocks = array();
1318 + $breakpoint_states = array_keys( $responsive_media_queries );
1298 1319
1320 + $common_block_settings = static::VALID_SETTINGS;
1321 + // `viewport` and `blockVisibility` are global-only settings and cannot be set per block for now.
1322 + unset(
1323 + $common_block_settings['viewport'],
1324 + $common_block_settings['blockVisibility']
1325 + );
1326 +
1299 1327 /*
1300 1328 * Generate a schema for blocks.
1301 1329 * - Block styles can contain `elements`, `variations`, and responsive breakpoint state definitions.
1302 1330 * - Variations definitions cannot be nested.
@@ -1304,23 +1332,29 @@
1304 1332 * - Variation inner `blocks` styles can contain `elements` and responsive breakpoint states.
1305 1333 *
1306 1334 * As each variation needs both a `blocks` schema and responsive `blocks` schemas
1307 1335 * for further nested inner `blocks`, the overall schema is generated in multiple passes.
1336 + *
1337 + * All blocks start with the same style schema. Build that common schema
1338 + * once, then add block-specific pseudo and custom states below.
1308 1339 */
1340 + $responsive_block_schema = $styles_non_top_level;
1341 + $responsive_block_schema['elements'] = $schema_styles_elements;
1342 +
1343 + $common_block_schema = $styles_non_top_level;
1344 + $common_block_schema['elements'] = $schema_styles_elements;
1345 +
1346 + foreach ( $breakpoint_states as $breakpoint_state ) {
1347 + $common_block_schema[ $breakpoint_state ] = $responsive_block_schema;
1348 + }
1349 +
1309 1350 foreach ( $valid_block_names as $block ) {
1310 - $schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
1311 - // `viewport` and `blockVisibility` are global-only settings and cannot be set per block for now.
1312 - unset( $schema_settings_blocks[ $block ]['viewport'] );
1313 - unset( $schema_settings_blocks[ $block ]['blockVisibility'] );
1314 - $schema_styles_blocks[ $block ] = $styles_non_top_level;
1315 - $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
1351 + $schema_settings_blocks[ $block ] = $common_block_settings;
1352 + $schema_styles_blocks[ $block ] = $common_block_schema;
1316 1353
1317 - // Add responsive breakpoint states for all blocks.
1318 - foreach ( array_keys( $responsive_media_queries ) as $breakpoint_state ) {
1319 - $schema_styles_blocks[ $block ][ $breakpoint_state ] = $styles_non_top_level;
1320 - $schema_styles_blocks[ $block ][ $breakpoint_state ]['elements'] = $schema_styles_elements;
1321 -
1322 - if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
1354 + // Add responsive pseudo-selectors only to blocks that support them.
1355 + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
1356 + foreach ( $breakpoint_states as $breakpoint_state ) {
1323 1357 foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) {
1324 1358 $schema_styles_blocks[ $block ][ $breakpoint_state ][ $pseudo_selector ] = $styles_non_top_level;
1325 1359 }
1326 1360 }
@@ -1375,9 +1409,8 @@
1375 1409 // Add responsive breakpoint states to block style variations.
1376 1410 foreach ( array_keys( $responsive_media_queries ) as $breakpoint_state ) {
1377 1411 $variation_schema[ $breakpoint_state ] = $styles_non_top_level;
1378 1412 $variation_schema[ $breakpoint_state ]['elements'] = $schema_styles_elements;
1379 - $variation_schema[ $breakpoint_state ]['blocks'] = $schema_styles_blocks;
1380 1413
1381 1414 if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
1382 1415 foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) {
1383 1416 $variation_schema[ $breakpoint_state ][ $pseudo_selector ] = $styles_non_top_level;
@@ -1405,8 +1438,49 @@
1405 1438 $schema['settings'] = static::VALID_SETTINGS;
1406 1439 $schema['settings']['blocks'] = $schema_settings_blocks;
1407 1440 $schema['settings']['typography']['fontFamilies'] = static::schema_in_root_and_per_origin( static::FONT_FAMILY_SCHEMA );
1408 1441
1442 + /*
1443 + * Add block style variation states to the top-level styles schema.
1444 + *
1445 + * Block style variations defined in a standalone JSON partial within a
1446 + * theme's `styles` directory declare their styles at the root of the
1447 + * `styles` object, so they are sanitized against the top-level schema.
1448 + * It needs to allow the same states that are allowed for variations
1449 + * declared inline in theme.json, otherwise those states are silently
1450 + * removed as unknown keys.
1451 + *
1452 + * The `blockTypes` property is only present on block style variation
1453 + * partials, so it both identifies the config as a variation and
1454 + * determines which pseudo-selectors are valid for it. Regular
1455 + * theme.json files are unaffected.
1456 + */
1457 + if ( ! empty( $input['blockTypes'] ) && is_array( $input['blockTypes'] ) ) {
1458 + $variation_pseudo_selectors = array();
1459 + foreach ( $input['blockTypes'] as $variation_block_type ) {
1460 + if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ] ) ) {
1461 + $variation_pseudo_selectors = array_merge(
1462 + $variation_pseudo_selectors,
1463 + static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ]
1464 + );
1465 + }
1466 + }
1467 + $variation_pseudo_selectors = array_unique( $variation_pseudo_selectors );
1468 +
1469 + foreach ( $breakpoint_states as $breakpoint_state ) {
1470 + $schema['styles'][ $breakpoint_state ] = $styles_non_top_level;
1471 + $schema['styles'][ $breakpoint_state ]['elements'] = $schema_styles_elements;
1472 +
1473 + foreach ( $variation_pseudo_selectors as $pseudo_selector ) {
1474 + $schema['styles'][ $breakpoint_state ][ $pseudo_selector ] = $styles_non_top_level;
1475 + }
1476 + }
1477 +
1478 + foreach ( $variation_pseudo_selectors as $pseudo_selector ) {
1479 + $schema['styles'][ $pseudo_selector ] = $styles_non_top_level;
1480 + }
1481 + }
1482 +
1409 1483 // Remove anything that's not present in the schema.
1410 1484 foreach ( array( 'styles', 'settings' ) as $subtree ) {
1411 1485 if ( ! isset( $input[ $subtree ] ) ) {
1412 1486 continue;
@@ -2109,9 +2183,9 @@
2109 2183 /**
2110 2184 * Returns the global styles custom CSS for a single block.
2111 2185 * This function is deprecated; please do not sync to core.
2112 2186 *
2113 - * @param array $css The block css node.
2187 + * @param array $css The block css node.
2114 2188 * @param string $selector The block selector.
2115 2189 *
2116 2190 * @return string The global styles custom CSS for the block.
2117 2191 */
@@ -2236,9 +2310,10 @@
2236 2310
2237 2311 // Gap styles will only be output if the theme has block gap support, or supports a fallback gap.
2238 2312 // Default layout gap styles will be skipped for themes that do not explicitly opt-in to blockGap with a `true` or `false` value.
2239 2313 if ( $has_block_gap_support || $has_fallback_gap_support ) {
2240 - $block_gap_value = null;
2314 + $block_gap_value = null;
2315 + $block_gap_row_value = null;
2241 2316 // Use a fallback gap value if block gap support is not available.
2242 2317 if ( ! $has_block_gap_support ) {
2243 2318 $block_gap_value = static::ROOT_BLOCK_SELECTOR === $selector ? '0.5em' : null;
2244 2319 if ( ! empty( $block_type ) ) {
@@ -2246,18 +2321,29 @@
2246 2321 }
2247 2322 } else {
2248 2323 $block_gap_value = static::get_property_value( $node, array( 'spacing', 'blockGap' ) );
2249 2324 }
2325 + $block_gap_row_value = $block_gap_value;
2250 2326
2251 2327 // Support split row / column values and concatenate to a shorthand value.
2252 2328 if ( is_array( $block_gap_value ) ) {
2253 - if ( isset( $block_gap_value['top'] ) && isset( $block_gap_value['left'] ) ) {
2254 - $gap_row = static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) );
2255 - $gap_column = static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) );
2256 - $block_gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column;
2329 + $has_block_gap_row_value = isset( $block_gap_value['top'] );
2330 + $has_block_gap_column_value = isset( $block_gap_value['left'] );
2331 +
2332 + if ( $has_block_gap_row_value || $has_block_gap_column_value ) {
2333 + $block_gap_row_value = $has_block_gap_row_value
2334 + ? static::get_property_value( $node, array( 'spacing', 'blockGap', 'top' ) )
2335 + : '0';
2336 + $block_gap_column_value = $has_block_gap_column_value
2337 + ? static::get_property_value( $node, array( 'spacing', 'blockGap', 'left' ) )
2338 + : '0';
2339 + $block_gap_value = $block_gap_row_value === $block_gap_column_value
2340 + ? $block_gap_row_value
2341 + : $block_gap_row_value . ' ' . $block_gap_column_value;
2257 2342 } else {
2258 - // Skip outputting gap value if not all sides are provided.
2259 - $block_gap_value = null;
2343 + // Skip outputting a gap value if neither supported axis is provided.
2344 + $block_gap_value = null;
2345 + $block_gap_row_value = null;
2260 2346 }
2261 2347 }
2262 2348
2263 2349 // If the block should have custom gap, add the gap styles.
@@ -2267,10 +2353,13 @@
2267 2353 if ( ! $has_block_gap_support && 'flex' !== $layout_definition_key && 'grid' !== $layout_definition_key ) {
2268 2354 continue;
2269 2355 }
2270 2356
2271 - $class_name = $layout_definition['className'] ?? false;
2272 - $spacing_rules = $layout_definition['spacingStyles'] ?? array();
2357 + $class_name = $layout_definition['className'] ?? false;
2358 + $spacing_rules = $layout_definition['spacingStyles'] ?? array();
2359 + $layout_gap_value = in_array( $layout_definition_key, array( 'default', 'constrained' ), true )
2360 + ? $block_gap_row_value
2361 + : $block_gap_value;
2273 2362
2274 2363 if (
2275 2364 ! empty( $class_name ) &&
2276 2365 ! empty( $spacing_rules )
@@ -2283,9 +2372,9 @@
2283 2372 ! empty( $spacing_rule['rules'] )
2284 2373 ) {
2285 2374 // Iterate over each of the styling rules and substitute non-string values such as `null` with the real `blockGap` value.
2286 2375 foreach ( $spacing_rule['rules'] as $css_property => $css_value ) {
2287 - $current_css_value = is_string( $css_value ) ? $css_value : $block_gap_value;
2376 + $current_css_value = is_string( $css_value ) ? $css_value : $layout_gap_value;
2288 2377 if ( static::is_safe_css_declaration( $css_property, $current_css_value ) ) {
2289 2378 $declarations[] = array(
2290 2379 'name' => $css_property,
2291 2380 'value' => $current_css_value,
@@ -2988,15 +3077,15 @@
2988 3077 * @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set.
2989 3078 * @since 6.6.0 Passing current theme JSON settings to wp_get_typography_font_size_value(). Using style engine to correctly fetch background CSS values.
2990 3079 * @since 6.7.0 Allow ref resolution of background properties.
2991 3080 *
2992 - * @param array $styles Styles to process.
2993 - * @param array $settings Theme settings.
2994 - * @param array $properties Properties metadata.
2995 - * @param array $theme_json Theme JSON array.
2996 - * @param string $selector The style block selector.
3081 + * @param array $styles Styles to process.
3082 + * @param array $settings Theme settings.
3083 + * @param array $properties Properties metadata.
3084 + * @param array $theme_json Theme JSON array.
3085 + * @param string $selector The style block selector.
2997 3086 * @param boolean $use_root_padding Whether to add custom properties at root level.
2998 - * @return array Returns the modified $declarations.
3087 + * @return array Returns the modified $declarations.
2999 3088 */
3000 3089 protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) {
3001 3090 if ( empty( $styles ) ) {
3002 3091 return array();
@@ -3120,10 +3209,10 @@
3120 3209 * @since 5.9.0 Added support for values of array type, which are returned as is.
3121 3210 * @since 6.1.0 Added the `$theme_json` parameter.
3122 3211 * @since 6.7.0 Added support for background image refs
3123 3212 *
3124 - * @param array $styles Styles subtree.
3125 - * @param array $path Which property to process.
3213 + * @param array $styles Styles subtree.
3214 + * @param array $path Which property to process.
3126 3215 * @param array $theme_json Theme JSON array.
3127 3216 * @return string|array Style property value.
3128 3217 */
3129 3218 protected static function get_property_value( $styles, $path, $theme_json = null ) {
@@ -3495,9 +3584,9 @@
3495 3584 * @since 6.1.0
3496 3585 *
3497 3586 * @param array $theme_json The theme.json converted to an array.
3498 3587 * @param array $selectors Optional list of selectors per block.
3499 - * @param array $options {
3588 + * @param array $options {
3500 3589 * Optional. An array of options for now used for internal purposes only (may change without notice).
3501 3590 *
3502 3591 * @type bool $include_block_style_variations Includes nodes for block style variations. Default false.
3503 3592 * @type bool $include_node_paths_only Return only block nodes node paths. Default false.
@@ -3702,24 +3791,47 @@
3702 3791 }
3703 3792 }
3704 3793 }
3705 3794 }
3706 - if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
3707 - foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) {
3795 + /*
3796 + * Elements can be styled outside any breakpoint, inside one, or both,
3797 + * so collect the names from all of those places before looping. An
3798 + * element styled only inside a breakpoint still needs a node.
3799 + */
3800 + $block_node = $theme_json['styles']['blocks'][ $name ] ?? array();
3801 + $element_names = array_keys( $block_node['elements'] ?? array() );
3802 + foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
3803 + $element_names = array_merge(
3804 + $element_names,
3805 + array_keys( $block_node[ $breakpoint ]['elements'] ?? array() )
3806 + );
3807 + }
3808 + $element_names = array_unique( $element_names );
3809 +
3810 + if ( ! empty( $element_names ) ) {
3811 + foreach ( $element_names as $element ) {
3708 3812 $element_path = array( 'styles', 'blocks', $name, 'elements', $element );
3709 3813 if ( $include_node_paths_only ) {
3710 - $nodes[] = array(
3711 - 'path' => $element_path,
3712 - );
3814 + if ( isset( $block_node['elements'][ $element ] ) ) {
3815 + $nodes[] = array(
3816 + 'path' => $element_path,
3817 + );
3818 + }
3713 3819 continue;
3714 3820 }
3715 3821
3822 + if ( ! isset( $selectors[ $name ]['elements'][ $element ] ) ) {
3823 + continue;
3824 + }
3825 +
3716 3826 $element_selector = $selectors[ $name ]['elements'][ $element ];
3717 3827
3718 - $nodes[] = array(
3719 - 'path' => $element_path,
3720 - 'selector' => $element_selector,
3721 - );
3828 + if ( isset( $block_node['elements'][ $element ] ) ) {
3829 + $nodes[] = array(
3830 + 'path' => $element_path,
3831 + 'selector' => $element_selector,
3832 + );
3833 + }
3722 3834
3723 3835 // Responsive element nodes: one node per breakpoint that has
3724 3836 // styles for this element. Cascade: a{} → @media{a{}}
3725 3837 foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
@@ -3734,45 +3846,29 @@
3734 3846
3735 3847 // Handle any pseudo selectors for the element.
3736 3848 if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) {
3737 3849 foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
3738 - // Create element pseudo node if default or any responsive breakpoint has the pseudo.
3739 - $has_element_pseudo = isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] );
3740 - if ( ! $has_element_pseudo ) {
3741 - foreach ( array_keys( $responsive_media_queries ) as $bp ) {
3742 - if ( isset( $theme_json['styles']['blocks'][ $name ][ $bp ]['elements'][ $element ][ $pseudo_selector ] ) ) {
3743 - $has_element_pseudo = true;
3744 - break;
3745 - }
3746 - }
3850 + // Emit the default pseudo node only when the default state styles
3851 + // the pseudo. Otherwise get_styles_for_block() falls back to the
3852 + // element's base styles, outputting a rule the theme never defined.
3853 + if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) {
3854 + $nodes[] = array(
3855 + 'path' => array( 'styles', 'blocks', $name, 'elements', $element ),
3856 + 'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
3857 + );
3747 3858 }
3748 3859
3749 - if ( $has_element_pseudo ) {
3750 - $element_pseudo_path = array( 'styles', 'blocks', $name, 'elements', $element );
3751 - if ( $include_node_paths_only ) {
3860 + // Responsive element pseudo nodes: one node per breakpoint
3861 + // that has this pseudo state for this element.
3862 + // Cascade: a:hover{} → @media{a:hover{}}
3863 + foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
3864 + if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ][ $pseudo_selector ] ) ) {
3752 3865 $nodes[] = array(
3753 - 'path' => $element_pseudo_path,
3866 + 'path' => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ),
3867 + 'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
3868 + 'media_query' => $responsive_media_queries[ $breakpoint ],
3754 3869 );
3755 - continue;
3756 3870 }
3757 -
3758 - $nodes[] = array(
3759 - 'path' => $element_pseudo_path,
3760 - 'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
3761 - );
3762 -
3763 - // Responsive element pseudo nodes: one node per breakpoint
3764 - // that has this pseudo state for this element.
3765 - // Cascade: a:hover{} → @media{a:hover{}}
3766 - foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
3767 - if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ][ $pseudo_selector ] ) ) {
3768 - $nodes[] = array(
3769 - 'path' => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ),
3770 - 'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
3771 - 'media_query' => $responsive_media_queries[ $breakpoint ],
3772 - );
3773 - }
3774 - }
3775 3871 }
3776 3872 }
3777 3873 }
3778 3874 }
@@ -3855,10 +3951,20 @@
3855 3951 // Store variation metadata and node for layout styles generation.
3856 3952 // Only store if the variation has blockGap defined.
3857 3953 if ( isset( $style_variation_node['spacing']['blockGap'] ) ) {
3858 3954 // Append block selector to the variation selector for proper targeting.
3859 - $variation_metadata_with_selector = $style_variation;
3860 - $variation_metadata_with_selector['selector'] = $style_variation['selector'] . $block_metadata['css'];
3955 + $variation_metadata_with_selector = $style_variation;
3956 + $variation_metadata_with_selector['selector'] = $style_variation['selector'] . $block_metadata['css'];
3957 +
3958 + /*
3959 + * `get_layout_styles()` reads `name` as a block name, to check that the block
3960 + * supports layout at all. A variation node's `name` is the variation slug,
3961 + * which is never a registered block, so the check fails and every variation
3962 + * gap rule is discarded. Pass the block the variation belongs to, so the
3963 + * support check answers the question it is actually asking.
3964 + */
3965 + $variation_metadata_with_selector['name'] = $block_name;
3966 +
3861 3967 $style_variation_layout_metadata[ $style_variation['selector'] ] = array(
3862 3968 'metadata' => $variation_metadata_with_selector,
3863 3969 'node' => $style_variation_node,
3864 3970 );
@@ -3912,9 +4018,13 @@
3912 4018 // Process blockGap responsive layout styles for this variation.
3913 4019 if ( isset( $breakpoint_node['spacing']['blockGap'] ) ) {
3914 4020 $variation_layout_metadata = $style_variation;
3915 4021 $variation_layout_metadata['selector'] = $style_variation['selector'] . $block_metadata['css'];
3916 - $variation_responsive_css .= $this->get_layout_styles(
4022 +
4023 + // The variation slug is not a block name here either. See above.
4024 + $variation_layout_metadata['name'] = $block_name;
4025 +
4026 + $variation_responsive_css .= $this->get_layout_styles(
3917 4027 $variation_layout_metadata,
3918 4028 array(
3919 4029 'node' => $breakpoint_node,
3920 4030 'media_query' => $breakpoint_media,
@@ -4577,9 +4687,9 @@
4577 4687 * Gets a `default`'s preset name by a provided slug.
4578 4688 *
4579 4689 * @since 5.9.0
4580 4690 *
4581 - * @param string $slug The slug we want to find a match from default presets.
4691 + * @param string $slug The slug we want to find a match from default presets.
4582 4692 * @param array $base_path The path to inspect. It's 'settings' by default.
4583 4693 * @return string|null
4584 4694 */
4585 4695 protected function get_name_from_defaults( $slug, $base_path ) {
@@ -4627,10 +4737,10 @@
4627 4737 * @since 5.9.0
4628 4738 * @since 6.6.0 Added support for block style variation element styles and $origin parameter.
4629 4739 *
4630 4740 * @param array $theme_json Structure to sanitize.
4631 - * @param string $origin Optional. What source of data this object represents.
4632 - * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'.
4741 + * @param string $origin Optional. What source of data this object represents.
4742 + * One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'.
4633 4743 * @return array Sanitized structure.
4634 4744 */
4635 4745 public static function remove_insecure_properties( $theme_json, $origin = 'theme' ) {
4636 4746 if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) {
@@ -5674,8 +5784,9 @@
5674 5784 * This is used to convert the internal representation of variables to the CSS representation.
5675 5785 * For example, `var:preset|color|vivid-green-cyan` becomes `var(--wp--preset--color--vivid-green-cyan)`.
5676 5786 *
5677 5787 * @since 6.3.0
5788 + * @since 7.2.0 Preset reference slugs are kebab-cased to match the generated custom properties.
5678 5789 * @param string $value The variable such as var:preset|color|vivid-green-cyan to convert.
5679 5790 * @return string The converted variable.
5680 5791 */
5681 5792 private static function convert_custom_properties( $value ) {
@@ -5683,14 +5794,31 @@
5683 5794 $prefix_len = strlen( $prefix );
5684 5795 $token_in = '|';
5685 5796 $token_out = '--';
5686 5797 if ( str_starts_with( $value, $prefix ) ) {
5687 - $unwrapped_name = str_replace(
5688 - $token_in,
5689 - $token_out,
5690 - substr( $value, $prefix_len )
5691 - );
5692 - $value = "var(--wp--$unwrapped_name)";
5798 + $parts = explode( $token_in, substr( $value, $prefix_len ) );
5799 +
5800 + /*
5801 + * The slug of a preset reference is kebab-cased so the resulting
5802 + * custom property matches the one generated from the preset,
5803 + * whose slug is also kebab-cased (see `get_settings_values_by_slug()`).
5804 + * For slugs that are not already kebab-cased (e.g. `n27`), a verbatim
5805 + * conversion produces a reference to a custom property that does
5806 + * not exist (`--wp--preset--font-family--n27` instead of the
5807 + * generated `--wp--preset--font-family--n-27`).
5808 + *
5809 + * Duotone is the exception: its custom properties are generated by
5810 + * `WP_Duotone_Gutenberg` from the presets it registers in
5811 + * `get_all_global_styles_presets()`. Duotone references are
5812 + * kebab-cased all the same: the editor and the JS style engine
5813 + * kebab-case the references of every preset type, and
5814 + * `WP_Duotone_Gutenberg` looks up presets by kebab-cased filter ID.
5815 + */
5816 + if ( 3 === count( $parts ) && 'preset' === $parts[0] ) {
5817 + $parts[2] = _wp_to_kebab_case( $parts[2] );
5818 + }
5819 +
5820 + $value = 'var(--wp--' . implode( $token_out, $parts ) . ')';
5693 5821 }
5694 5822
5695 5823 return $value;
5696 5824 }
@@ -5699,9 +5827,9 @@
5699 5827 * Given a tree, converts the internal representation of variables to the CSS representation.
5700 5828 * It is recursive and modifies the input in-place.
5701 5829 *
5702 5830 * @since 6.3.0
5703 - * @param array $tree Input to process.
5831 + * @param array $tree Input to process.
5704 5832 * @return array The modified $tree.
5705 5833 */
5706 5834 private static function resolve_custom_css_format( $tree ) {
5707 5835 $prefix = 'var:';
@@ -5849,9 +5977,9 @@
5849 5977 * the variation class directly to the selector that will receive the
5850 5978 * declarations instead of deriving it by subtracting the root selector from
5851 5979 * the feature selector.
5852 5980 *
5853 - * @param array $style_variation Style variation metadata.
5981 + * @param array $style_variation Style variation metadata.
5854 5982 * @param string $feature_selector CSS selector for the feature.
5855 5983 * @return string Feature selector with block style variation selector added.
5856 5984 */
5857 5985 protected static function get_block_style_variation_feature_selector( $style_variation, $feature_selector ) {