| @@ -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; |
| @@ -1469,9 +1543,9 @@ | ||
| 1469 | 1543 | * separate selectors. If none of the start characters are present, |
| 1470 | 1544 | * there should be no way for a comma to mean anything other than a |
| 1471 | 1545 | * comma token. The exception are syntax errors, which are not handled here. |
| 1472 | 1546 | * |
| 1473 | - * @see https://www.w3.org/TR/css-syntax-3/#parse-comma-separated-list-of-component-values | |
| 1547 | + * @link https://www.w3.org/TR/css-syntax-3/#parse-comma-separated-list-of-component-values | |
| 1474 | 1548 | */ |
| 1475 | 1549 | if ( strlen( $selector ) === strcspn( $selector, '/\'"(<\\' ) ) { |
| 1476 | 1550 | return str_replace( ',', $to_append . ',', $selector ) . $to_append; |
| 1477 | 1551 | } |
| @@ -1518,9 +1592,9 @@ | ||
| 1518 | 1592 | * separate selectors. If none of the start characters are present, |
| 1519 | 1593 | * there should be no way for a comma to mean anything other than a |
| 1520 | 1594 | * comma token. The exception are syntax errors, which are not handled here. |
| 1521 | 1595 | * |
| 1522 | - * @see https://www.w3.org/TR/css-syntax-3/#parse-comma-separated-list-of-component-values | |
| 1596 | + * @link https://www.w3.org/TR/css-syntax-3/#parse-comma-separated-list-of-component-values | |
| 1523 | 1597 | */ |
| 1524 | 1598 | if ( strlen( $selector ) === strcspn( $selector, '/\'"(<\\' ) ) { |
| 1525 | 1599 | return $to_prepend . str_replace( ',', ',' . $to_prepend, $selector ); |
| 1526 | 1600 | } |
| @@ -1564,10 +1638,10 @@ | ||
| 1564 | 1638 | * |
| 1565 | 1639 | * // Comments stay with the selector they follow. |
| 1566 | 1640 | * array( '.a /* a, the first *\/', '.b' ) === self::split_selector_list( '.a /* a, the first *\/,.b' ); |
| 1567 | 1641 | * |
| 1568 | - * @see https://www.w3.org/TR/selectors/#parse-selector | |
| 1569 | - * @see https://www.w3.org/TR/css-syntax-3/ | |
| 1642 | + * @link https://www.w3.org/TR/selectors/#parse-selector | |
| 1643 | + * @link https://www.w3.org/TR/css-syntax-3/ | |
| 1570 | 1644 | * |
| 1571 | 1645 | * @param string $selector CSS selector list. |
| 1572 | 1646 | * @return string[] Selectors. |
| 1573 | 1647 | */ |
| @@ -1670,10 +1744,10 @@ | ||
| 1670 | 1744 | * > Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are |
| 1671 | 1745 | * > not included in this definition, as they are converted |
| 1672 | 1746 | * > to U+000A LINE FEED during preprocessing. |
| 1673 | 1747 | * |
| 1674 | - * @see https://www.w3.org/TR/css-syntax/#whitespace | |
| 1675 | - * @see https://www.w3.org/TR/css-syntax/#newline | |
| 1748 | + * @link https://www.w3.org/TR/css-syntax/#whitespace | |
| 1749 | + * @link https://www.w3.org/TR/css-syntax/#newline | |
| 1676 | 1750 | */ |
| 1677 | 1751 | $selectors[] = trim( substr( $selector, $was_at, $next_at - $was_at ), " \t\n" ); |
| 1678 | 1752 | $at = $next_at + 1; |
| 1679 | 1753 | $was_at = $at; |
| @@ -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, |
| @@ -2424,9 +2513,9 @@ | ||
| 2424 | 2513 | * .has-value-gradient-background { |
| 2425 | 2514 | * background: value; |
| 2426 | 2515 | * } |
| 2427 | 2516 | * |
| 2428 | - * p.has-value-gradient-background { | |
| 2517 | + * :where(p).has-value-gradient-background { | |
| 2429 | 2518 | * background: value; |
| 2430 | 2519 | * } |
| 2431 | 2520 | * |
| 2432 | 2521 | * @since 5.9.0 |
| @@ -2627,10 +2716,18 @@ | ||
| 2627 | 2716 | foreach ( $slugs as $slug ) { |
| 2628 | 2717 | $css_var = static::replace_slug_in_string( $preset_metadata['css_vars'], $slug ); |
| 2629 | 2718 | $class_name = static::replace_slug_in_string( $class, $slug ); |
| 2630 | 2719 | |
| 2631 | - // $selector is often empty, so we can save ourselves the `append_to_selector()` call then. | |
| 2632 | - $new_selector = '' === $selector ? $class_name : static::append_to_selector( $selector, $class_name ); | |
| 2720 | + /* | |
| 2721 | + * $selector is often empty (root-level presets), in which case the | |
| 2722 | + * bare class is used. For block-level presets the block selector is | |
| 2723 | + * wrapped in `:where()` so the class keeps the same 0-1-0 specificity | |
| 2724 | + * as a root-level preset. Without this, block-level palette rules | |
| 2725 | + * (e.g. `p.has-x-color`) out-rank equally-important rules that also | |
| 2726 | + * target the same property at 0-1-0, such as per-instance responsive | |
| 2727 | + * state styles. | |
| 2728 | + */ | |
| 2729 | + $new_selector = '' === $selector ? $class_name : ':where(' . $selector . ')' . $class_name; | |
| 2633 | 2730 | $stylesheet .= static::to_ruleset( |
| 2634 | 2731 | $new_selector, |
| 2635 | 2732 | array( |
| 2636 | 2733 | array( |
| @@ -2980,15 +3077,15 @@ | ||
| 2980 | 3077 | * @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set. |
| 2981 | 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. |
| 2982 | 3079 | * @since 6.7.0 Allow ref resolution of background properties. |
| 2983 | 3080 | * |
| 2984 | - * @param array $styles Styles to process. | |
| 2985 | - * @param array $settings Theme settings. | |
| 2986 | - * @param array $properties Properties metadata. | |
| 2987 | - * @param array $theme_json Theme JSON array. | |
| 2988 | - * @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. | |
| 2989 | 3086 | * @param boolean $use_root_padding Whether to add custom properties at root level. |
| 2990 | - * @return array Returns the modified $declarations. | |
| 3087 | + * @return array Returns the modified $declarations. | |
| 2991 | 3088 | */ |
| 2992 | 3089 | protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) { |
| 2993 | 3090 | if ( empty( $styles ) ) { |
| 2994 | 3091 | return array(); |
| @@ -3112,10 +3209,10 @@ | ||
| 3112 | 3209 | * @since 5.9.0 Added support for values of array type, which are returned as is. |
| 3113 | 3210 | * @since 6.1.0 Added the `$theme_json` parameter. |
| 3114 | 3211 | * @since 6.7.0 Added support for background image refs |
| 3115 | 3212 | * |
| 3116 | - * @param array $styles Styles subtree. | |
| 3117 | - * @param array $path Which property to process. | |
| 3213 | + * @param array $styles Styles subtree. | |
| 3214 | + * @param array $path Which property to process. | |
| 3118 | 3215 | * @param array $theme_json Theme JSON array. |
| 3119 | 3216 | * @return string|array Style property value. |
| 3120 | 3217 | */ |
| 3121 | 3218 | protected static function get_property_value( $styles, $path, $theme_json = null ) { |
| @@ -3487,9 +3584,9 @@ | ||
| 3487 | 3584 | * @since 6.1.0 |
| 3488 | 3585 | * |
| 3489 | 3586 | * @param array $theme_json The theme.json converted to an array. |
| 3490 | 3587 | * @param array $selectors Optional list of selectors per block. |
| 3491 | - * @param array $options { | |
| 3588 | + * @param array $options { | |
| 3492 | 3589 | * Optional. An array of options for now used for internal purposes only (may change without notice). |
| 3493 | 3590 | * |
| 3494 | 3591 | * @type bool $include_block_style_variations Includes nodes for block style variations. Default false. |
| 3495 | 3592 | * @type bool $include_node_paths_only Return only block nodes node paths. Default false. |
| @@ -3694,24 +3791,47 @@ | ||
| 3694 | 3791 | } |
| 3695 | 3792 | } |
| 3696 | 3793 | } |
| 3697 | 3794 | } |
| 3698 | - if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { | |
| 3699 | - 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 ) { | |
| 3700 | 3812 | $element_path = array( 'styles', 'blocks', $name, 'elements', $element ); |
| 3701 | 3813 | if ( $include_node_paths_only ) { |
| 3702 | - $nodes[] = array( | |
| 3703 | - 'path' => $element_path, | |
| 3704 | - ); | |
| 3814 | + if ( isset( $block_node['elements'][ $element ] ) ) { | |
| 3815 | + $nodes[] = array( | |
| 3816 | + 'path' => $element_path, | |
| 3817 | + ); | |
| 3818 | + } | |
| 3705 | 3819 | continue; |
| 3706 | 3820 | } |
| 3707 | 3821 | |
| 3822 | + if ( ! isset( $selectors[ $name ]['elements'][ $element ] ) ) { | |
| 3823 | + continue; | |
| 3824 | + } | |
| 3825 | + | |
| 3708 | 3826 | $element_selector = $selectors[ $name ]['elements'][ $element ]; |
| 3709 | 3827 | |
| 3710 | - $nodes[] = array( | |
| 3711 | - 'path' => $element_path, | |
| 3712 | - 'selector' => $element_selector, | |
| 3713 | - ); | |
| 3828 | + if ( isset( $block_node['elements'][ $element ] ) ) { | |
| 3829 | + $nodes[] = array( | |
| 3830 | + 'path' => $element_path, | |
| 3831 | + 'selector' => $element_selector, | |
| 3832 | + ); | |
| 3833 | + } | |
| 3714 | 3834 | |
| 3715 | 3835 | // Responsive element nodes: one node per breakpoint that has |
| 3716 | 3836 | // styles for this element. Cascade: a{} → @media{a{}} |
| 3717 | 3837 | foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) { |
| @@ -3726,45 +3846,29 @@ | ||
| 3726 | 3846 | |
| 3727 | 3847 | // Handle any pseudo selectors for the element. |
| 3728 | 3848 | if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) { |
| 3729 | 3849 | foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { |
| 3730 | - // Create element pseudo node if default or any responsive breakpoint has the pseudo. | |
| 3731 | - $has_element_pseudo = isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ); | |
| 3732 | - if ( ! $has_element_pseudo ) { | |
| 3733 | - foreach ( array_keys( $responsive_media_queries ) as $bp ) { | |
| 3734 | - if ( isset( $theme_json['styles']['blocks'][ $name ][ $bp ]['elements'][ $element ][ $pseudo_selector ] ) ) { | |
| 3735 | - $has_element_pseudo = true; | |
| 3736 | - break; | |
| 3737 | - } | |
| 3738 | - } | |
| 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 | + ); | |
| 3739 | 3858 | } |
| 3740 | 3859 | |
| 3741 | - if ( $has_element_pseudo ) { | |
| 3742 | - $element_pseudo_path = array( 'styles', 'blocks', $name, 'elements', $element ); | |
| 3743 | - 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 ] ) ) { | |
| 3744 | 3865 | $nodes[] = array( |
| 3745 | - '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 ], | |
| 3746 | 3869 | ); |
| 3747 | - continue; | |
| 3748 | 3870 | } |
| 3749 | - | |
| 3750 | - $nodes[] = array( | |
| 3751 | - 'path' => $element_pseudo_path, | |
| 3752 | - 'selector' => static::append_to_selector( $element_selector, $pseudo_selector ), | |
| 3753 | - ); | |
| 3754 | - | |
| 3755 | - // Responsive element pseudo nodes: one node per breakpoint | |
| 3756 | - // that has this pseudo state for this element. | |
| 3757 | - // Cascade: a:hover{} → @media{a:hover{}} | |
| 3758 | - foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) { | |
| 3759 | - if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ][ $pseudo_selector ] ) ) { | |
| 3760 | - $nodes[] = array( | |
| 3761 | - 'path' => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ), | |
| 3762 | - 'selector' => static::append_to_selector( $element_selector, $pseudo_selector ), | |
| 3763 | - 'media_query' => $responsive_media_queries[ $breakpoint ], | |
| 3764 | - ); | |
| 3765 | - } | |
| 3766 | - } | |
| 3767 | 3871 | } |
| 3768 | 3872 | } |
| 3769 | 3873 | } |
| 3770 | 3874 | } |
| @@ -3847,10 +3951,20 @@ | ||
| 3847 | 3951 | // Store variation metadata and node for layout styles generation. |
| 3848 | 3952 | // Only store if the variation has blockGap defined. |
| 3849 | 3953 | if ( isset( $style_variation_node['spacing']['blockGap'] ) ) { |
| 3850 | 3954 | // Append block selector to the variation selector for proper targeting. |
| 3851 | - $variation_metadata_with_selector = $style_variation; | |
| 3852 | - $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 | + | |
| 3853 | 3967 | $style_variation_layout_metadata[ $style_variation['selector'] ] = array( |
| 3854 | 3968 | 'metadata' => $variation_metadata_with_selector, |
| 3855 | 3969 | 'node' => $style_variation_node, |
| 3856 | 3970 | ); |
| @@ -3904,9 +4018,13 @@ | ||
| 3904 | 4018 | // Process blockGap responsive layout styles for this variation. |
| 3905 | 4019 | if ( isset( $breakpoint_node['spacing']['blockGap'] ) ) { |
| 3906 | 4020 | $variation_layout_metadata = $style_variation; |
| 3907 | 4021 | $variation_layout_metadata['selector'] = $style_variation['selector'] . $block_metadata['css']; |
| 3908 | - $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( | |
| 3909 | 4027 | $variation_layout_metadata, |
| 3910 | 4028 | array( |
| 3911 | 4029 | 'node' => $breakpoint_node, |
| 3912 | 4030 | 'media_query' => $breakpoint_media, |
| @@ -4569,9 +4687,9 @@ | ||
| 4569 | 4687 | * Gets a `default`'s preset name by a provided slug. |
| 4570 | 4688 | * |
| 4571 | 4689 | * @since 5.9.0 |
| 4572 | 4690 | * |
| 4573 | - * @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. | |
| 4574 | 4692 | * @param array $base_path The path to inspect. It's 'settings' by default. |
| 4575 | 4693 | * @return string|null |
| 4576 | 4694 | */ |
| 4577 | 4695 | protected function get_name_from_defaults( $slug, $base_path ) { |
| @@ -4619,10 +4737,10 @@ | ||
| 4619 | 4737 | * @since 5.9.0 |
| 4620 | 4738 | * @since 6.6.0 Added support for block style variation element styles and $origin parameter. |
| 4621 | 4739 | * |
| 4622 | 4740 | * @param array $theme_json Structure to sanitize. |
| 4623 | - * @param string $origin Optional. What source of data this object represents. | |
| 4624 | - * 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'. | |
| 4625 | 4743 | * @return array Sanitized structure. |
| 4626 | 4744 | */ |
| 4627 | 4745 | public static function remove_insecure_properties( $theme_json, $origin = 'theme' ) { |
| 4628 | 4746 | if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) { |
| @@ -5666,8 +5784,9 @@ | ||
| 5666 | 5784 | * This is used to convert the internal representation of variables to the CSS representation. |
| 5667 | 5785 | * For example, `var:preset|color|vivid-green-cyan` becomes `var(--wp--preset--color--vivid-green-cyan)`. |
| 5668 | 5786 | * |
| 5669 | 5787 | * @since 6.3.0 |
| 5788 | + * @since 7.2.0 Preset reference slugs are kebab-cased to match the generated custom properties. | |
| 5670 | 5789 | * @param string $value The variable such as var:preset|color|vivid-green-cyan to convert. |
| 5671 | 5790 | * @return string The converted variable. |
| 5672 | 5791 | */ |
| 5673 | 5792 | private static function convert_custom_properties( $value ) { |
| @@ -5675,14 +5794,31 @@ | ||
| 5675 | 5794 | $prefix_len = strlen( $prefix ); |
| 5676 | 5795 | $token_in = '|'; |
| 5677 | 5796 | $token_out = '--'; |
| 5678 | 5797 | if ( str_starts_with( $value, $prefix ) ) { |
| 5679 | - $unwrapped_name = str_replace( | |
| 5680 | - $token_in, | |
| 5681 | - $token_out, | |
| 5682 | - substr( $value, $prefix_len ) | |
| 5683 | - ); | |
| 5684 | - $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 ) . ')'; | |
| 5685 | 5821 | } |
| 5686 | 5822 | |
| 5687 | 5823 | return $value; |
| 5688 | 5824 | } |
| @@ -5691,9 +5827,9 @@ | ||
| 5691 | 5827 | * Given a tree, converts the internal representation of variables to the CSS representation. |
| 5692 | 5828 | * It is recursive and modifies the input in-place. |
| 5693 | 5829 | * |
| 5694 | 5830 | * @since 6.3.0 |
| 5695 | - * @param array $tree Input to process. | |
| 5831 | + * @param array $tree Input to process. | |
| 5696 | 5832 | * @return array The modified $tree. |
| 5697 | 5833 | */ |
| 5698 | 5834 | private static function resolve_custom_css_format( $tree ) { |
| 5699 | 5835 | $prefix = 'var:'; |
| @@ -5841,9 +5977,9 @@ | ||
| 5841 | 5977 | * the variation class directly to the selector that will receive the |
| 5842 | 5978 | * declarations instead of deriving it by subtracting the root selector from |
| 5843 | 5979 | * the feature selector. |
| 5844 | 5980 | * |
| 5845 | - * @param array $style_variation Style variation metadata. | |
| 5981 | + * @param array $style_variation Style variation metadata. | |
| 5846 | 5982 | * @param string $feature_selector CSS selector for the feature. |
| 5847 | 5983 | * @return string Feature selector with block style variation selector added. |
| 5848 | 5984 | */ |
| 5849 | 5985 | protected static function get_block_style_variation_feature_selector( $style_variation, $feature_selector ) { |