224 * @since 6.2.0 Added `outline-*`, and `min-height` properties. |
224 * @since 6.2.0 Added `outline-*`, and `min-height` properties. |
225 * @since 6.3.0 Added `column-count` property. |
225 * @since 6.3.0 Added `column-count` property. |
226 * @since 6.4.0 Added `writing-mode` property. |
226 * @since 6.4.0 Added `writing-mode` property. |
227 * @since 6.5.0 Added `aspect-ratio` property. |
227 * @since 6.5.0 Added `aspect-ratio` property. |
228 * @since 6.6.0 Added `background-[image|position|repeat|size]` properties. |
228 * @since 6.6.0 Added `background-[image|position|repeat|size]` properties. |
229 * |
229 * @since 6.7.0 Added `background-attachment` property. |
230 * @var array |
230 * @var array |
231 */ |
231 */ |
232 const PROPERTIES_METADATA = array( |
232 const PROPERTIES_METADATA = array( |
233 'aspect-ratio' => array( 'dimensions', 'aspectRatio' ), |
233 'aspect-ratio' => array( 'dimensions', 'aspectRatio' ), |
234 'background' => array( 'color', 'gradient' ), |
234 'background' => array( 'color', 'gradient' ), |
235 'background-color' => array( 'color', 'background' ), |
235 'background-color' => array( 'color', 'background' ), |
236 'background-image' => array( 'background', 'backgroundImage' ), |
236 'background-image' => array( 'background', 'backgroundImage' ), |
237 'background-position' => array( 'background', 'backgroundPosition' ), |
237 'background-position' => array( 'background', 'backgroundPosition' ), |
238 'background-repeat' => array( 'background', 'backgroundRepeat' ), |
238 'background-repeat' => array( 'background', 'backgroundRepeat' ), |
239 'background-size' => array( 'background', 'backgroundSize' ), |
239 'background-size' => array( 'background', 'backgroundSize' ), |
|
240 'background-attachment' => array( 'background', 'backgroundAttachment' ), |
240 'border-radius' => array( 'border', 'radius' ), |
241 'border-radius' => array( 'border', 'radius' ), |
241 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), |
242 'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ), |
242 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), |
243 'border-top-right-radius' => array( 'border', 'radius', 'topRight' ), |
243 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), |
244 'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ), |
244 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), |
245 'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ), |
576 ); |
576 ); |
577 |
577 |
578 /** |
578 /** |
579 * Defines which pseudo selectors are enabled for which elements. |
579 * Defines which pseudo selectors are enabled for which elements. |
580 * |
580 * |
581 * The order of the selectors should be: link, any-link, visited, hover, focus, active. |
581 * The order of the selectors should be: link, any-link, visited, hover, focus, focus-visible, active. |
582 * This is to ensure the user action (hover, focus and active) styles have a higher |
582 * This is to ensure the user action (hover, focus and active) styles have a higher |
583 * specificity than the visited styles, which in turn have a higher specificity than |
583 * specificity than the visited styles, which in turn have a higher specificity than |
584 * the unvisited styles. |
584 * the unvisited styles. |
585 * |
585 * |
586 * See https://core.trac.wordpress.org/ticket/56928. |
586 * See https://core.trac.wordpress.org/ticket/56928. |
587 * Note: this will affect both top-level and block-level elements. |
587 * Note: this will affect both top-level and block-level elements. |
588 * |
588 * |
589 * @since 6.1.0 |
589 * @since 6.1.0 |
590 * @since 6.2.0 Added support for ':link' and ':any-link'. |
590 * @since 6.2.0 Added support for ':link' and ':any-link'. |
|
591 * @since 6.8.0 Added support for ':focus-visible'. |
|
592 * @var array |
591 */ |
593 */ |
592 const VALID_ELEMENT_PSEUDO_SELECTORS = array( |
594 const VALID_ELEMENT_PSEUDO_SELECTORS = array( |
593 'link' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':active' ), |
595 'link' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':focus-visible', ':active' ), |
594 'button' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':active' ), |
596 'button' => array( ':link', ':any-link', ':visited', ':hover', ':focus', ':focus-visible', ':active' ), |
595 ); |
597 ); |
596 |
598 |
597 /** |
599 /** |
598 * The valid elements that can be found under styles. |
600 * The valid elements that can be found under styles. |
599 * |
601 * |
754 if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) { |
756 if ( ! in_array( $origin, static::VALID_ORIGINS, true ) ) { |
755 $origin = 'theme'; |
757 $origin = 'theme'; |
756 } |
758 } |
757 |
759 |
758 $this->theme_json = WP_Theme_JSON_Schema::migrate( $theme_json, $origin ); |
760 $this->theme_json = WP_Theme_JSON_Schema::migrate( $theme_json, $origin ); |
759 $valid_block_names = array_keys( static::get_blocks_metadata() ); |
761 $blocks_metadata = static::get_blocks_metadata(); |
|
762 $valid_block_names = array_keys( $blocks_metadata ); |
760 $valid_element_names = array_keys( static::ELEMENTS ); |
763 $valid_element_names = array_keys( static::ELEMENTS ); |
761 $valid_variations = static::get_valid_block_style_variations(); |
764 $valid_variations = static::get_valid_block_style_variations( $blocks_metadata ); |
762 $this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json, $valid_variations ); |
765 $this->theme_json = static::unwrap_shared_block_style_variations( $this->theme_json, $valid_variations ); |
763 $this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations ); |
766 $this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations ); |
764 $this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json ); |
767 $this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json ); |
765 |
768 |
766 // Internally, presets are keyed by origin. |
769 // Internally, presets are keyed by origin. |
2282 * Given a styles array, it extracts the style properties |
2294 * Given a styles array, it extracts the style properties |
2283 * and adds them to the $declarations array following the format: |
2295 * and adds them to the $declarations array following the format: |
2284 * |
2296 * |
2285 * array( |
2297 * array( |
2286 * 'name' => 'property_name', |
2298 * 'name' => 'property_name', |
2287 * 'value' => 'property_value, |
2299 * 'value' => 'property_value', |
2288 * ) |
2300 * ) |
2289 * |
2301 * |
2290 * @since 5.8.0 |
2302 * @since 5.8.0 |
2291 * @since 5.9.0 Added the `$settings` and `$properties` parameters. |
2303 * @since 5.9.0 Added the `$settings` and `$properties` parameters. |
2292 * @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters. |
2304 * @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters. |
2293 * @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set. |
2305 * @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set. |
2294 * @since 6.6.0 Pass current theme JSON settings to wp_get_typography_font_size_value(), and process background properties. |
2306 * @since 6.6.0 Pass current theme JSON settings to wp_get_typography_font_size_value(), and process background properties. |
|
2307 * @since 6.7.0 `ref` resolution of background properties, and assigning custom default values. |
2295 * |
2308 * |
2296 * @param array $styles Styles to process. |
2309 * @param array $styles Styles to process. |
2297 * @param array $settings Theme settings. |
2310 * @param array $settings Theme settings. |
2298 * @param array $properties Properties metadata. |
2311 * @param array $properties Properties metadata. |
2299 * @param array $theme_json Theme JSON array. |
2312 * @param array $theme_json Theme JSON array. |
2300 * @param string $selector The style block selector. |
2313 * @param string $selector The style block selector. |
2301 * @param boolean $use_root_padding Whether to add custom properties at root level. |
2314 * @param boolean $use_root_padding Whether to add custom properties at root level. |
2302 * @return array Returns the modified $declarations. |
2315 * @return array Returns the modified $declarations. |
2303 */ |
2316 */ |
2304 protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) { |
2317 protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) { |
|
2318 if ( empty( $styles ) ) { |
|
2319 return array(); |
|
2320 } |
|
2321 |
2305 if ( null === $properties ) { |
2322 if ( null === $properties ) { |
2306 $properties = static::PROPERTIES_METADATA; |
2323 $properties = static::PROPERTIES_METADATA; |
2307 } |
2324 } |
2308 |
2325 $declarations = array(); |
2309 $declarations = array(); |
|
2310 if ( empty( $styles ) ) { |
|
2311 return $declarations; |
|
2312 } |
|
2313 |
|
2314 $root_variable_duplicates = array(); |
2326 $root_variable_duplicates = array(); |
|
2327 $root_style_length = strlen( '--wp--style--root--' ); |
2315 |
2328 |
2316 foreach ( $properties as $css_property => $value_path ) { |
2329 foreach ( $properties as $css_property => $value_path ) { |
|
2330 if ( ! is_array( $value_path ) ) { |
|
2331 continue; |
|
2332 } |
|
2333 |
|
2334 $is_root_style = str_starts_with( $css_property, '--wp--style--root--' ); |
|
2335 if ( $is_root_style && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) { |
|
2336 continue; |
|
2337 } |
|
2338 |
2317 $value = static::get_property_value( $styles, $value_path, $theme_json ); |
2339 $value = static::get_property_value( $styles, $value_path, $theme_json ); |
2318 |
2340 |
2319 if ( str_starts_with( $css_property, '--wp--style--root--' ) && ( static::ROOT_BLOCK_SELECTOR !== $selector || ! $use_root_padding ) ) { |
|
2320 continue; |
|
2321 } |
|
2322 /* |
2341 /* |
2323 * Root-level padding styles don't currently support strings with CSS shorthand values. |
2342 * Root-level padding styles don't currently support strings with CSS shorthand values. |
2324 * This may change: https://github.com/WordPress/gutenberg/issues/40132. |
2343 * This may change: https://github.com/WordPress/gutenberg/issues/40132. |
2325 */ |
2344 */ |
2326 if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) { |
2345 if ( '--wp--style--root--padding' === $css_property && is_string( $value ) ) { |
2327 continue; |
2346 continue; |
2328 } |
2347 } |
2329 |
2348 |
2330 if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) { |
2349 if ( $is_root_style && $use_root_padding ) { |
2331 $root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) ); |
2350 $root_variable_duplicates[] = substr( $css_property, $root_style_length ); |
|
2351 } |
|
2352 |
|
2353 /* |
|
2354 * Processes background image styles. |
|
2355 * If the value is a URL, it will be converted to a CSS `url()` value. |
|
2356 * For uploaded image (images with a database ID), apply size and position defaults, |
|
2357 * equal to those applied in block supports in lib/background.php. |
|
2358 */ |
|
2359 if ( 'background-image' === $css_property && ! empty( $value ) ) { |
|
2360 $background_styles = wp_style_engine_get_styles( |
|
2361 array( 'background' => array( 'backgroundImage' => $value ) ) |
|
2362 ); |
|
2363 $value = $background_styles['declarations'][ $css_property ]; |
|
2364 } |
|
2365 if ( empty( $value ) && static::ROOT_BLOCK_SELECTOR !== $selector && ! empty( $styles['background']['backgroundImage']['id'] ) ) { |
|
2366 if ( 'background-size' === $css_property ) { |
|
2367 $value = 'cover'; |
|
2368 } |
|
2369 // If the background size is set to `contain` and no position is set, set the position to `center`. |
|
2370 if ( 'background-position' === $css_property ) { |
|
2371 $background_size = $styles['background']['backgroundSize'] ?? null; |
|
2372 $value = 'contain' === $background_size ? '50% 50%' : null; |
|
2373 } |
|
2374 } |
|
2375 |
|
2376 // Skip if empty and not "0" or value represents array of longhand values. |
|
2377 $has_missing_value = empty( $value ) && ! is_numeric( $value ); |
|
2378 if ( $has_missing_value || is_array( $value ) ) { |
|
2379 continue; |
2332 } |
2380 } |
2333 |
2381 |
2334 /* |
2382 /* |
2335 * Look up protected properties, keyed by value path. |
2383 * Look up protected properties, keyed by value path. |
2336 * Skip protected properties that are explicitly set to `null`. |
2384 * Skip protected properties that are explicitly set to `null`. |
2337 */ |
2385 */ |
2338 if ( is_array( $value_path ) ) { |
2386 $path_string = implode( '.', $value_path ); |
2339 $path_string = implode( '.', $value_path ); |
2387 if ( |
2340 if ( |
2388 isset( static::PROTECTED_PROPERTIES[ $path_string ] ) && |
2341 isset( static::PROTECTED_PROPERTIES[ $path_string ] ) && |
2389 _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null |
2342 _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null |
2390 ) { |
2343 ) { |
|
2344 continue; |
|
2345 } |
|
2346 } |
|
2347 |
|
2348 // Processes background styles. |
|
2349 if ( 'background' === $value_path[0] && isset( $styles['background'] ) ) { |
|
2350 $background_styles = wp_style_engine_get_styles( array( 'background' => $styles['background'] ) ); |
|
2351 $value = isset( $background_styles['declarations'][ $css_property ] ) ? $background_styles['declarations'][ $css_property ] : $value; |
|
2352 } |
|
2353 |
|
2354 // Skip if empty and not "0" or value represents array of longhand values. |
|
2355 $has_missing_value = empty( $value ) && ! is_numeric( $value ); |
|
2356 if ( $has_missing_value || is_array( $value ) ) { |
|
2357 continue; |
2391 continue; |
2358 } |
2392 } |
2359 |
2393 |
2360 // Calculates fluid typography rules where available. |
2394 // Calculates fluid typography rules where available. |
2361 if ( 'font-size' === $css_property ) { |
2395 if ( 'font-size' === $css_property ) { |
2424 return ''; |
2459 return ''; |
2425 } |
2460 } |
2426 |
2461 |
2427 /* |
2462 /* |
2428 * This converts references to a path to the value at that path |
2463 * This converts references to a path to the value at that path |
2429 * where the values is an array with a "ref" key, pointing to a path. |
2464 * where the value is an array with a "ref" key, pointing to a path. |
2430 * For example: { "ref": "style.color.background" } => "#fff". |
2465 * For example: { "ref": "style.color.background" } => "#fff". |
|
2466 * In the case of backgroundImage, if both a ref and a URL are present in the value, |
|
2467 * the URL takes precedence and the ref is ignored. |
2431 */ |
2468 */ |
2432 if ( is_array( $value ) && isset( $value['ref'] ) ) { |
2469 if ( is_array( $value ) && isset( $value['ref'] ) ) { |
2433 $value_path = explode( '.', $value['ref'] ); |
2470 $value_path = explode( '.', $value['ref'] ); |
2434 $ref_value = _wp_array_get( $theme_json, $value_path ); |
2471 $ref_value = _wp_array_get( $theme_json, $value_path ); |
|
2472 // Background Image refs can refer to a string or an array containing a URL string. |
|
2473 $ref_value_url = $ref_value['url'] ?? null; |
2435 // Only use the ref value if we find anything. |
2474 // Only use the ref value if we find anything. |
2436 if ( ! empty( $ref_value ) && is_string( $ref_value ) ) { |
2475 if ( ! empty( $ref_value ) && ( is_string( $ref_value ) || is_string( $ref_value_url ) ) ) { |
2437 $value = $ref_value; |
2476 $value = $ref_value; |
2438 } |
2477 } |
2439 |
2478 |
2440 if ( is_array( $ref_value ) && isset( $ref_value['ref'] ) ) { |
2479 if ( is_array( $ref_value ) && isset( $ref_value['ref'] ) ) { |
2441 $path_string = json_encode( $path ); |
2480 $path_string = json_encode( $path ); |
2654 * An internal method to get the block nodes from a theme.json file. |
2693 * An internal method to get the block nodes from a theme.json file. |
2655 * |
2694 * |
2656 * @since 6.1.0 |
2695 * @since 6.1.0 |
2657 * @since 6.3.0 Refactored and stabilized selectors API. |
2696 * @since 6.3.0 Refactored and stabilized selectors API. |
2658 * @since 6.6.0 Added optional selectors and options for generating block nodes. |
2697 * @since 6.6.0 Added optional selectors and options for generating block nodes. |
|
2698 * @since 6.7.0 Added $include_node_paths_only option. |
2659 * |
2699 * |
2660 * @param array $theme_json The theme.json converted to an array. |
2700 * @param array $theme_json The theme.json converted to an array. |
2661 * @param array $selectors Optional list of selectors per block. |
2701 * @param array $selectors Optional list of selectors per block. |
2662 * @param array $options { |
2702 * @param array $options { |
2663 * Optional. An array of options for now used for internal purposes only (may change without notice). |
2703 * Optional. An array of options for now used for internal purposes only (may change without notice). |
2664 * |
2704 * |
2665 * @type bool $include_block_style_variations Includes nodes for block style variations. Default false. |
2705 * @type bool $include_block_style_variations Include nodes for block style variations. Default false. |
|
2706 * @type bool $include_node_paths_only Return only block nodes node paths. Default false. |
2666 * } |
2707 * } |
2667 * @return array The block nodes in theme.json. |
2708 * @return array The block nodes in theme.json. |
2668 */ |
2709 */ |
2669 private static function get_block_nodes( $theme_json, $selectors = array(), $options = array() ) { |
2710 private static function get_block_nodes( $theme_json, $selectors = array(), $options = array() ) { |
2670 $selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors; |
2711 $nodes = array(); |
2671 $nodes = array(); |
2712 |
2672 if ( ! isset( $theme_json['styles'] ) ) { |
|
2673 return $nodes; |
|
2674 } |
|
2675 |
|
2676 // Blocks. |
|
2677 if ( ! isset( $theme_json['styles']['blocks'] ) ) { |
2713 if ( ! isset( $theme_json['styles']['blocks'] ) ) { |
2678 return $nodes; |
2714 return $nodes; |
2679 } |
2715 } |
2680 |
2716 |
|
2717 $include_variations = $options['include_block_style_variations'] ?? false; |
|
2718 $include_node_paths_only = $options['include_node_paths_only'] ?? false; |
|
2719 |
|
2720 // If only node paths are to be returned, skip selector assignment. |
|
2721 if ( ! $include_node_paths_only ) { |
|
2722 $selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors; |
|
2723 } |
|
2724 |
2681 foreach ( $theme_json['styles']['blocks'] as $name => $node ) { |
2725 foreach ( $theme_json['styles']['blocks'] as $name => $node ) { |
2682 $selector = null; |
2726 $node_path = array( 'styles', 'blocks', $name ); |
2683 if ( isset( $selectors[ $name ]['selector'] ) ) { |
2727 if ( $include_node_paths_only ) { |
2684 $selector = $selectors[ $name ]['selector']; |
2728 $variation_paths = array(); |
2685 } |
2729 if ( $include_variations && isset( $node['variations'] ) ) { |
2686 |
2730 foreach ( $node['variations'] as $variation => $variation_node ) { |
2687 $duotone_selector = null; |
2731 $variation_paths[] = array( |
2688 if ( isset( $selectors[ $name ]['duotone'] ) ) { |
2732 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ), |
2689 $duotone_selector = $selectors[ $name ]['duotone']; |
2733 ); |
2690 } |
2734 } |
2691 |
2735 } |
2692 $feature_selectors = null; |
2736 $node = array( |
2693 if ( isset( $selectors[ $name ]['selectors'] ) ) { |
2737 'path' => $node_path, |
2694 $feature_selectors = $selectors[ $name ]['selectors']; |
2738 ); |
2695 } |
2739 if ( ! empty( $variation_paths ) ) { |
2696 |
2740 $node['variations'] = $variation_paths; |
2697 $variation_selectors = array(); |
2741 } |
2698 $include_variations = $options['include_block_style_variations'] ?? false; |
2742 $nodes[] = $node; |
2699 if ( $include_variations && isset( $node['variations'] ) ) { |
2743 } else { |
2700 foreach ( $node['variations'] as $variation => $node ) { |
2744 $selector = null; |
2701 $variation_selectors[] = array( |
2745 if ( isset( $selectors[ $name ]['selector'] ) ) { |
2702 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ), |
2746 $selector = $selectors[ $name ]['selector']; |
2703 'selector' => $selectors[ $name ]['styleVariations'][ $variation ], |
2747 } |
2704 ); |
2748 |
2705 } |
2749 $duotone_selector = null; |
2706 } |
2750 if ( isset( $selectors[ $name ]['duotone'] ) ) { |
2707 |
2751 $duotone_selector = $selectors[ $name ]['duotone']; |
2708 $nodes[] = array( |
2752 } |
2709 'name' => $name, |
2753 |
2710 'path' => array( 'styles', 'blocks', $name ), |
2754 $feature_selectors = null; |
2711 'selector' => $selector, |
2755 if ( isset( $selectors[ $name ]['selectors'] ) ) { |
2712 'selectors' => $feature_selectors, |
2756 $feature_selectors = $selectors[ $name ]['selectors']; |
2713 'duotone' => $duotone_selector, |
2757 } |
2714 'features' => $feature_selectors, |
2758 |
2715 'variations' => $variation_selectors, |
2759 $variation_selectors = array(); |
2716 ); |
2760 if ( $include_variations && isset( $node['variations'] ) ) { |
|
2761 foreach ( $node['variations'] as $variation => $node ) { |
|
2762 $variation_selectors[] = array( |
|
2763 'path' => array( 'styles', 'blocks', $name, 'variations', $variation ), |
|
2764 'selector' => $selectors[ $name ]['styleVariations'][ $variation ], |
|
2765 ); |
|
2766 } |
|
2767 } |
|
2768 |
|
2769 $nodes[] = array( |
|
2770 'name' => $name, |
|
2771 'path' => $node_path, |
|
2772 'selector' => $selector, |
|
2773 'selectors' => $feature_selectors, |
|
2774 'duotone' => $duotone_selector, |
|
2775 'features' => $feature_selectors, |
|
2776 'variations' => $variation_selectors, |
|
2777 'css' => $selector, |
|
2778 ); |
|
2779 } |
2717 |
2780 |
2718 if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { |
2781 if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { |
2719 foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { |
2782 foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { |
|
2783 $node_path = array( 'styles', 'blocks', $name, 'elements', $element ); |
|
2784 if ( $include_node_paths_only ) { |
|
2785 $nodes[] = array( |
|
2786 'path' => $node_path, |
|
2787 ); |
|
2788 continue; |
|
2789 } |
|
2790 |
2720 $nodes[] = array( |
2791 $nodes[] = array( |
2721 'path' => array( 'styles', 'blocks', $name, 'elements', $element ), |
2792 'path' => $node_path, |
2722 'selector' => $selectors[ $name ]['elements'][ $element ], |
2793 'selector' => $selectors[ $name ]['elements'][ $element ], |
2723 ); |
2794 ); |
2724 |
2795 |
2725 // Handle any pseudo selectors for the element. |
2796 // Handle any pseudo selectors for the element. |
2726 if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) { |
2797 if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) { |
2727 foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { |
2798 foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { |
2728 if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) { |
2799 if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) { |
|
2800 $node_path = array( 'styles', 'blocks', $name, 'elements', $element ); |
|
2801 if ( $include_node_paths_only ) { |
|
2802 $nodes[] = array( |
|
2803 'path' => $node_path, |
|
2804 ); |
|
2805 continue; |
|
2806 } |
|
2807 |
2729 $nodes[] = array( |
2808 $nodes[] = array( |
2730 'path' => array( 'styles', 'blocks', $name, 'elements', $element ), |
2809 'path' => $node_path, |
2731 'selector' => static::append_to_selector( $selectors[ $name ]['elements'][ $element ], $pseudo_selector ), |
2810 'selector' => static::append_to_selector( $selectors[ $name ]['elements'][ $element ], $pseudo_selector ), |
2732 ); |
2811 ); |
2733 } |
2812 } |
2734 } |
2813 } |
2735 } |
2814 } |
2771 // Generate any feature/subfeature style declarations for the current style variation. |
2849 // Generate any feature/subfeature style declarations for the current style variation. |
2772 $variation_declarations = static::get_feature_declarations_for_node( $block_metadata, $style_variation_node ); |
2850 $variation_declarations = static::get_feature_declarations_for_node( $block_metadata, $style_variation_node ); |
2773 |
2851 |
2774 // Combine selectors with style variation's selector and add to overall style variation declarations. |
2852 // Combine selectors with style variation's selector and add to overall style variation declarations. |
2775 foreach ( $variation_declarations as $current_selector => $new_declarations ) { |
2853 foreach ( $variation_declarations as $current_selector => $new_declarations ) { |
2776 // If current selector includes block classname, remove it but leave the whitespace in. |
2854 /* |
2777 $shortened_selector = str_replace( $block_metadata['selector'] . ' ', ' ', $current_selector ); |
2855 * Clean up any whitespace between comma separated selectors. |
|
2856 * This prevents these spaces breaking compound selectors such as: |
|
2857 * - `.wp-block-list:not(.wp-block-list .wp-block-list)` |
|
2858 * - `.wp-block-image img, .wp-block-image.my-class img` |
|
2859 */ |
|
2860 $clean_current_selector = preg_replace( '/,\s+/', ',', $current_selector ); |
|
2861 $shortened_selector = str_replace( $block_metadata['selector'], '', $clean_current_selector ); |
2778 |
2862 |
2779 // Prepend the variation selector to the current selector. |
2863 // Prepend the variation selector to the current selector. |
2780 $split_selectors = explode( ',', $shortened_selector ); |
2864 $split_selectors = explode( ',', $shortened_selector ); |
2781 $updated_selectors = array_map( |
2865 $updated_selectors = array_map( |
2782 static function ( $split_selector ) use ( $clean_style_variation_selector ) { |
2866 static function ( $split_selector ) use ( $clean_style_variation_selector ) { |
2972 $css = ''; |
3060 $css = ''; |
2973 $settings = isset( $this->theme_json['settings'] ) ? $this->theme_json['settings'] : array(); |
3061 $settings = isset( $this->theme_json['settings'] ) ? $this->theme_json['settings'] : array(); |
2974 $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; |
3062 $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; |
2975 |
3063 |
2976 /* |
3064 /* |
2977 * If there are content and wide widths in theme.json, output them |
3065 * If there are content and wide widths in theme.json, output them |
2978 * as custom properties on the body element so all blocks can use them. |
3066 * as custom properties on the body element so all blocks can use them. |
2979 */ |
3067 */ |
2980 if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { |
3068 if ( isset( $settings['layout']['contentSize'] ) || isset( $settings['layout']['wideSize'] ) ) { |
2981 $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; |
3069 $content_size = isset( $settings['layout']['contentSize'] ) ? $settings['layout']['contentSize'] : $settings['layout']['wideSize']; |
2982 $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; |
3070 $content_size = static::is_safe_css_declaration( 'max-width', $content_size ) ? $content_size : 'initial'; |
2983 $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; |
3071 $wide_size = isset( $settings['layout']['wideSize'] ) ? $settings['layout']['wideSize'] : $settings['layout']['contentSize']; |
2984 $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; |
3072 $wide_size = static::is_safe_css_declaration( 'max-width', $wide_size ) ? $wide_size : 'initial'; |
2985 $css .= static::ROOT_CSS_PROPERTIES_SELECTOR . ' { --wp--style--global--content-size: ' . $content_size . ';'; |
3073 $css .= static::ROOT_CSS_PROPERTIES_SELECTOR . ' { --wp--style--global--content-size: ' . $content_size . ';'; |
2986 $css .= '--wp--style--global--wide-size: ' . $wide_size . '; }'; |
3074 $css .= '--wp--style--global--wide-size: ' . $wide_size . '; }'; |
2987 } |
3075 } |
2988 |
3076 |
2989 /* |
3077 /* |
2990 * Reset default browser margin on the body element. |
3078 * Reset default browser margin on the body element. |
2991 * This is set on the body selector **before** generating the ruleset |
3079 * This is set on the body selector **before** generating the ruleset |
2992 * from the `theme.json`. This is to ensure that if the `theme.json` declares |
3080 * from the `theme.json`. This is to ensure that if the `theme.json` declares |
2993 * `margin` in its `spacing` declaration for the `body` element then these |
3081 * `margin` in its `spacing` declaration for the `body` element then these |
2994 * user-generated values take precedence in the CSS cascade. |
3082 * user-generated values take precedence in the CSS cascade. |
2995 * @link https://github.com/WordPress/gutenberg/issues/36147. |
3083 * @link https://github.com/WordPress/gutenberg/issues/36147. |
2996 */ |
3084 */ |
2997 $css .= ':where(body) { margin: 0; }'; |
3085 $css .= ':where(body) { margin: 0; }'; |
2998 |
3086 |
2999 if ( $use_root_padding ) { |
3087 if ( $use_root_padding ) { |
3000 // Top and bottom padding are applied to the outer block container. |
3088 // Top and bottom padding are applied to the outer block container. |
3001 $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; |
3089 $css .= '.wp-site-blocks { padding-top: var(--wp--style--root--padding-top); padding-bottom: var(--wp--style--root--padding-bottom); }'; |
3499 } else { |
3602 } else { |
3500 $theme_json['settings'] = $sanitized['settings']; |
3603 $theme_json['settings'] = $sanitized['settings']; |
3501 } |
3604 } |
3502 |
3605 |
3503 return $theme_json; |
3606 return $theme_json; |
|
3607 } |
|
3608 |
|
3609 /** |
|
3610 * Remove insecure element styles within a variation or block. |
|
3611 * |
|
3612 * @since 6.8.0 |
|
3613 * |
|
3614 * @param array $elements The elements to process. |
|
3615 * @return array The sanitized elements styles. |
|
3616 */ |
|
3617 protected static function remove_insecure_element_styles( $elements ) { |
|
3618 $sanitized = array(); |
|
3619 $valid_element_names = array_keys( static::ELEMENTS ); |
|
3620 |
|
3621 foreach ( $valid_element_names as $element_name ) { |
|
3622 $element_input = $elements[ $element_name ] ?? null; |
|
3623 if ( $element_input ) { |
|
3624 $element_output = static::remove_insecure_styles( $element_input ); |
|
3625 |
|
3626 if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) { |
|
3627 foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) { |
|
3628 if ( isset( $element_input[ $pseudo_selector ] ) ) { |
|
3629 $element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] ); |
|
3630 } |
|
3631 } |
|
3632 } |
|
3633 |
|
3634 $sanitized[ $element_name ] = $element_output; |
|
3635 } |
|
3636 } |
|
3637 return $sanitized; |
|
3638 } |
|
3639 |
|
3640 /** |
|
3641 * Remove insecure styles from inner blocks and their elements. |
|
3642 * |
|
3643 * @since 6.8.0 |
|
3644 * |
|
3645 * @param array $blocks The block styles to process. |
|
3646 * @return array Sanitized block type styles. |
|
3647 */ |
|
3648 protected static function remove_insecure_inner_block_styles( $blocks ) { |
|
3649 $sanitized = array(); |
|
3650 foreach ( $blocks as $block_type => $block_input ) { |
|
3651 $block_output = static::remove_insecure_styles( $block_input ); |
|
3652 |
|
3653 if ( isset( $block_input['elements'] ) ) { |
|
3654 $block_output['elements'] = static::remove_insecure_element_styles( $block_input['elements'] ); |
|
3655 } |
|
3656 |
|
3657 $sanitized[ $block_type ] = $block_output; |
|
3658 } |
|
3659 return $sanitized; |
3504 } |
3660 } |
3505 |
3661 |
3506 /** |
3662 /** |
3507 * Processes a setting node and returns the same node |
3663 * Processes a setting node and returns the same node |
3508 * without the insecure settings. |
3664 * without the insecure settings. |