Make WordPress Core


Ignore:
Timestamp:
01/29/2026 06:14:05 PM (2 months 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.

File:
1 edited

Legend:

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

    r61539 r61554  
    37023702    }
    37033703
     3704    // Capture the styles enqueued at the enqueue_block_assets action, so that non-core block styles and global styles can be inserted afterwards during hoisting.
     3705    $style_handles_at_enqueue_block_assets = array();
     3706    add_action(
     3707        'enqueue_block_assets',
     3708        static function () use ( &$style_handles_at_enqueue_block_assets ) {
     3709            $style_handles_at_enqueue_block_assets = wp_styles()->queue;
     3710        },
     3711        PHP_INT_MIN
     3712    );
     3713    add_action(
     3714        'enqueue_block_assets',
     3715        static function () use ( &$style_handles_at_enqueue_block_assets ) {
     3716            $style_handles_at_enqueue_block_assets = array_values( array_diff( wp_styles()->queue, $style_handles_at_enqueue_block_assets ) );
     3717        },
     3718        PHP_INT_MAX
     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 be 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_core_block_styles = array_values( array_intersect( $all_core_block_style_handles, wp_styles()->queue ) );
     3767        if ( count( $enqueued_core_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, &$style_handles_at_enqueue_block_assets, &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_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                    $processor->set_bookmark( 'wp_block_library' );
     3878                } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) {
     3879                    $processor->set_bookmark( 'head_end' );
     3880                    break;
     3881                } elseif ( ( 'STYLE' === $processor->get_tag() || 'LINK' === $processor->get_tag() ) && $processor->get_attribute( 'id' ) ) {
     3882                    $id     = $processor->get_attribute( 'id' );
     3883                    $handle = null;
     3884                    if ( 'STYLE' === $processor->get_tag() ) {
     3885                        if ( preg_match( '/^(.+)-inline-css$/', $id, $matches ) ) {
     3886                            $handle = $matches[1];
     3887                        }
     3888                    } elseif ( preg_match( '/^(.+)-css$/', $id, $matches ) ) {
     3889                        $handle = $matches[1];
    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             * Insert block styles right after wp-block-library (if it is present). The placeholder CSS comment will
     3907             * always be added to the wp-block-library inline style since it gets printed at `wp_head` before the blocks
     3908             * are rendered. This means that there may not actually be any block styles to hoist from the footer to
     3909             * insert after this inline style. The placeholder CSS comment needs to be added so that the inline style
     3910             * gets printed, but if the resulting inline style is empty after the placeholder is removed, then the
     3911             * inline style is removed.
     3912             */
     3913            if ( $processor->has_bookmark( 'wp_block_library' ) ) {
     3914                $processor->seek( 'wp_block_library' );
     3915
     3916                $css_text = $processor->get_modifiable_text();
     3917
     3918                /*
     3919                 * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to
     3920                 * be printed. Now that we've located the inline style, the placeholder comment can be removed. If
     3921                 * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL
     3922                 * comment), then remove the STYLE entirely.
     3923                 */
     3924                $css_text = str_replace( $placeholder, '', $css_text );
     3925                if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) {
     3926                    $processor->remove();
     3927                } else {
     3928                    $processor->set_modifiable_text( $css_text );
     3929                }
     3930
     3931                $inserted_after            = $printed_core_block_styles;
     3932                $printed_core_block_styles = '';
     3933
     3934                // If the classic-theme-styles is absent, then the third-party block styles cannot be inserted after it, so they get inserted here.
     3935                if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) {
     3936                    if ( '' !== $printed_other_block_styles ) {
     3937                        $inserted_after .= $printed_other_block_styles;
     3938                    }
     3939                    $printed_other_block_styles = '';
     3940
     3941                    // If there aren't any other styles printed at enqueue_block_assets either, then the global styles need to also be printed here.
     3942                    if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) {
     3943                        if ( '' !== $printed_global_styles ) {
     3944                            $inserted_after .= $printed_global_styles;
     3945                        }
     3946                        $printed_global_styles = '';
     3947                    }
     3948                }
     3949
     3950                if ( '' !== $inserted_after ) {
     3951                    $processor->insert_after( "\n" . $inserted_after );
     3952                }
     3953            }
     3954
     3955            // Insert global-styles after the styles enqueued at enqueue_block_assets.
     3956            if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) {
     3957                $processor->seek( 'last_style_at_enqueue_block_assets' );
     3958
     3959                $processor->insert_after( "\n" . $printed_global_styles );
     3960                $printed_global_styles = '';
     3961
     3962                if ( ! $processor->has_bookmark( 'classic_theme_styles' ) && '' !== $printed_other_block_styles ) {
     3963                    $processor->insert_after( "\n" . $printed_other_block_styles );
     3964                    $printed_other_block_styles = '';
     3965                }
     3966            }
     3967
     3968            // Insert third-party block styles right after the classic-theme-styles.
     3969            if ( '' !== $printed_other_block_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) {
     3970                $processor->seek( 'classic_theme_styles' );
     3971                $processor->insert_after( "\n" . $printed_other_block_styles );
     3972                $printed_other_block_styles = '';
     3973            }
     3974
     3975            // Print all remaining styles.
     3976            $remaining_styles = $printed_core_block_styles . $printed_other_block_styles . $printed_global_styles . $printed_late_styles;
     3977            if ( $remaining_styles && $processor->has_bookmark( 'head_end' ) ) {
     3978                $processor->seek( 'head_end' );
     3979                $processor->insert_before( $remaining_styles . "\n" );
     3980            }
    38853981            return $processor->get_updated_html();
    38863982        }
Note: See TracChangeset for help on using the changeset viewer.