Make WordPress Core

Changeset 58896


Ignore:
Timestamp:
08/14/2024 06:55:26 PM (2 months ago)
Author:
hellofromTonya
Message:

Editor: Fix block custom CSS pseudo element selectors in global styles.

Fixes a regression introduced in [58241] where selectors with pseudo elements are wrapped within :where() causing malformed CSS and the CSS rule(s) not being applied.

When processing custom CSS for blocks, this changeset:

  • Strips the pseudo-elements from the original nested selector, performs the required wrapping in :root :where, then re-appends the pseudo-element selector with its leading combinators if present.
  • Removes empty CSS rules.

It includes the PHP changes.

Reference:

Follow-up to [58241], [56812], [55216].

Props aaronrobertshaw, wongjn, harlet7, dballari, ramonopoly, andrewserong, aristath, hellofromTonya.
Fixes #61769.

Location:
trunk
Files:
2 edited

Legend:

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

    r58890 r58896  
    14491449        $processed_css = '';
    14501450
     1451        if ( empty( $css ) ) {
     1452            return $processed_css;
     1453        }
     1454
    14511455        // Split CSS nested rules.
    14521456        $parts = explode( '&', $css );
    14531457        foreach ( $parts as $part ) {
     1458            if ( empty( $part ) ) {
     1459                continue;
     1460            }
    14541461            $is_root_css = ( ! str_contains( $part, '{' ) );
    14551462            if ( $is_root_css ) {
     
    14641471                $nested_selector = $part[0];
    14651472                $css_value       = $part[1];
    1466                 $part_selector   = str_starts_with( $nested_selector, ' ' )
     1473
     1474                /*
     1475                 * Handle pseudo elements such as ::before, ::after etc. Regex will also
     1476                 * capture any leading combinator such as >, +, or ~, as well as spaces.
     1477                 * This allows pseudo elements as descendants e.g. `.parent ::before`.
     1478                 */
     1479                $matches            = array();
     1480                $has_pseudo_element = preg_match( '/([>+~\s]*::[a-zA-Z-]+)/', $nested_selector, $matches );
     1481                $pseudo_part        = $has_pseudo_element ? $matches[1] : '';
     1482                $nested_selector    = $has_pseudo_element ? str_replace( $pseudo_part, '', $nested_selector ) : $nested_selector;
     1483
     1484                // Finalize selector and re-append pseudo element if required.
     1485                $part_selector  = str_starts_with( $nested_selector, ' ' )
    14671486                    ? static::scope_selector( $selector, $nested_selector )
    14681487                    : static::append_to_selector( $selector, $nested_selector );
    1469                 $final_selector  = ":root :where($part_selector)";
    1470                 $processed_css  .= $final_selector . '{' . trim( $css_value ) . '}';}
     1488                $final_selector = ":root :where($part_selector)$pseudo_part";
     1489
     1490                $processed_css .= $final_selector . '{' . trim( $css_value ) . '}';
     1491            }
    14711492        }
    14721493        return $processed_css;
  • trunk/tests/phpunit/tests/theme/wpThemeJson.php

    r58890 r58896  
    52445244    /**
    52455245     * @ticket 61165
     5246     * @ticket 61769
    52465247     *
    52475248     * @dataProvider data_process_blocks_custom_css
     
    52715272        return array(
    52725273            // Simple CSS without any nested selectors.
     5274            'empty css'                    => array(
     5275                'input'    => array(
     5276                    'selector' => '.foo',
     5277                    'css'      => '',
     5278                ),
     5279                'expected' => '',
     5280            ),
    52735281            'no nested selectors'          => array(
    52745282                'input'    => array(
     
    52865294                'expected' => ':root :where(.foo){color: red; margin: auto;}:root :where(.foo.one){color: blue;}:root :where(.foo .two){color: green;}',
    52875295            ),
     5296            'no root styles'               => array(
     5297                'input'    => array(
     5298                    'selector' => '.foo',
     5299                    'css'      => '&::before{color: red;}',
     5300                ),
     5301                'expected' => ':root :where(.foo)::before{color: red;}',
     5302            ),
    52885303            // CSS with pseudo elements.
    52895304            'with pseudo elements'         => array(
     
    52925307                    'css'      => 'color: red; margin: auto; &::before{color: blue;} & ::before{color: green;}  &.one::before{color: yellow;} & .two::before{color: purple;}',
    52935308                ),
    5294                 'expected' => ':root :where(.foo){color: red; margin: auto;}:root :where(.foo::before){color: blue;}:root :where(.foo ::before){color: green;}:root :where(.foo.one::before){color: yellow;}:root :where(.foo .two::before){color: purple;}',
     5309                'expected' => ':root :where(.foo){color: red; margin: auto;}:root :where(.foo)::before{color: blue;}:root :where(.foo) ::before{color: green;}:root :where(.foo.one)::before{color: yellow;}:root :where(.foo .two)::before{color: purple;}',
    52955310            ),
    52965311            // CSS with multiple root selectors.
     
    53005315                    'css'      => 'color: red; margin: auto; &.one{color: blue;} & .two{color: green;} &::before{color: yellow;} & ::before{color: purple;}  &.three::before{color: orange;} & .four::before{color: skyblue;}',
    53015316                ),
    5302                 'expected' => ':root :where(.foo, .bar){color: red; margin: auto;}:root :where(.foo.one, .bar.one){color: blue;}:root :where(.foo .two, .bar .two){color: green;}:root :where(.foo::before, .bar::before){color: yellow;}:root :where(.foo ::before, .bar ::before){color: purple;}:root :where(.foo.three::before, .bar.three::before){color: orange;}:root :where(.foo .four::before, .bar .four::before){color: skyblue;}',
     5317                'expected' => ':root :where(.foo, .bar){color: red; margin: auto;}:root :where(.foo.one, .bar.one){color: blue;}:root :where(.foo .two, .bar .two){color: green;}:root :where(.foo, .bar)::before{color: yellow;}:root :where(.foo, .bar) ::before{color: purple;}:root :where(.foo.three, .bar.three)::before{color: orange;}:root :where(.foo .four, .bar .four)::before{color: skyblue;}',
    53035318            ),
    53045319        );
Note: See TracChangeset for help on using the changeset viewer.