Make WordPress Core

Changeset 61554

Timestamp:
01/29/2026 06:14:05 PM (5 weeks ago)
Author:
westonruter
Message:

Script Loader: Preserve original CSS cascade for classic themes when hoisting late-printed styles.

This refactors wp_hoist_late_printed_styles() to strictly preserve the classic theme CSS cascade when moving styles from wp_footer to the HEAD. Previously, hoisted styles were appended in an order closer to the CSS cascade for block themes, potentially causing specificity issues for CSS selectors written for the previous cascade. This is intended to eliminate the need for sites which upgraded to 6.9 to apply a hotfix involving add_filter( 'should_load_separate_core_block_assets', '__return_false' ) to disable the optimization.

Key changes:

  • Identifies styles enqueued during enqueue_block_assets.
  • Separates core block styles from third-party block styles.
  • Inserts core block styles immediately after wp-block-library.
  • Inserts third-party block styles after classic-theme-styles.
  • Inserts global-styles after all enqueue_block_assets styles.

This ensures the following order is maintained:

  1. Core block library.
  2. Core block styles.
  3. Classic theme styles.
  4. Third-party block styles.
  5. Global styles.

Developed in https://github.com/WordPress/wordpress-develop/pull/10601

Follow-up to [61174], [61122], [61076], [61008].

Props westonruter, wildworks, jorbin, peterwilsoncc, sabernhardt, audrasjb, pmbs, threadi, madhavishah01, raftaar1191, noruzzaman, ozgursar.
See #64099, #64150, #43258.
Fixes #64354.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/script-loader.php

    r61539 r61554  
    37023702    }
    37033703
     3704
     3705
     3706
     3707
     3708
     3709
     3710
     3711
     3712
     3713
     3714
     3715
     3716
     3717
     3718
     3719
     3720
    37043721    /*
    3705      * Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles
     3722     * Add a placeholder comment into the inline styles for wp-block-library, after which the late block styles
    37063723     * can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement
    37073724     * output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at
     
    37223739     * before `print_footer_scripts()` is called.
    37233740     */
    3724     $printed_block_styles = '';
    3725     $printed_late_styles  = '';
    3726     $capture_late_styles  = static function () use ( &$printed_block_styles, &$printed_late_styles ) {
     3741    $printed_core_block_styles  = '';
     3742    $printed_other_block_styles = '';
     3743    $printed_global_styles      = '';
     3744    $printed_late_styles        = '';
     3745
     3746    $capture_late_styles = static function () use ( &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) {
    37273747        // Gather the styles related to on-demand block enqueues.
    3728         $all_block_style_handles = array();
     3748        $all_core_block_style_handles  = array();
     3749        $all_other_block_style_handles = array();
    37293750        foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) {
    3730             foreach ( $block_type->style_handles as $style_handle ) {
    3731                 $all_block_style_handles[] = $style_handle;
     3751            if ( str_starts_with( $block_type->name, 'core/' ) ) {
     3752                foreach ( $block_type->style_handles as $style_handle ) {
     3753                    $all_core_block_style_handles[] = $style_handle;
     3754                }
     3755            } else {
     3756                foreach ( $block_type->style_handles as $style_handle ) {
     3757                    $all_other_block_style_handles[] = $style_handle;
     3758                }
    37323759            }
    37333760        }
    3734         $all_block_style_handles = array_merge(
    3735             $all_block_style_handles,
    3736             array(
    3737                 'global-styles',
    3738                 'block-style-variation-styles',
    3739                 'core-block-supports',
    3740                 'core-block-supports-duotone',
    3741             )
    3742         );
    37433761
    37443762        /*
    3745          * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet
     3763         * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet
    37463764         * to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`.
    37473765         */
    3748         $enqueued_block_styles = array_values( array_intersect( $all_block_style_handles, wp_styles()->queue ) );
    3749         if ( count( $enqueued_block_styles ) > 0 ) {
     3766        $enqueued__block_style_handles, wp_styles()->queue ) );
     3767        if ( count( $enqueued_block_styles ) > 0 ) {
    37503768            ob_start();
    3751             wp_styles()->do_items( $enqueued_block_styles );
    3752             $printed_block_styles = ob_get_clean();
     3769            wp_styles()->do_items( $enqueued_core_block_styles );
     3770            $printed_core_block_styles = ob_get_clean();
     3771        }
     3772
     3773        // Non-core block styles get printed after the classic-theme-styles stylesheet.
     3774        $enqueued_other_block_styles = array_values( array_intersect( $all_other_block_style_handles, wp_styles()->queue ) );
     3775        if ( count( $enqueued_other_block_styles ) > 0 ) {
     3776            ob_start();
     3777            wp_styles()->do_items( $enqueued_other_block_styles );
     3778            $printed_other_block_styles = ob_get_clean();
     3779        }
     3780
     3781        // Capture the global-styles so that it can be printed separately after classic-theme-styles and other styles enqueued at enqueue_block_assets.
     3782        if ( wp_style_is( 'global-styles' ) ) {
     3783            ob_start();
     3784            wp_styles()->do_items( array( 'global-styles' ) );
     3785            $printed_global_styles = ob_get_clean();
    37533786        }
    37543787
     
    37913824    add_filter(
    37923825        'wp_template_enhancement_output_buffer',
    3793         static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_late_styles ) {
     3826        static function ( $buffer ) use ( $placeholder, &$_styles, &$printed_late_styles ) {
    37943827
    37953828            // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans.
     
    38363869            };
    38373870
    3838             /*
    3839              * Insert block styles right after wp-block-library (if it is present), and then insert any remaining styles
    3840              * at </head> (or else print everything there). The placeholder CSS comment will always be added to the
    3841              * wp-block-library inline style since it gets printed at `wp_head` before the blocks are rendered.
    3842              * This means that there may not actually be any block styles to hoist from the footer to insert after this
    3843              * inline style. The placeholder CSS comment needs to be added so that the inline style gets printed, but
    3844              * if the resulting inline style is empty after the placeholder is removed, then the inline style is
    3845              * removed.
    3846              */
     3871            // Locate the insertion points in the HEAD.
    38473872            while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
    38483873                if (
     
    38503875                    'wp-block-library-inline-css' === $processor->get_attribute( 'id' )
    38513876                ) {
    3852                     $css_text = $processor->get_modifiable_text();
    3853 
    3854                     /*
    3855                      * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
    3856                      * be printed. Now that we've located the inline style, the placeholder comment can be removed. If
    3857                      * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
    3858                      * comment, then remove the STYLE entirely.)
    3859                      */
    3860                     $css_text = str_replace( $placeholder, '', $css_text );
    3861                     if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) {
    3862                         $processor->remove();
    3863                     } else {
    3864                         $processor->set_modifiable_text( $css_text );
     3877                    $);
     3878                } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
     3879                   
     3880                   
     3881               
     3882                   
     3883                   
     3884                   
     3885                   
     3886                   
     3887                       
     3888                    } else {
     3889                        $;
    38653890                    }
    38663891
    3867                     // Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade.
    3868                     if ( '' !== $printed_block_styles ) {
    3869                         $processor->insert_after( $printed_block_styles );
    3870 
    3871                         // Prevent printing them again at </head>.
    3872                         $printed_block_styles = '';
     3892                    if ( 'classic-theme-styles' === $handle ) {
     3893                        $processor->set_bookmark( 'classic_theme_styles' );
    38733894                    }
    38743895
    3875                     // If there aren't any late styles, there's no need to continue to finding </head>.
    3876                     if ( '' === $printed_late_styles ) {
    3877                         break;
     3896                    if ( $handle && in_array( $handle, $style_handles_at_enqueue_block_assets, true ) ) {
     3897                        if ( ! $processor->has_bookmark( 'first_style_at_enqueue_block_assets' ) ) {
     3898                            $processor->set_bookmark( 'first_style_at_enqueue_block_assets' );
     3899                        }
     3900                        $processor->set_bookmark( 'last_style_at_enqueue_block_assets' );
    38783901                    }
    3879                 } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
    3880                     $processor->insert_before( $printed_block_styles . $printed_late_styles );
    3881                     break;
    38823902                }
    38833903            }
    38843904
     3905
     3906
     3907
     3908
     3909
     3910
     3911
     3912
     3913
     3914
     3915
     3916
     3917
     3918
     3919
     3920
     3921
     3922
     3923
     3924
     3925
     3926
     3927
     3928
     3929
     3930
     3931
     3932
     3933
     3934
     3935
     3936
     3937
     3938
     3939
     3940
     3941
     3942
     3943
     3944
     3945
     3946
     3947
     3948
     3949
     3950
     3951
     3952
     3953
     3954
     3955
     3956
     3957
     3958
     3959
     3960
     3961
     3962
     3963
     3964
     3965
     3966
     3967
     3968
     3969
     3970
     3971
     3972
     3973
     3974
     3975
     3976
     3977
     3978
     3979
     3980
    38853981            return $processor->get_updated_html();
    38863982        }
  • trunk/tests/phpunit/tests/template.php

    r61479 r61554  
    146146        unregister_taxonomy( 'taxo' );
    147147        $this->set_permalink_structure( '' );
     148
     149
     150
     151
     152
    148153
    149154        parent::tear_down();
     
    14761481     */
    14771482    public function data_wp_hoist_late_printed_styles(): array {
    1478         $common_expected_head_styles = array(
     1483        $_styles = array(
    14791484            'wp-img-auto-sizes-contain-inline-css',
    14801485            'early-css',
    14811486            'early-inline-css',
    14821487            'wp-emoji-styles-inline-css',
    1483             'wp-block-library-css',
    1484             'wp-block-separator-css',
    1485             'global-styles-inline-css',
    1486             'core-block-supports-inline-css',
    1487             'classic-theme-styles-css',
     1488        );
     1489
     1490        $common_late_in_head = array(
     1491            // Styles enqueued at wp_enqueue_scripts (priority 10).
    14881492            'normal-css',
    14891493            'normal-inline-css',
     1494
     1495
    14901496            'wp-custom-css',
     1497
     1498
     1499
    14911500            'late-css',
    14921501            'late-inline-css',
     1502
     1503
     1504
     1505
     1506
     1507
     1508
     1509
     1510
     1511
     1512
     1513
     1514
     1515
     1516
     1517
     1518
     1519
     1520
     1521
     1522
     1523
     1524
     1525
    14931526        );
    14941527
     
    15061539                'inline_size_limit' => PHP_INT_MAX,
    15071540                'expected_styles'   => array(
    1508                     'HEAD' => array(
    1509                         'wp-img-auto-sizes-contain-inline-css',
    1510                         'early-css',
    1511                         'early-inline-css',
    1512                         'wp-emoji-styles-inline-css',
    1513                         'wp-block-library-inline-css',
    1514                         'wp-block-separator-inline-css',
    1515                         'global-styles-inline-css',
    1516                         'core-block-supports-inline-css',
    1517                         'classic-theme-styles-inline-css',
    1518                         'normal-css',
    1519                         'normal-inline-css',
    1520                         'wp-custom-css',
    1521                         'late-css',
    1522                         'late-inline-css',
     1541                    'HEAD' => array_merge(
     1542                        $early_common_styles,
     1543                        array(
     1544                            'wp-block-library-inline-css',
     1545                            'wp-block-separator-inline-css',
     1546                            'classic-theme-styles-inline-css',
     1547                            'third-party-test-block-css',
     1548                            'custom-block-styles-css',
     1549                            'global-styles-inline-css',
     1550                        ),
     1551                        $common_late_in_head,
     1552                        $common_late_in_body
     1553                    ),
     1554                    'BODY' => array(),
     1555                ),
     1556            ),
     1557            'classic_theme_styles_omitted'                => array(
     1558                'set_up'            => static function () {
     1559                    // Note that wp_enqueue_scripts is used instead of enqueue_block_assets because it runs again at the former action.
     1560                    add_action(
     1561                        'wp_enqueue_scripts',
     1562                        static function () {
     1563                            wp_dequeue_style( 'classic-theme-styles' );
     1564                        },
     1565                        100
     1566                    );
     1567                },
     1568                'inline_size_limit' => PHP_INT_MAX,
     1569                'expected_styles'   => array(
     1570                    'HEAD' => array_merge(
     1571                        $early_common_styles,
     1572                        array(
     1573                            'wp-block-library-inline-css',
     1574                            'wp-block-separator-inline-css',
     1575                            'third-party-test-block-css',
     1576                            'custom-block-styles-css',
     1577                            'global-styles-inline-css',
     1578                        ),
     1579                        $common_late_in_head,
     1580                        $common_late_in_body
     1581                    ),
     1582                    'BODY' => array(),
     1583                ),
     1584            ),
     1585            'no_styles_at_enqueued_block_assets'          => array(
     1586                'set_up'            => static function () {
     1587                    add_action(
     1588                        'wp_enqueue_scripts',
     1589                        static function () {
     1590                            wp_dequeue_style( 'classic-theme-styles' );
     1591                            wp_dequeue_style( 'custom-block-styles' );
     1592                        },
     1593                        100
     1594                    );
     1595                },
     1596                'inline_size_limit' => PHP_INT_MAX,
     1597                'expected_styles'   => array(
     1598                    'HEAD' => array_merge(
     1599                        $early_common_styles,
     1600                        array(
     1601                            'wp-block-library-inline-css',
     1602                            'wp-block-separator-inline-css',
     1603                            'third-party-test-block-css',
     1604                            'global-styles-inline-css',
     1605                        ),
     1606                        $common_late_in_head,
     1607                        $common_late_in_body
     1608                    ),
     1609                    'BODY' => array(),
     1610                ),
     1611            ),
     1612            'no_global_styles'                            => array(
     1613                'set_up'            => static function () {
     1614                    add_filter(
     1615                        'print_styles_array',
     1616                        static function ( $handles ) {
     1617                            return array_values( array_diff( $handles, array( 'global-styles' ) ) );
     1618                        }
     1619                    );
     1620                },
     1621                'inline_size_limit' => PHP_INT_MAX,
     1622                'expected_styles'   => array(
     1623                    'HEAD' => array_merge(
     1624                        $early_common_styles,
     1625                        array(
     1626                            'wp-block-library-inline-css',
     1627                            'wp-block-separator-inline-css',
     1628                            'classic-theme-styles-inline-css',
     1629                            'third-party-test-block-css',
     1630                            'custom-block-styles-css',
     1631                        ),
     1632                        $common_late_in_head,
     1633                        $common_late_in_body
    15231634                    ),
    15241635                    'BODY' => array(),
     
    15521663                'inline_size_limit' => 0,
    15531664                'expected_styles'   => array(
    1554                     'HEAD' => array(
    1555                         'wp-img-auto-sizes-contain-inline-css',
    1556                         'early-css',
    1557                         'early-inline-css',
    1558                         'wp-emoji-styles-inline-css',
    1559                         'wp-block-library-css',
    1560                         'classic-theme-styles-css',
    1561                         'global-styles-inline-css',
    1562                         'normal-css',
    1563                         'normal-inline-css',
    1564                         'wp-custom-css',
     1665                    'HEAD' => array_merge(
     1666                        $early_common_styles,
     1667                        array(
     1668                            'wp-block-library-css',
     1669                            'classic-theme-styles-css',
     1670                            'third-party-test-block-css',
     1671                            'custom-block-styles-css',
     1672                            'global-styles-inline-css',
     1673                        ),
     1674                        $common_late_in_head
    15651675                    ),
    1566                     'BODY' => array(
    1567                         'late-css',
    1568                         'late-inline-css',
    1569                         'core-block-supports-inline-css',
    1570                     ),
     1676                    'BODY' => $common_late_in_body,
    15711677                ),
    15721678            ),
     
    16151721                'inline_size_limit' => 0,
    16161722                'expected_styles'   => array(
    1617                     'HEAD' => array(
    1618                         'wp-img-auto-sizes-contain-inline-css',
    1619                         'early-css',
    1620                         'early-inline-css',
    1621                         'wp-emoji-styles-inline-css',
    1622                         'classic-theme-styles-css',
    1623                         'global-styles-inline-css',
    1624                         'normal-css',
    1625                         'normal-inline-css',
    1626                         'wp-custom-css',
     1723                    'HEAD' => array_merge(
     1724                        $early_common_styles,
     1725                        array(
     1726                            'classic-theme-styles-css',
     1727                            'third-party-test-block-css',
     1728                            'custom-block-styles-css',
     1729                            'global-styles-inline-css',
     1730                        ),
     1731                        $common_late_in_head
    16271732                    ),
    1628                     'BODY' => array(
    1629                         'late-css',
    1630                         'late-inline-css',
    1631                         'core-block-supports-inline-css',
    1632                     ),
     1733                    'BODY' => $common_late_in_body,
    16331734                ),
    16341735            ),
     
    16451746                'inline_size_limit' => 0,
    16461747                'expected_styles'   => array(
    1647                     'HEAD' => array(
    1648                         'wp-img-auto-sizes-contain-inline-css',
    1649                         'early-css',
    1650                         'early-inline-css',
    1651                         'wp-emoji-styles-inline-css',
    1652                         'wp-block-library-css',
    1653                         'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text.
    1654                         'wp-block-separator-css',
    1655                         'global-styles-inline-css',
    1656                         'core-block-supports-inline-css',
    1657                         'classic-theme-styles-css',
    1658                         'normal-css',
    1659                         'normal-inline-css',
    1660                         'wp-custom-css',
    1661                         'late-css',
    1662                         'late-inline-css',
     1748                    'HEAD' => array_merge(
     1749                        $early_common_styles,
     1750                        array(
     1751                            'wp-block-library-css',
     1752                            'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text.
     1753                            'wp-block-separator-css',
     1754                            'classic-theme-styles-css',
     1755                            'third-party-test-block-css',
     1756                            'custom-block-styles-css',
     1757                            'global-styles-inline-css',
     1758                        ),
     1759                        $common_late_in_head,
     1760                        $common_late_in_body
    16631761                    ),
    16641762                    'BODY' => array(),
     
    16721770     *
    16731771     * @ticket 64099
     1772
    16741773     * @covers ::wp_load_classic_theme_block_styles_on_demand
    16751774     * @covers ::wp_hoist_late_printed_styles
     
    16971796            static function () {
    16981797                return '/* CUSTOM CSS from Customizer */';
     1798
     1799
     1800
     1801
     1802
     1803
     1804
     1805
     1806
     1807
     1808
     1809
     1810
     1811
     1812
     1813
     1814
     1815
     1816
    16991817            }
    17001818        );
     
    17521870        $content = apply_filters(
    17531871            'the_content',
    1754             '<!-- wp:separator --><hr class="wp-block-separator has-alpha-channel-opacity"/><!-- /wp:separator -->'
     1872            '<!-- wp:separator --><hr class="wp-block-separator has-alpha-channel-opacity"/><!-- /wp:separator -->' .
     1873            '<!-- wp:third-party/test --><div>This is only a test!</div><!-- /wp:third-party/test -->'
    17551874        );
    17561875
Note: See TracChangeset for help on using the changeset viewer.