Make WordPress Core

Changeset 62444


Ignore:
Timestamp:
06/02/2026 06:03:58 AM (3 days ago)
Author:
isabel_brison
Message:

Editor: add responsive global styles for blocks.

Uses block states to represent tablet and mobile breakpoints for block global styles.

Props isabel_brison, ramonopoly.
See #65164.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-theme-json.php

    r62415 r62444  
    646646
    647647    /**
     648     * Responsive breakpoint state keys and their corresponding CSS media queries.
     649     * These are available for all blocks and wrap their styles in the given media query.
     650     * Keep in sync with RESPONSIVE_BREAKPOINTS in packages/global-styles-engine/src/core/render.tsx.
     651     *
     652     * @since 7.1.0
     653     * @var array
     654     */
     655    const RESPONSIVE_BREAKPOINTS = array(
     656        'mobile' => '@media (width <= 480px)',
     657        'tablet' => '@media (480px < width <= 782px)',
     658    );
     659
     660    /**
    648661     * The valid elements that can be found under styles.
    649662     *
     
    10551068
    10561069        /*
    1057          * Set allowed element pseudo selectors based on per element allow list.
     1070         * Set allowed element pseudo selectors and responsive breakpoint states.
    10581071         * Target data structure in schema:
    10591072         * e.g.
    10601073         * - top level elements: `$schema['styles']['elements']['link'][':hover']`.
    10611074         * - block level elements: `$schema['styles']['blocks']['core/button']['elements']['link'][':hover']`.
     1075         * - block responsive elements: `$schema['styles']['blocks']['core/button']['tablet']['elements']['link'][':hover']`.
    10621076         */
    10631077        foreach ( $valid_element_names as $element ) {
     
    10691083                }
    10701084            }
     1085
     1086            // Add responsive breakpoint states for elements.
     1087            foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint_state ) {
     1088                $schema_styles_elements[ $element ][ $breakpoint_state ] = $styles_non_top_level;
     1089            }
    10711090        }
    10721091
     
    10761095        /*
    10771096         * Generate a schema for blocks.
    1078          * - Block styles can contain `elements` & `variations` definitions.
     1097         * - Block styles can contain `elements`, `variations`, and responsive breakpoint state definitions.
    10791098         * - Variations definitions cannot be nested.
    1080          * - Variations can contain styles for inner `blocks`.
    1081          * - Variation inner `blocks` styles can contain `elements`.
     1099         * - Variations can contain styles for inner `blocks`, `elements`, and responsive breakpoint states.
     1100         * - Variation inner `blocks` styles can contain `elements` and responsive breakpoint states.
    10821101         *
    1083          * As each variation needs a `blocks` schema but further nested
    1084          * inner `blocks`, the overall schema will be generated in multiple passes.
     1102         * As each variation needs both a `blocks` schema and responsive `blocks` schemas
     1103         * for further nested inner `blocks`, the overall schema is generated in multiple passes.
    10851104         */
    10861105        foreach ( $valid_block_names as $block ) {
     
    10881107            $schema_styles_blocks[ $block ]             = $styles_non_top_level;
    10891108            $schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
     1109
     1110            // Add responsive breakpoint states for all blocks.
     1111            foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint_state ) {
     1112                $schema_styles_blocks[ $block ][ $breakpoint_state ]             = $styles_non_top_level;
     1113                $schema_styles_blocks[ $block ][ $breakpoint_state ]['elements'] = $schema_styles_elements;
     1114
     1115                if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
     1116                    foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) {
     1117                        $schema_styles_blocks[ $block ][ $breakpoint_state ][ $pseudo_selector ] = $styles_non_top_level;
     1118                    }
     1119                }
     1120            }
    10901121
    10911122            // Add pseudo-selectors for blocks that support them
     
    11191150                foreach ( $style_variation_names as $variation_name ) {
    11201151                    $variation_schema = $block_style_variation_styles;
     1152
     1153                    // Add responsive breakpoint states to block style variations.
     1154                    foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint_state ) {
     1155                        $variation_schema[ $breakpoint_state ]             = $styles_non_top_level;
     1156                        $variation_schema[ $breakpoint_state ]['elements'] = $schema_styles_elements;
     1157                        $variation_schema[ $breakpoint_state ]['blocks']   = $schema_styles_blocks;
     1158
     1159                        if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] ) ) {
     1160                            foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block ] as $pseudo_selector ) {
     1161                                $variation_schema[ $breakpoint_state ][ $pseudo_selector ] = $styles_non_top_level;
     1162                            }
     1163                        }
     1164                    }
    11211165
    11221166                    // Add pseudo-selectors to variations for blocks that support them
     
    18871931            }
    18881932        }
     1933
     1934        if ( ! empty( $options['media_query'] ) && ! empty( $block_rules ) ) {
     1935            $block_rules = $options['media_query'] . '{' . $block_rules . '}';
     1936        }
     1937
    18891938        return $block_rules;
    18901939    }
     
    28742923
    28752924                $variation_selectors = array();
     2925
    28762926                if ( $include_variations && isset( $node['variations'] ) ) {
    28772927                    foreach ( $node['variations'] as $variation => $node ) {
     
    28882938                    'selector'   => $selector,
    28892939                    'selectors'  => $feature_selectors,
     2940                    'elements'   => $selectors[ $name ]['elements'] ?? array(),
    28902941                    'duotone'    => $duotone_selector,
    2891                     'features'   => $feature_selectors,
    28922942                    'variations' => $variation_selectors,
    28932943                    'css'        => $selector,
    28942944                );
    28952945
     2946                // Responsive block nodes: emit one node per breakpoint that has styles.
     2947                // These are rendered immediately after the base block node so that
     2948                // the cascade order is: .block{} → @media{.block{}}
     2949                foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     2950                    if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ] ) ) {
     2951                        $nodes[] = array(
     2952                            'name'        => $name,
     2953                            'path'        => array( 'styles', 'blocks', $name, $breakpoint ),
     2954                            'media_query' => static::RESPONSIVE_BREAKPOINTS[ $breakpoint ],
     2955                            'selector'    => $selector,
     2956                            'selectors'   => $feature_selectors,
     2957                            'elements'    => $selectors[ $name ]['elements'] ?? array(),
     2958                            'variations'  => $variation_selectors,
     2959                            'css'         => $selector,
     2960                        );
     2961                    }
     2962                }
     2963
    28962964                // Handle any pseudo selectors for the block.
    28972965                if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] ) ) {
    28982966                    foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] as $pseudo_selector ) {
    2899                         if ( isset( $theme_json['styles']['blocks'][ $name ][ $pseudo_selector ] ) ) {
     2967                        $has_pseudo            = isset( $theme_json['styles']['blocks'][ $name ][ $pseudo_selector ] );
     2968                        $has_responsive_pseudo = false;
     2969                        foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     2970                            if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ][ $pseudo_selector ] ) ) {
     2971                                $has_responsive_pseudo = true;
     2972                                break;
     2973                            }
     2974                        }
     2975
     2976                        if ( ! $has_pseudo && ! $has_responsive_pseudo ) {
     2977                            continue;
     2978                        }
     2979
     2980                        /*
     2981                         * Append the pseudo-selector to each feature selector so that
     2982                         * get_feature_declarations_for_node generates CSS scoped to the
     2983                         * pseudo-state (e.g. '.wp-block-button:hover') rather than the
     2984                         * default state (e.g. '.wp-block-button').
     2985                         */
     2986                        $pseudo_feature_selectors = array();
     2987                        foreach ( $feature_selectors ?? array() as $feature => $feature_selector ) {
     2988                            if ( is_array( $feature_selector ) ) {
     2989                                $pseudo_feature_selectors[ $feature ] = array();
     2990                                foreach ( $feature_selector as $subfeature => $subfeature_selector ) {
     2991                                    $pseudo_feature_selectors[ $feature ][ $subfeature ] = static::append_to_selector( $subfeature_selector, $pseudo_selector );
     2992                                }
     2993                            } else {
     2994                                $pseudo_feature_selectors[ $feature ] = static::append_to_selector( $feature_selector, $pseudo_selector );
     2995                            }
     2996                        }
     2997
     2998                        if ( $has_pseudo ) {
    29002999                            $nodes[] = array(
    29013000                                'name'       => $name,
    29023001                                'path'       => array( 'styles', 'blocks', $name, $pseudo_selector ),
    29033002                                'selector'   => static::append_to_selector( $selector, $pseudo_selector ),
    2904                                 'selectors'  => $feature_selectors,
     3003                                'selectors'  => $pseudo_feature_selectors,
     3004                                'elements'   => $selectors[ $name ]['elements'] ?? array(),
    29053005                                'duotone'    => $duotone_selector,
    29063006                                'variations' => $variation_selectors,
     
    29083008                            );
    29093009                        }
    2910                     }
    2911                 }
    2912             }
    2913 
     3010
     3011                        // Responsive pseudo nodes: emit one node per breakpoint that has
     3012                        // this pseudo state, immediately after the default pseudo node.
     3013                        // Cascade order: .block:hover{} → @media{.block:hover{}}
     3014                        foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     3015                            if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ][ $pseudo_selector ] ) ) {
     3016                                $nodes[] = array(
     3017                                    'name'        => $name,
     3018                                    'path'        => array( 'styles', 'blocks', $name, $breakpoint, $pseudo_selector ),
     3019                                    'media_query' => static::RESPONSIVE_BREAKPOINTS[ $breakpoint ],
     3020                                    'selector'    => static::append_to_selector( $selector, $pseudo_selector ),
     3021                                    'selectors'   => $pseudo_feature_selectors,
     3022                                    'elements'    => $selectors[ $name ]['elements'] ?? array(),
     3023                                    'variations'  => $variation_selectors,
     3024                                    'css'         => static::append_to_selector( $selector, $pseudo_selector ),
     3025                                );
     3026                            }
     3027                        }
     3028                    }
     3029                }
     3030            }
    29143031            if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
    29153032                foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) {
    2916                     $node_path = array( 'styles', 'blocks', $name, 'elements', $element );
    2917 
     3033                    $element_path = array( 'styles', 'blocks', $name, 'elements', $element );
    29183034                    if ( $include_node_paths_only ) {
    29193035                        $nodes[] = array(
    2920                             'path' => $node_path,
     3036                            'path' => $element_path,
    29213037                        );
    29223038                        continue;
    29233039                    }
    29243040
     3041                    $element_selector = $selectors[ $name ]['elements'][ $element ];
     3042
    29253043                    $nodes[] = array(
    2926                         'path'     => $node_path,
    2927                         'selector' => $selectors[ $name ]['elements'][ $element ],
     3044                        'path'     => $element_path,
     3045                        'selector' => $element_selector,
    29283046                    );
     3047
     3048                    // Responsive element nodes: one node per breakpoint that has
     3049                    // styles for this element. Cascade: a{} → @media{a{}}
     3050                    foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     3051                        if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ] ) ) {
     3052                            $nodes[] = array(
     3053                                'path'        => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ),
     3054                                'selector'    => $element_selector,
     3055                                'media_query' => static::RESPONSIVE_BREAKPOINTS[ $breakpoint ],
     3056                            );
     3057                        }
     3058                    }
    29293059
    29303060                    // Handle any pseudo selectors for the element.
    29313061                    if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) {
    29323062                        foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
    2933                             if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) {
    2934                                 $node_path = array( 'styles', 'blocks', $name, 'elements', $element );
     3063                            // Create element pseudo node if default or any responsive breakpoint has the pseudo.
     3064                            $has_element_pseudo = isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] );
     3065                            if ( ! $has_element_pseudo ) {
     3066                                foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $bp ) {
     3067                                    if ( isset( $theme_json['styles']['blocks'][ $name ][ $bp ]['elements'][ $element ][ $pseudo_selector ] ) ) {
     3068                                        $has_element_pseudo = true;
     3069                                        break;
     3070                                    }
     3071                                }
     3072                            }
     3073
     3074                            if ( $has_element_pseudo ) {
     3075                                $element_pseudo_path = array( 'styles', 'blocks', $name, 'elements', $element );
     3076                                if ( $include_node_paths_only ) {
     3077                                    $nodes[] = array(
     3078                                        'path' => $element_pseudo_path,
     3079                                    );
     3080                                    continue;
     3081                                }
    29353082
    29363083                                $nodes[] = array(
    2937                                     'path'     => $node_path,
    2938                                     'selector' => static::append_to_selector( $selectors[ $name ]['elements'][ $element ], $pseudo_selector ),
     3084                                    'path'     => $element_pseudo_path,
     3085                                    'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
    29393086                                );
     3087
     3088                                // Responsive element pseudo nodes: one node per breakpoint
     3089                                // that has this pseudo state for this element.
     3090                                // Cascade: a:hover{} → @media{a:hover{}}
     3091                                foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     3092                                    if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ][ $pseudo_selector ] ) ) {
     3093                                        $nodes[] = array(
     3094                                            'path'        => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ),
     3095                                            'selector'    => static::append_to_selector( $element_selector, $pseudo_selector ),
     3096                                            'media_query' => static::RESPONSIVE_BREAKPOINTS[ $breakpoint ],
     3097                                        );
     3098                                    }
     3099                                }
    29403100                            }
    29413101                        }
     
    29663126        $feature_declarations = static::get_feature_declarations_for_node( $block_metadata, $node );
    29673127        $is_root_selector     = static::ROOT_BLOCK_SELECTOR === $selector;
     3128        $media_query          = $block_metadata['media_query'] ?? null;
    29683129
    29693130        // Update text indent selector for paragraph blocks based on the textIndent setting.
    29703131        $block_name           = $block_metadata['name'] ?? null;
    29713132        $feature_declarations = static::update_paragraph_text_indent_selector( $feature_declarations, $settings, $block_name );
     3133        $block_elements       = $block_metadata['elements'] ?? array();
    29723134
    29733135        // If there are style variations, generate the declarations for them, including any feature selectors the block may have.
    29743136        $style_variation_declarations    = array();
    29753137        $style_variation_custom_css      = array();
     3138        $style_variation_responsive_css  = array();
    29763139        $style_variation_layout_metadata = array();
    2977         if ( ! empty( $block_metadata['variations'] ) ) {
     3140        if ( ! $media_query && ! empty( $block_metadata['variations'] ) ) {
    29783141            foreach ( $block_metadata['variations'] as $style_variation ) {
    29793142                $style_variation_node           = _wp_array_get( $this->theme_json, $style_variation['path'], array() );
     
    30183181                    $block_name = $block_metadata['name'];
    30193182                } elseif ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 3 ) {
    3020                     $block_name = $block_metadata['path'][2];
     3183                    $block_name = static::get_block_name_from_metadata_path( $block_metadata );
    30213184                } else {
    30223185                    $block_name = null;
     
    30413204                    );
    30423205                }
     3206
     3207                // Store responsive breakpoint CSS for the style variation.
     3208                // This includes both base properties and feature-level selectors.
     3209                $variation_responsive_css = '';
     3210
     3211                foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     3212                    if ( ! isset( $style_variation_node[ $breakpoint ] ) ) {
     3213                        continue;
     3214                    }
     3215
     3216                    $breakpoint_node  = $style_variation_node[ $breakpoint ];
     3217                    $breakpoint_media = static::RESPONSIVE_BREAKPOINTS[ $breakpoint ];
     3218                    // Process feature-level declarations for this breakpoint.
     3219                    $breakpoint_feature_declarations = static::get_feature_declarations_for_node( $block_metadata, $breakpoint_node );
     3220                    $breakpoint_feature_declarations = static::update_paragraph_text_indent_selector( $breakpoint_feature_declarations, $settings, $block_name );
     3221                    foreach ( $breakpoint_feature_declarations as $feature_selector => $feature_decl ) {
     3222                        $clean_feature_selector = preg_replace( '/,\s+/', ',', $feature_selector );
     3223                        $shortened_selector     = str_replace( $block_metadata['selector'], '', $clean_feature_selector );
     3224
     3225                        if ( $block_metadata['selector'] && ! str_contains( $clean_feature_selector, $block_metadata['selector'] ) ) {
     3226                            /*
     3227                             * Feature selector is block-level (e.g. `.wp-block-button` for
     3228                             * dimensions/width) — apply the variation class directly to it.
     3229                             */
     3230                            $feature_element_selector = str_replace( $shortened_selector, '', $clean_style_variation_selector );
     3231                            $combined_selectors       = str_replace( $feature_element_selector, '', $clean_style_variation_selector );
     3232                        } else {
     3233                            // Prepend the variation selector to the current selector.
     3234                            $split_selectors    = explode( ',', $shortened_selector );
     3235                            $updated_selectors  = array_map(
     3236                                static function ( $split_selector ) use ( $clean_style_variation_selector ) {
     3237                                    return $clean_style_variation_selector . $split_selector;
     3238                                },
     3239                                $split_selectors
     3240                            );
     3241                            $combined_selectors = implode( ',', $updated_selectors );
     3242                        }
     3243
     3244                        $feature_ruleset           = static::to_ruleset( ':root :where(' . $combined_selectors . ')', $feature_decl );
     3245                        $variation_responsive_css .= $breakpoint_media . '{' . $feature_ruleset . '}';
     3246                    }
     3247
     3248                    // Process base properties for this breakpoint.
     3249                    $breakpoint_declarations = static::compute_style_properties( $breakpoint_node, $settings, null, $this->theme_json );
     3250                    if ( ! empty( $breakpoint_declarations ) ) {
     3251                        $base_ruleset              = static::to_ruleset( ':root :where(' . $style_variation['selector'] . ')', $breakpoint_declarations );
     3252                        $variation_responsive_css .= $breakpoint_media . '{' . $base_ruleset . '}';
     3253                    }
     3254
     3255                    $breakpoint_pseudo_declarations = static::process_pseudo_selectors( $breakpoint_node, $style_variation['selector'], $settings, $block_name );
     3256                    foreach ( $breakpoint_pseudo_declarations as $pseudo_selector => $pseudo_declarations ) {
     3257                        if ( empty( $pseudo_declarations ) ) {
     3258                            continue;
     3259                        }
     3260                        $pseudo_ruleset            = static::to_ruleset( ':root :where(' . $pseudo_selector . ')', $pseudo_declarations );
     3261                        $variation_responsive_css .= $breakpoint_media . '{' . $pseudo_ruleset . '}';
     3262                    }
     3263
     3264                    // Process custom CSS for this breakpoint.
     3265                    if ( isset( $breakpoint_node['css'] ) ) {
     3266                        $breakpoint_custom_css     = static::process_blocks_custom_css( $breakpoint_node['css'], $style_variation['selector'] );
     3267                        $variation_responsive_css .= $breakpoint_media . '{' . $breakpoint_custom_css . '}';
     3268                    }
     3269
     3270                    // Process blockGap responsive layout styles for this variation.
     3271                    if ( isset( $breakpoint_node['spacing']['blockGap'] ) ) {
     3272                        $variation_layout_metadata             = $style_variation;
     3273                        $variation_layout_metadata['selector'] = $style_variation['selector'] . $block_metadata['css'];
     3274                        $variation_responsive_css             .= $this->get_layout_styles(
     3275                            $variation_layout_metadata,
     3276                            array(
     3277                                'node'        => $breakpoint_node,
     3278                                'media_query' => $breakpoint_media,
     3279                            )
     3280                        );
     3281                    }
     3282
     3283                    // Process nested element styles for this breakpoint state.
     3284                    if ( isset( $breakpoint_node['elements'] ) && ! empty( $block_elements ) ) {
     3285                        foreach ( $breakpoint_node['elements'] as $element_name => $element_node ) {
     3286                            if ( ! isset( $block_elements[ $element_name ] ) ) {
     3287                                continue;
     3288                            }
     3289
     3290                            $clean_element_selector     = preg_replace( '/,\s+/', ',', $block_elements[ $element_name ] );
     3291                            $shortened_selector         = str_replace( $block_metadata['selector'], '', $clean_element_selector );
     3292                            $split_selectors            = explode( ',', $shortened_selector );
     3293                            $updated_selectors          = array_map(
     3294                                static function ( $split_selector ) use ( $clean_style_variation_selector ) {
     3295                                    return $clean_style_variation_selector . $split_selector;
     3296                                },
     3297                                $split_selectors
     3298                            );
     3299                            $variation_element_selector = implode( ',', $updated_selectors );
     3300
     3301                            $element_declarations = static::compute_style_properties( $element_node, $settings, null, $this->theme_json );
     3302                            if ( ! empty( $element_declarations ) ) {
     3303                                $element_ruleset           = static::to_ruleset( ':root :where(' . $variation_element_selector . ')', $element_declarations );
     3304                                $variation_responsive_css .= $breakpoint_media . '{' . $element_ruleset . '}';
     3305                            }
     3306
     3307                            if ( isset( $element_node['css'] ) ) {
     3308                                $element_custom_css        = static::process_blocks_custom_css( $element_node['css'], $variation_element_selector );
     3309                                $variation_responsive_css .= $breakpoint_media . '{' . $element_custom_css . '}';
     3310                            }
     3311
     3312                            if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
     3313                                foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
     3314                                    if ( ! isset( $element_node[ $pseudo_selector ] ) ) {
     3315                                        continue;
     3316                                    }
     3317
     3318                                    $pseudo_declarations = static::compute_style_properties( $element_node[ $pseudo_selector ], $settings, null, $this->theme_json );
     3319                                    if ( empty( $pseudo_declarations ) ) {
     3320                                        continue;
     3321                                    }
     3322
     3323                                    $pseudo_selector_ruleset   = static::to_ruleset( ':root :where(' . static::append_to_selector( $variation_element_selector, $pseudo_selector ) . ')', $pseudo_declarations );
     3324                                    $variation_responsive_css .= $breakpoint_media . '{' . $pseudo_selector_ruleset . '}';
     3325                                }
     3326                            }
     3327                        }
     3328                    }
     3329                }
     3330
     3331                if ( ! empty( $variation_responsive_css ) ) {
     3332                    $style_variation_responsive_css[ $style_variation['selector'] ] = $variation_responsive_css;
     3333                }
    30433334            }
    30443335        }
     
    30663357        $block_pseudo_selector      = null;
    30673358        if ( in_array( 'blocks', $block_metadata['path'], true ) && count( $block_metadata['path'] ) >= 4 ) {
    3068             $block_name        = $block_metadata['path'][2]; // 'core/button'
     3359            $block_name        = static::get_block_name_from_metadata_path( $block_metadata ); // 'core/button'
    30693360            $last_path_element = $block_metadata['path'][ count( $block_metadata['path'] ) - 1 ]; // ':hover'
    30703361
     
    31083399            // Process block pseudo-selector styles
    31093400            // For block pseudo-selectors, we need to get the block data first, then access the pseudo-selector
    3110             $block_name  = $block_metadata['path'][2]; // 'core/button'
     3401            $block_name  = static::get_block_name_from_metadata_path( $block_metadata ); // 'core/button'
    31113402            $block_data  = _wp_array_get( $this->theme_json, array( 'styles', 'blocks', $block_name ), array() );
    31123403            $pseudo_data = $block_data[ $block_pseudo_selector ] ?? array();
     
    32253516                $block_rules .= $style_variation_custom_css[ $style_variation_selector ];
    32263517            }
     3518            if ( isset( $style_variation_responsive_css[ $style_variation_selector ] ) ) {
     3519                $block_rules .= $style_variation_responsive_css[ $style_variation_selector ];
     3520            }
    32273521        }
    32283522
     
    32303524        if ( isset( $node['css'] ) && ! $is_root_selector ) {
    32313525            $block_rules .= $this->process_blocks_custom_css( $node['css'], $selector );
     3526        }
     3527
     3528        // 8. Wrap the entire block output in a media query if this is a responsive node.
     3529        // Responsive nodes are created by get_block_nodes() for each breakpoint and carry
     3530        // a 'media_query' key.
     3531        if ( $media_query && ! empty( $block_rules ) ) {
     3532            $block_rules = $media_query . '{' . $block_rules . '}';
    32323533        }
    32333534
     
    37274028            }
    37284029
     4030            $block_name = in_array( 'blocks', $metadata['path'], true )
     4031                ? static::get_block_name_from_metadata_path( $metadata )
     4032                : null;
     4033
    37294034            // The global styles custom CSS is not sanitized, but can only be edited by users with 'edit_css' capability.
    37304035            if ( isset( $input['css'] ) && current_user_can( 'edit_css' ) ) {
     
    37524057            }
    37534058
     4059            // Re-add and process responsive breakpoint styles.
     4060            foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     4061                if ( isset( $input[ $breakpoint ] ) ) {
     4062                    $output[ $breakpoint ] = static::remove_insecure_styles( $input[ $breakpoint ] );
     4063
     4064                    if ( isset( $input[ $breakpoint ]['elements'] ) ) {
     4065                        $output[ $breakpoint ]['elements'] = static::remove_insecure_element_styles( $input[ $breakpoint ]['elements'] );
     4066                    }
     4067
     4068                    if ( isset( $input[ $breakpoint ]['blocks'] ) ) {
     4069                        $output[ $breakpoint ]['blocks'] = static::remove_insecure_inner_block_styles( $input[ $breakpoint ]['blocks'] );
     4070                    }
     4071
     4072                    if ( $block_name && isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) {
     4073                        foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) {
     4074                            if ( isset( $input[ $breakpoint ][ $pseudo_selector ] ) ) {
     4075                                $output[ $breakpoint ][ $pseudo_selector ] = static::remove_insecure_styles( $input[ $breakpoint ][ $pseudo_selector ] );
     4076                            }
     4077                        }
     4078                    }
     4079
     4080                    // Responsive custom CSS is allowed for users with 'edit_css' capability.
     4081                    if ( isset( $input[ $breakpoint ]['css'] ) && current_user_can( 'edit_css' ) ) {
     4082                        $output[ $breakpoint ]['css'] = $input[ $breakpoint ]['css'];
     4083                    }
     4084                }
     4085            }
     4086
    37544087            if ( ! empty( $output ) ) {
    37554088                _wp_array_set( $sanitized, $metadata['path'], $output );
     
    37734106                    }
    37744107
     4108                    // Re-add and process responsive breakpoint styles for variations.
     4109                    foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     4110                        if ( isset( $variation_input[ $breakpoint ] ) ) {
     4111                            $variation_output[ $breakpoint ] = static::remove_insecure_styles( $variation_input[ $breakpoint ] );
     4112
     4113                            if ( isset( $variation_input[ $breakpoint ]['elements'] ) ) {
     4114                                $variation_output[ $breakpoint ]['elements'] = static::remove_insecure_element_styles( $variation_input[ $breakpoint ]['elements'] );
     4115                            }
     4116
     4117                            if ( isset( $variation_input[ $breakpoint ]['blocks'] ) ) {
     4118                                $variation_output[ $breakpoint ]['blocks'] = static::remove_insecure_inner_block_styles( $variation_input[ $breakpoint ]['blocks'] );
     4119                            }
     4120
     4121                            if ( $block_name && isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ) ) {
     4122                                foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] as $pseudo_selector ) {
     4123                                    if ( isset( $variation_input[ $breakpoint ][ $pseudo_selector ] ) ) {
     4124                                        $variation_output[ $breakpoint ][ $pseudo_selector ] = static::remove_insecure_styles( $variation_input[ $breakpoint ][ $pseudo_selector ] );
     4125                                    }
     4126                                }
     4127                            }
     4128
     4129                            // Responsive custom CSS is allowed for users with 'edit_css' capability.
     4130                            if ( isset( $variation_input[ $breakpoint ]['css'] ) && current_user_can( 'edit_css' ) ) {
     4131                                $variation_output[ $breakpoint ]['css'] = $variation_input[ $breakpoint ]['css'];
     4132                            }
     4133                        }
     4134                    }
     4135
    37754136                    if ( ! empty( $variation_output ) ) {
    37764137                        _wp_array_set( $sanitized, $variation['path'], $variation_output );
     
    38334194                }
    38344195
     4196                // Re-add and process responsive breakpoint styles for elements.
     4197                foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     4198                    if ( isset( $element_input[ $breakpoint ] ) ) {
     4199                        $element_output[ $breakpoint ] = static::remove_insecure_styles( $element_input[ $breakpoint ] );
     4200
     4201                        if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
     4202                            foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
     4203                                if ( isset( $element_input[ $breakpoint ][ $pseudo_selector ] ) ) {
     4204                                    $element_output[ $breakpoint ][ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $breakpoint ][ $pseudo_selector ] );
     4205                                }
     4206                            }
     4207                        }
     4208                    }
     4209                }
     4210
    38354211                $sanitized[ $element_name ] = $element_output;
    38364212            }
     
    38544230            if ( isset( $block_input['elements'] ) ) {
    38554231                $block_output['elements'] = static::remove_insecure_element_styles( $block_input['elements'] );
     4232            }
     4233
     4234            // Re-add and process responsive breakpoint styles for inner blocks.
     4235            foreach ( array_keys( static::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
     4236                if ( isset( $block_input[ $breakpoint ] ) ) {
     4237                    $block_output[ $breakpoint ] = static::remove_insecure_styles( $block_input[ $breakpoint ] );
     4238
     4239                    if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_type ] ) ) {
     4240                        foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $block_type ] as $pseudo_selector ) {
     4241                            if ( isset( $block_input[ $breakpoint ][ $pseudo_selector ] ) ) {
     4242                                $block_output[ $breakpoint ][ $pseudo_selector ] = static::remove_insecure_styles( $block_input[ $breakpoint ][ $pseudo_selector ] );
     4243                            }
     4244                        }
     4245                    }
     4246                }
    38564247            }
    38574248
     
    48445235        return $valid_variations;
    48455236    }
     5237
     5238    /**
     5239     * Extracts the block name from the block metadata path.
     5240     *
     5241     * @since 7.1
     5242     *
     5243     * @param array $block_metadata Block metadata.
     5244     * @return string|null The block name or null if not found.
     5245     */
     5246    private static function get_block_name_from_metadata_path( $block_metadata ) {
     5247        return $block_metadata['path'][2] ?? null;
     5248    }
    48465249}
  • trunk/tests/phpunit/tests/theme/wpThemeJson.php

    r62415 r62444  
    938938
    939939    /**
     940     * @ticket 65164
     941     */
     942    public function test_get_styles_for_block_responsive_feature_selector_not_duplicated_on_base_selector() {
     943        register_block_type(
     944            'test/responsive-feature',
     945            array(
     946                'api_version' => 3,
     947                'selectors'   => array(
     948                    'root'  => '.wp-block-test-responsive-feature',
     949                    'color' => '.wp-block-test-responsive-feature .color-target',
     950                ),
     951            )
     952        );
     953
     954        $theme_json = new WP_Theme_JSON(
     955            array(
     956                'version' => WP_Theme_JSON::LATEST_SCHEMA,
     957                'styles'  => array(
     958                    'blocks' => array(
     959                        'test/responsive-feature' => array(
     960                            'mobile' => array(
     961                                'color' => array(
     962                                    'text' => 'red',
     963                                ),
     964                            ),
     965                        ),
     966                    ),
     967                ),
     968            )
     969        );
     970
     971        $base_metadata = array(
     972            'name'      => 'test/responsive-feature',
     973            'path'      => array( 'styles', 'blocks', 'test/responsive-feature' ),
     974            'selector'  => '.wp-block-test-responsive-feature',
     975            'selectors' => array(
     976                'color' => '.wp-block-test-responsive-feature .color-target',
     977            ),
     978        );
     979
     980        $mobile_metadata = array(
     981            'name'        => 'test/responsive-feature',
     982            'path'        => array( 'styles', 'blocks', 'test/responsive-feature', 'mobile' ),
     983            'selector'    => '.wp-block-test-responsive-feature',
     984            'selectors'   => array(
     985                'color' => '.wp-block-test-responsive-feature .color-target',
     986            ),
     987            'media_query' => '@media (width <= 480px)',
     988        );
     989
     990        $actual_styles  = $theme_json->get_styles_for_block( $base_metadata );
     991        $actual_styles .= $theme_json->get_styles_for_block( $mobile_metadata );
     992
     993        unregister_block_type( 'test/responsive-feature' );
     994
     995        $this->assertStringContainsString(
     996            '@media (width <= 480px){:root :where(.wp-block-test-responsive-feature .color-target){color: red;}}',
     997            $actual_styles
     998        );
     999        $this->assertStringNotContainsString(
     1000            '@media (width <= 480px){:root :where(.wp-block-test-responsive-feature){color: red;}}',
     1001            $actual_styles
     1002        );
     1003    }
     1004
     1005    /**
     1006     * @ticket 65164
     1007     */
     1008    public function test_get_styles_for_block_outputs_responsive_block_gap_after_default_gap() {
     1009        $theme_json = new WP_Theme_JSON(
     1010            array(
     1011                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     1012                'settings' => array(
     1013                    'spacing' => array(
     1014                        'blockGap' => true,
     1015                    ),
     1016                ),
     1017                'styles'   => array(
     1018                    'blocks' => array(
     1019                        'core/group' => array(
     1020                            'spacing' => array(
     1021                                'blockGap' => '5rem',
     1022                            ),
     1023                            'mobile'  => array(
     1024                                'spacing' => array(
     1025                                    'blockGap' => '2rem',
     1026                                ),
     1027                            ),
     1028                        ),
     1029                    ),
     1030                ),
     1031            )
     1032        );
     1033
     1034        $base_metadata = array(
     1035            'name'     => 'core/group',
     1036            'path'     => array( 'styles', 'blocks', 'core/group' ),
     1037            'selector' => '.wp-block-group',
     1038            'css'      => '.wp-block-group',
     1039        );
     1040
     1041        $mobile_metadata = array(
     1042            'name'        => 'core/group',
     1043            'path'        => array( 'styles', 'blocks', 'core/group', 'mobile' ),
     1044            'selector'    => '.wp-block-group',
     1045            'css'         => '.wp-block-group',
     1046            'media_query' => '@media (width <= 480px)',
     1047        );
     1048
     1049        $actual_styles  = $theme_json->get_styles_for_block( $base_metadata );
     1050        $actual_styles .= $theme_json->get_styles_for_block( $mobile_metadata );
     1051
     1052        $default_gap = ':root :where(.wp-block-group-is-layout-flex){gap: 5rem;}';
     1053        $mobile_gap  = ':root :where(.wp-block-group-is-layout-flex){gap: 2rem;}';
     1054
     1055        $this->assertStringContainsString( $default_gap, $actual_styles );
     1056        $this->assertStringContainsString( '@media (width <= 480px)', $actual_styles );
     1057        $this->assertStringContainsString( $mobile_gap, $actual_styles );
     1058        $this->assertLessThan( strpos( $actual_styles, $mobile_gap ), strpos( $actual_styles, $default_gap ) );
     1059    }
     1060
     1061    /**
     1062     * @ticket 65164
     1063     */
     1064    public function test_get_styles_for_block_responsive_element_pseudo_styles_preserve_order_and_do_not_duplicate_pseudo() {
     1065        $theme_json = new WP_Theme_JSON(
     1066            array(
     1067                'version' => WP_Theme_JSON::LATEST_SCHEMA,
     1068                'styles'  => array(
     1069                    'blocks' => array(
     1070                        'core/group' => array(
     1071                            'elements' => array(
     1072                                'link' => array(
     1073                                    'color'  => array(
     1074                                        'text' => 'blue',
     1075                                    ),
     1076                                    ':hover' => array(
     1077                                        'color' => array(
     1078                                            'text' => 'navy',
     1079                                        ),
     1080                                    ),
     1081                                ),
     1082                            ),
     1083                            'mobile'   => array(
     1084                                'elements' => array(
     1085                                    'link' => array(
     1086                                        'color'  => array(
     1087                                            'text' => 'red',
     1088                                        ),
     1089                                        ':hover' => array(
     1090                                            'color' => array(
     1091                                                'text' => 'darkred',
     1092                                            ),
     1093                                        ),
     1094                                    ),
     1095                                ),
     1096                            ),
     1097                        ),
     1098                    ),
     1099                ),
     1100            )
     1101        );
     1102
     1103        $link_selector = '.wp-block-group a:where(:not(.wp-element-button))';
     1104
     1105        // Nodes are assembled in cascade order: default, responsive, pseudo, responsive pseudo.
     1106        $link_node = array(
     1107            'path'     => array( 'styles', 'blocks', 'core/group', 'elements', 'link' ),
     1108            'selector' => $link_selector,
     1109        );
     1110
     1111        $mobile_link_node = array(
     1112            'path'        => array( 'styles', 'blocks', 'core/group', 'mobile', 'elements', 'link' ),
     1113            'selector'    => $link_selector,
     1114            'media_query' => '@media (width <= 480px)',
     1115        );
     1116
     1117        $hover_node = array(
     1118            'path'     => array( 'styles', 'blocks', 'core/group', 'elements', 'link' ),
     1119            'selector' => $link_selector . ':hover',
     1120        );
     1121
     1122        $mobile_hover_node = array(
     1123            'path'        => array( 'styles', 'blocks', 'core/group', 'mobile', 'elements', 'link' ),
     1124            'selector'    => $link_selector . ':hover',
     1125            'media_query' => '@media (width <= 480px)',
     1126        );
     1127
     1128        $actual_styles  = $theme_json->get_styles_for_block( $link_node );
     1129        $actual_styles .= $theme_json->get_styles_for_block( $mobile_link_node );
     1130        $actual_styles .= $theme_json->get_styles_for_block( $hover_node );
     1131        $actual_styles .= $theme_json->get_styles_for_block( $mobile_hover_node );
     1132
     1133        $default_link = ':root :where(.wp-block-group a:where(:not(.wp-element-button))){color: blue;}';
     1134        $mobile_link  = '@media (width <= 480px){:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: red;}}';
     1135        $default_hov  = ':root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){color: navy;}';
     1136        $mobile_hov   = '@media (width <= 480px){:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){color: darkred;}}';
     1137
     1138        $this->assertStringContainsString( $default_link, $actual_styles );
     1139        $this->assertStringContainsString( $mobile_link, $actual_styles );
     1140        $this->assertStringContainsString( $default_hov, $actual_styles );
     1141        $this->assertStringContainsString( $mobile_hov, $actual_styles );
     1142
     1143        $this->assertLessThan( strpos( $actual_styles, $mobile_link ), strpos( $actual_styles, $default_link ) );
     1144        $this->assertLessThan( strpos( $actual_styles, $default_hov ), strpos( $actual_styles, $mobile_link ) );
     1145        $this->assertLessThan( strpos( $actual_styles, $mobile_hov ), strpos( $actual_styles, $default_hov ) );
     1146        $this->assertStringNotContainsString( ':hover:hover', $actual_styles );
     1147    }
     1148
     1149    /**
     1150     * @ticket 65164
     1151     */
     1152    public function test_get_styles_for_block_with_style_variations_and_responsive_block_gap() {
     1153        register_block_style(
     1154            'core/group',
     1155            array(
     1156                'name'  => 'withGap',
     1157                'label' => 'With Gap',
     1158            )
     1159        );
     1160
     1161        $theme_json = new WP_Theme_JSON(
     1162            array(
     1163                'version'  => WP_Theme_JSON::LATEST_SCHEMA,
     1164                'settings' => array(
     1165                    'spacing' => array(
     1166                        'blockGap' => true,
     1167                    ),
     1168                ),
     1169                'styles'   => array(
     1170                    'blocks' => array(
     1171                        'core/group' => array(
     1172                            'variations' => array(
     1173                                'withGap' => array(
     1174                                    'spacing' => array(
     1175                                        'blockGap' => '5rem',
     1176                                    ),
     1177                                    'mobile'  => array(
     1178                                        'spacing' => array(
     1179                                            'blockGap' => '2rem',
     1180                                        ),
     1181                                    ),
     1182                                ),
     1183                            ),
     1184                        ),
     1185                    ),
     1186                ),
     1187            )
     1188        );
     1189
     1190        $metadata = array(
     1191            'name'       => 'core/group',
     1192            'path'       => array( 'styles', 'blocks', 'core/group' ),
     1193            'selector'   => '.wp-block-group',
     1194            'css'        => '.wp-block-group',
     1195            'variations' => array(
     1196                array(
     1197                    'path'     => array( 'styles', 'blocks', 'core/group', 'variations', 'withGap' ),
     1198                    'selector' => '.is-style-withGap.wp-block-group',
     1199                ),
     1200            ),
     1201        );
     1202
     1203        $actual_styles = $theme_json->get_styles_for_block( $metadata );
     1204
     1205        unregister_block_style( 'core/group', 'withGap' );
     1206
     1207        $default_gap = ':root :where(.is-style-withGap.wp-block-group.wp-block-group-is-layout-flex){gap: 5rem;}';
     1208        $mobile_gap  = ':root :where(.is-style-withGap.wp-block-group.wp-block-group-is-layout-flex){gap: 2rem;}';
     1209
     1210        $this->assertStringContainsString( $default_gap, $actual_styles );
     1211        $this->assertStringContainsString( '@media (width <= 480px)', $actual_styles );
     1212        $this->assertStringContainsString( $mobile_gap, $actual_styles );
     1213        $this->assertLessThan( strpos( $actual_styles, $mobile_gap ), strpos( $actual_styles, $default_gap ) );
     1214    }
     1215
     1216    /**
     1217     * @ticket 65164
     1218     */
     1219    public function test_get_styles_for_block_outputs_tablet_responsive_styles_only() {
     1220        register_block_type(
     1221            'test/tablet-only',
     1222            array(
     1223                'api_version' => 3,
     1224            )
     1225        );
     1226
     1227        $theme_json = new WP_Theme_JSON(
     1228            array(
     1229                'version' => WP_Theme_JSON::LATEST_SCHEMA,
     1230                'styles'  => array(
     1231                    'blocks' => array(
     1232                        'test/tablet-only' => array(
     1233                            'tablet' => array(
     1234                                'color' => array(
     1235                                    'text' => 'purple',
     1236                                ),
     1237                            ),
     1238                        ),
     1239                    ),
     1240                ),
     1241            )
     1242        );
     1243
     1244        $tablet_metadata = array(
     1245            'name'        => 'test/tablet-only',
     1246            'path'        => array( 'styles', 'blocks', 'test/tablet-only', 'tablet' ),
     1247            'selector'    => '.wp-block-test-tablet-only',
     1248            'media_query' => '@media (480px < width <= 782px)',
     1249        );
     1250
     1251        $actual_styles = $theme_json->get_styles_for_block( $tablet_metadata );
     1252
     1253        unregister_block_type( 'test/tablet-only' );
     1254
     1255        $this->assertStringContainsString(
     1256            '@media (480px < width <= 782px){:root :where(.wp-block-test-tablet-only){color: purple;}}',
     1257            $actual_styles
     1258        );
     1259        $this->assertStringNotContainsString( '@media (width <= 480px)', $actual_styles );
     1260    }
     1261
     1262    /**
    9401263     * Tests that if an element has nothing but pseudo selector styles, they are still output by get_stylesheet.
    9411264     *
     
    28573180            ),
    28583181        );
     3182        $this->assertEqualSetsWithIndex( $expected, $actual );
     3183    }
     3184
     3185    /**
     3186     * @covers WP_Theme_JSON::remove_insecure_properties
     3187     *
     3188     * @ticket 65164
     3189     */
     3190    public function test_remove_insecure_properties_preserves_responsive_block_element_styles() {
     3191        $actual = WP_Theme_JSON::remove_insecure_properties(
     3192            array(
     3193                'version' => WP_Theme_JSON::LATEST_SCHEMA,
     3194                'styles'  => array(
     3195                    'blocks' => array(
     3196                        'core/group' => array(
     3197                            'elements' => array(
     3198                                'link' => array(
     3199                                    'color'  => array(
     3200                                        'text' => 'var:preset|color|dark-gray',
     3201                                    ),
     3202                                    'mobile' => array(
     3203                                        'color' => array(
     3204                                            'text' => 'var:preset|color|dark-pink',
     3205                                        ),
     3206                                    ),
     3207                                    'tablet' => array(
     3208                                        'color' => array(
     3209                                            'text' => 'var:preset|color|dark-red',
     3210                                        ),
     3211                                    ),
     3212                                ),
     3213                            ),
     3214                        ),
     3215                    ),
     3216                ),
     3217            )
     3218        );
     3219
     3220        $expected = array(
     3221            'version' => WP_Theme_JSON::LATEST_SCHEMA,
     3222            'styles'  => array(
     3223                'blocks' => array(
     3224                    'core/group' => array(
     3225                        'elements' => array(
     3226                            'link' => array(
     3227                                'color'  => array(
     3228                                    'text' => 'var(--wp--preset--color--dark-gray)',
     3229                                ),
     3230                                'mobile' => array(
     3231                                    'color' => array(
     3232                                        'text' => 'var(--wp--preset--color--dark-pink)',
     3233                                    ),
     3234                                ),
     3235                                'tablet' => array(
     3236                                    'color' => array(
     3237                                        'text' => 'var(--wp--preset--color--dark-red)',
     3238                                    ),
     3239                                ),
     3240                            ),
     3241                        ),
     3242                    ),
     3243                ),
     3244            ),
     3245        );
     3246
     3247        $this->assertEqualSetsWithIndex( $expected, $actual );
     3248    }
     3249
     3250    /**
     3251     * @covers WP_Theme_JSON::remove_insecure_properties
     3252     *
     3253     * @ticket 65164
     3254     */
     3255    public function test_remove_insecure_properties_preserves_responsive_elements_within_block_state() {
     3256        $actual = WP_Theme_JSON::remove_insecure_properties(
     3257            array(
     3258                'version' => WP_Theme_JSON::LATEST_SCHEMA,
     3259                'styles'  => array(
     3260                    'blocks' => array(
     3261                        'core/group' => array(
     3262                            'mobile' => array(
     3263                                'elements' => array(
     3264                                    'link' => array(
     3265                                        'color' => array(
     3266                                            'text' => 'var:preset|color|dark-pink',
     3267                                        ),
     3268                                    ),
     3269                                ),
     3270                            ),
     3271                        ),
     3272                    ),
     3273                ),
     3274            )
     3275        );
     3276
     3277        $expected = array(
     3278            'version' => WP_Theme_JSON::LATEST_SCHEMA,
     3279            'styles'  => array(
     3280                'blocks' => array(
     3281                    'core/group' => array(
     3282                        'mobile' => array(
     3283                            'elements' => array(
     3284                                'link' => array(
     3285                                    'color' => array(
     3286                                        'text' => 'var(--wp--preset--color--dark-pink)',
     3287                                    ),
     3288                                ),
     3289                            ),
     3290                        ),
     3291                    ),
     3292                ),
     3293            ),
     3294        );
     3295
    28593296        $this->assertEqualSetsWithIndex( $expected, $actual );
    28603297    }
Note: See TracChangeset for help on using the changeset viewer.