| @@ -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 ) { |