From 523be88d080073b57a981a1a2fe9fce6ba3ecfad Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 4 Dec 2025 23:44:31 -0800 Subject: [PATCH 01/21] Preserve original CSS cascade when hoisting late-printed styles --- src/wp-includes/script-loader.php | 130 +++++++++++++++++++----------- tests/phpunit/tests/template.php | 12 +-- 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 7dccff9775731..c3e387e732a7f 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3698,9 +3698,10 @@ static function () use ( $placeholder ) { * later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts` * before `print_footer_scripts()` is called. */ - $printed_block_styles = ''; - $printed_late_styles = ''; - $capture_late_styles = static function () use ( &$printed_block_styles, &$printed_late_styles ) { + $printed_block_styles = ''; + $printed_global_styles = ''; + $printed_late_styles = ''; + $capture_late_styles = static function () use ( &$printed_block_styles, &$printed_global_styles, &$printed_late_styles ) { // Gather the styles related to on-demand block enqueues. $all_block_style_handles = array(); foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) { @@ -3708,15 +3709,6 @@ static function () use ( $placeholder ) { $all_block_style_handles[] = $style_handle; } } - $all_block_style_handles = array_merge( - $all_block_style_handles, - array( - 'global-styles', - 'block-style-variation-styles', - 'core-block-supports', - 'core-block-supports-duotone', - ) - ); /* * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet @@ -3729,6 +3721,13 @@ static function () use ( $placeholder ) { $printed_block_styles = ob_get_clean(); } + // Capture the global-styles so that it can be printed separately after classic-theme-styles, to preserve the original order, + if ( wp_style_is( 'global-styles' ) ) { + ob_start(); + wp_styles()->do_items( array( 'global-styles' ) ); + $printed_global_styles = ob_get_clean(); + } + /* * Print all remaining styles not related to blocks. This contains a subset of the logic from * `print_late_styles()`, without admin-specific logic and the `print_late_styles` filter to control whether @@ -3767,7 +3766,7 @@ static function () use ( $capture_late_styles ) { // Replace placeholder with the captured late styles. add_filter( 'wp_template_enhancement_output_buffer', - static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_late_styles ) { + static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_global_styles, &$printed_late_styles ) { // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans. $processor = new class( $buffer ) extends WP_HTML_Tag_Processor { @@ -3812,53 +3811,86 @@ public function remove() { } }; - /* - * Insert block styles right after wp-block-library (if it is present), and then insert any remaining styles - * at (or else print everything there). The placeholder CSS comment will always be added to the - * wp-block-library inline style since it gets printed at `wp_head` before the blocks are rendered. - * This means that there may not actually be any block styles to hoist from the footer to insert after this - * inline style. The placeholder CSS comment needs to be added so that the inline style gets printed, but - * if the resulting inline style is empty after the placeholder is removed, then the inline style is - * removed. - */ + // Locate the insertion points in the HEAD. while ( $processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) { if ( 'STYLE' === $processor->get_tag() && 'wp-block-library-inline-css' === $processor->get_attribute( 'id' ) ) { - $css_text = $processor->get_modifiable_text(); - - /* - * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to - * be printed. Now that we've located the inline style, the placeholder comment can be removed. If - * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL - * comment, then remove the STYLE entirely.) - */ - $css_text = str_replace( $placeholder, '', $css_text ); - if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) { - $processor->remove(); - } else { - $processor->set_modifiable_text( $css_text ); - } + $processor->set_bookmark( 'wp_block_library' ); + } elseif ( + ( + 'STYLE' === $processor->get_tag() && + 'classic-theme-styles-inline-css' === $processor->get_attribute( 'id' ) + ) || + ( + 'LINK' === $processor->get_tag() && + 'classic-theme-styles-css' === $processor->get_attribute( 'id' ) + ) + ) { + $processor->set_bookmark( 'classic_theme_styles' ); + } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) { + $processor->set_bookmark( 'head_end' ); + break; + } + } - // Insert the $printed_late_styles immediately after the closing inline STYLE tag. This preserves the CSS cascade. - if ( '' !== $printed_block_styles ) { - $processor->insert_after( $printed_block_styles ); + /* + * Insert block styles right after wp-block-library (if it is present). The placeholder CSS comment will + * always be added to the wp-block-library inline style since it gets printed at `wp_head` before the blocks + * are rendered. This means that there may not actually be any block styles to hoist from the footer to + * insert after this inline style. The placeholder CSS comment needs to be added so that the inline style + * gets printed, but if the resulting inline style is empty after the placeholder is removed, then the + * inline style is removed. + */ + if ( $processor->has_bookmark( 'wp_block_library' ) ) { + $processor->seek( 'wp_block_library' ); - // Prevent printing them again at . - $printed_block_styles = ''; - } + $css_text = $processor->get_modifiable_text(); - // If there aren't any late styles, there's no need to continue to finding . - if ( '' === $printed_late_styles ) { - break; - } - } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) { - $processor->insert_before( $printed_block_styles . $printed_late_styles ); - break; + /* + * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to + * be printed. Now that we've located the inline style, the placeholder comment can be removed. If + * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL + * comment, then remove the STYLE entirely.) + */ + $css_text = str_replace( $placeholder, '', $css_text ); + if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) { + $processor->remove(); + } else { + $processor->set_modifiable_text( $css_text ); + } + + $inserted_after = $printed_block_styles; + if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { + $inserted_after .= $printed_global_styles; + + // Prevent printing them again at . + $printed_global_styles = ''; + } + + if ( '' !== $inserted_after ) { + $processor->insert_after( $inserted_after ); + + // Prevent printing them again at . + $printed_block_styles = ''; } } + if ( $printed_global_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) { + $processor->seek( 'classic_theme_styles' ); + $processor->insert_after( $printed_global_styles ); + + // Prevent printing them again at . + $printed_global_styles = ''; + } + + // Print all remaining styles. + $remaining_styles = $printed_block_styles . $printed_global_styles . $printed_late_styles; + if ( $remaining_styles && $processor->has_bookmark( 'head_end' ) ) { + $processor->seek( 'head_end' ); + $processor->insert_before( $remaining_styles ); + } return $processor->get_updated_html(); } ); diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index a304fff95f865..5c8409375f97f 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1482,14 +1482,14 @@ public function data_wp_hoist_late_printed_styles(): array { 'wp-emoji-styles-inline-css', 'wp-block-library-css', 'wp-block-separator-css', - 'global-styles-inline-css', - 'core-block-supports-inline-css', 'classic-theme-styles-css', + 'global-styles-inline-css', 'normal-css', 'normal-inline-css', 'wp-custom-css', 'late-css', 'late-inline-css', + 'core-block-supports-inline-css', ); return array( @@ -1512,14 +1512,14 @@ public function data_wp_hoist_late_printed_styles(): array { 'wp-emoji-styles-inline-css', 'wp-block-library-inline-css', 'wp-block-separator-inline-css', - 'global-styles-inline-css', - 'core-block-supports-inline-css', 'classic-theme-styles-inline-css', + 'global-styles-inline-css', 'normal-css', 'normal-inline-css', 'wp-custom-css', 'late-css', 'late-inline-css', + 'core-block-supports-inline-css', ), 'BODY' => array(), ), @@ -1652,14 +1652,14 @@ function (): void { 'wp-block-library-css', 'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text. 'wp-block-separator-css', - 'global-styles-inline-css', - 'core-block-supports-inline-css', 'classic-theme-styles-css', + 'global-styles-inline-css', 'normal-css', 'normal-inline-css', 'wp-custom-css', 'late-css', 'late-inline-css', + 'core-block-supports-inline-css', ), 'BODY' => array(), ), From 2fb1c42a3fe643472ccfe42cf1b8833299ed80ba Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 15 Dec 2025 13:03:19 -0800 Subject: [PATCH 02/21] Ensure styles for third-party blocks get printed after classic-theme-styles --- src/wp-includes/script-loader.php | 58 +++++++++++++++++++++---------- tests/phpunit/tests/template.php | 21 ++++++++++- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index c3e387e732a7f..5a4a6f778f476 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3698,15 +3698,24 @@ static function () use ( $placeholder ) { * later hoisted to the HEAD in the template enhancement output buffer. This will run at `wp_print_footer_scripts` * before `print_footer_scripts()` is called. */ - $printed_block_styles = ''; - $printed_global_styles = ''; - $printed_late_styles = ''; - $capture_late_styles = static function () use ( &$printed_block_styles, &$printed_global_styles, &$printed_late_styles ) { + $printed_core_block_styles = ''; + $printed_other_block_styles = ''; + $printed_global_styles = ''; + $printed_late_styles = ''; + + $capture_late_styles = static function () use ( &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) { // Gather the styles related to on-demand block enqueues. - $all_block_style_handles = array(); + $all_core_block_style_handles = array(); + $all_other_block_style_handles = array(); foreach ( WP_Block_Type_Registry::get_instance()->get_all_registered() as $block_type ) { - foreach ( $block_type->style_handles as $style_handle ) { - $all_block_style_handles[] = $style_handle; + if ( str_starts_with( $block_type->name, 'core/' ) ) { + foreach ( $block_type->style_handles as $style_handle ) { + $all_core_block_style_handles[] = $style_handle; + } + } else { + foreach ( $block_type->style_handles as $style_handle ) { + $all_other_block_style_handles[] = $style_handle; + } } } @@ -3714,11 +3723,18 @@ static function () use ( $placeholder ) { * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet * to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`. */ - $enqueued_block_styles = array_values( array_intersect( $all_block_style_handles, wp_styles()->queue ) ); - if ( count( $enqueued_block_styles ) > 0 ) { + $enqueued_core_block_styles = array_values( array_intersect( $all_core_block_style_handles, wp_styles()->queue ) ); + if ( count( $enqueued_core_block_styles ) > 0 ) { + ob_start(); + wp_styles()->do_items( $enqueued_core_block_styles ); + $printed_core_block_styles = ob_get_clean(); + } + + $enqueued_other_block_styles = array_values( array_intersect( $all_other_block_style_handles, wp_styles()->queue ) ); + if ( count( $enqueued_other_block_styles ) > 0 ) { ob_start(); - wp_styles()->do_items( $enqueued_block_styles ); - $printed_block_styles = ob_get_clean(); + wp_styles()->do_items( $enqueued_other_block_styles ); + $printed_other_block_styles = ob_get_clean(); } // Capture the global-styles so that it can be printed separately after classic-theme-styles, to preserve the original order, @@ -3766,7 +3782,7 @@ static function () use ( $capture_late_styles ) { // Replace placeholder with the captured late styles. add_filter( 'wp_template_enhancement_output_buffer', - static function ( $buffer ) use ( $placeholder, &$printed_block_styles, &$printed_global_styles, &$printed_late_styles ) { + static function ( $buffer ) use ( $placeholder, &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) { // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans. $processor = new class( $buffer ) extends WP_HTML_Tag_Processor { @@ -3861,32 +3877,36 @@ public function remove() { $processor->set_modifiable_text( $css_text ); } - $inserted_after = $printed_block_styles; + $inserted_after = $printed_core_block_styles; if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { + $inserted_after .= $printed_other_block_styles; $inserted_after .= $printed_global_styles; // Prevent printing them again at . - $printed_global_styles = ''; + $printed_other_block_styles = ''; + $printed_global_styles = ''; } if ( '' !== $inserted_after ) { $processor->insert_after( $inserted_after ); // Prevent printing them again at . - $printed_block_styles = ''; + $printed_core_block_styles = ''; } } - if ( $printed_global_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) { + $inserted_after = $printed_other_block_styles . $printed_global_styles; + if ( '' !== $inserted_after && $processor->has_bookmark( 'classic_theme_styles' ) ) { $processor->seek( 'classic_theme_styles' ); - $processor->insert_after( $printed_global_styles ); + $processor->insert_after( $inserted_after ); // Prevent printing them again at . - $printed_global_styles = ''; + $printed_other_block_styles = ''; + $printed_global_styles = ''; } // Print all remaining styles. - $remaining_styles = $printed_block_styles . $printed_global_styles . $printed_late_styles; + $remaining_styles = $printed_core_block_styles . $printed_other_block_styles . $printed_global_styles . $printed_late_styles; if ( $remaining_styles && $processor->has_bookmark( 'head_end' ) ) { $processor->seek( 'head_end' ); $processor->insert_before( $remaining_styles ); diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index 5c8409375f97f..78e1fdd27616e 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -146,6 +146,11 @@ public function tear_down() { unregister_taxonomy( 'taxo' ); $this->set_permalink_structure( '' ); + $registry = WP_Block_Type_Registry::get_instance(); + if ( $registry->is_registered( 'third-party/test' ) ) { + $registry->unregister( 'third-party/test' ); + } + parent::tear_down(); } @@ -1483,6 +1488,7 @@ public function data_wp_hoist_late_printed_styles(): array { 'wp-block-library-css', 'wp-block-separator-css', 'classic-theme-styles-css', + 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1513,6 +1519,7 @@ public function data_wp_hoist_late_printed_styles(): array { 'wp-block-library-inline-css', 'wp-block-separator-inline-css', 'classic-theme-styles-inline-css', + 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1558,6 +1565,7 @@ static function () { 'wp-emoji-styles-inline-css', 'wp-block-library-css', 'classic-theme-styles-css', + 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1620,6 +1628,7 @@ function (): void { 'early-inline-css', 'wp-emoji-styles-inline-css', 'classic-theme-styles-css', + 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1653,6 +1662,7 @@ function (): void { 'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text. 'wp-block-separator-css', 'classic-theme-styles-css', + 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1696,6 +1706,14 @@ static function () { } ); + wp_register_style( 'third-party-test-block', 'https://example.com/third-party-test-block.css', array(), null ); + register_block_type( + 'third-party/test', + array( + 'style_handles' => array( 'third-party-test-block' ), + ) + ); + if ( $set_up ) { $set_up(); } @@ -1746,7 +1764,8 @@ static function () { // Simulate the_content(). $content = apply_filters( 'the_content', - '
' + '
' . + '
This is only a test!
' ); // Simulate footer scripts. From 51c47cf0a69a5ea45e01bde753c082b190e5665e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 15 Dec 2025 21:58:04 -0800 Subject: [PATCH 03/21] Mostly account for styles enqueued at enqueued_block_assets --- src/wp-includes/script-loader.php | 53 +++++++++++++++++++++---------- tests/phpunit/tests/template.php | 40 ++++++++++++++++++++--- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 5a4a6f778f476..afc549a4ab747 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3678,6 +3678,23 @@ function wp_hoist_late_printed_styles() { return; } + // Capture the styles enqueued at the enqueue_block_assets action, so that non-core block styles and global styles can be inserted after at hoisting. + $style_handles_at_enqueued_block_assets = array(); + add_action( + 'enqueue_block_assets', + static function () use ( &$style_handles_at_enqueued_block_assets ) { + $style_handles_at_enqueued_block_assets = wp_styles()->queue; + }, + PHP_INT_MIN + ); + add_action( + 'enqueue_block_assets', + static function () use ( &$style_handles_at_enqueued_block_assets ) { + $style_handles_at_enqueued_block_assets = array_values( array_diff( wp_styles()->queue, $style_handles_at_enqueued_block_assets ) ); + }, + PHP_INT_MAX + ); + /* * Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles * can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement @@ -3782,7 +3799,7 @@ static function () use ( $capture_late_styles ) { // Replace placeholder with the captured late styles. add_filter( 'wp_template_enhancement_output_buffer', - static function ( $buffer ) use ( $placeholder, &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) { + static function ( $buffer ) use ( $placeholder, &$style_handles_at_enqueued_block_assets, &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) { // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans. $processor = new class( $buffer ) extends WP_HTML_Tag_Processor { @@ -3834,20 +3851,23 @@ public function remove() { 'wp-block-library-inline-css' === $processor->get_attribute( 'id' ) ) { $processor->set_bookmark( 'wp_block_library' ); - } elseif ( - ( - 'STYLE' === $processor->get_tag() && - 'classic-theme-styles-inline-css' === $processor->get_attribute( 'id' ) - ) || - ( - 'LINK' === $processor->get_tag() && - 'classic-theme-styles-css' === $processor->get_attribute( 'id' ) - ) - ) { - $processor->set_bookmark( 'classic_theme_styles' ); } elseif ( 'HEAD' === $processor->get_tag() && $processor->is_tag_closer() ) { $processor->set_bookmark( 'head_end' ); break; + } elseif ( ( 'STYLE' === $processor->get_tag() || 'LINK' === $processor->get_tag() ) && $processor->get_attribute( 'id' ) ) { + $id = $processor->get_attribute( 'id' ); + $handle = null; + if ( 'STYLE' === $processor->get_tag() ) { + if ( preg_match( '/^(.+)-inline-css$/', $id, $matches ) ) { + $handle = $matches[1]; + } + } elseif ( preg_match( '/^(.+)-css$/', $id, $matches ) ) { + $handle = $matches[1]; + } + + if ( $handle && in_array( $handle, $style_handles_at_enqueued_block_assets, true ) ) { + $processor->set_bookmark( 'last_style_at_enqueued_block_assets' ); + } } } @@ -3878,7 +3898,8 @@ public function remove() { } $inserted_after = $printed_core_block_styles; - if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { + if ( ! $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { + // TODO: There is no coverage for this. $inserted_after .= $printed_other_block_styles; $inserted_after .= $printed_global_styles; @@ -3896,9 +3917,9 @@ public function remove() { } $inserted_after = $printed_other_block_styles . $printed_global_styles; - if ( '' !== $inserted_after && $processor->has_bookmark( 'classic_theme_styles' ) ) { - $processor->seek( 'classic_theme_styles' ); - $processor->insert_after( $inserted_after ); + if ( '' !== $inserted_after && $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { + $processor->seek( 'last_style_at_enqueued_block_assets' ); + $processor->insert_after( "\n" . $inserted_after ); // Prevent printing them again at . $printed_other_block_styles = ''; diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index 78e1fdd27616e..e0ed0d2e801db 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1485,14 +1485,31 @@ public function data_wp_hoist_late_printed_styles(): array { 'early-css', 'early-inline-css', 'wp-emoji-styles-inline-css', - 'wp-block-library-css', - 'wp-block-separator-css', - 'classic-theme-styles-css', - 'third-party-test-block-css', + + // Core block styles enqueued by wp_common_block_scripts_and_styles() at which runs at wp_enqueue_scripts priority 10, added first. + 'wp-block-library-css', // Inline printed. + 'wp-block-separator-css', // Hoisted. + + // The wp_common_block_scripts_and_styles() function also fires enqueue_block_assets, at which wp_enqueue_classic_theme_styles() runs. + 'classic-theme-styles-css', // Printed at enqueue_block_assets. + + // Other styles enqueued at enqueue_block_assets, which is fired by wp_common_block_scripts_and_styles(). + 'custom-block-styles-css', // Printed at enqueue_block_assets. + + // Third-party block styles. + 'third-party-test-block-css', // Hoisted. + + // Hoisted. Enqueued by wp_enqueue_global_styles() which runs at wp_enqueue_scripts priority 10 and wp_footer priority 1. 'global-styles-inline-css', + + // Styles enqueued at wp_enqueue_scripts (priority 10). 'normal-css', 'normal-inline-css', + + // Styles printed at wp_head priority 10. 'wp-custom-css', + + // Hoisted. Styles printed in the footer. 'late-css', 'late-inline-css', 'core-block-supports-inline-css', @@ -1519,6 +1536,7 @@ public function data_wp_hoist_late_printed_styles(): array { 'wp-block-library-inline-css', 'wp-block-separator-inline-css', 'classic-theme-styles-inline-css', + 'custom-block-styles-css', 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', @@ -1565,6 +1583,7 @@ static function () { 'wp-emoji-styles-inline-css', 'wp-block-library-css', 'classic-theme-styles-css', + 'custom-block-styles-css', // TODO: Test is failing here. 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', @@ -1629,6 +1648,7 @@ function (): void { 'wp-emoji-styles-inline-css', 'classic-theme-styles-css', 'third-party-test-block-css', + 'custom-block-styles-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1662,6 +1682,7 @@ function (): void { 'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text. 'wp-block-separator-css', 'classic-theme-styles-css', + 'custom-block-styles-css', 'third-party-test-block-css', 'global-styles-inline-css', 'normal-css', @@ -1714,6 +1735,17 @@ static function () { ) ); + /* + * This is very old guidance about how to add enqueue styles for blocks. Certain themes still enqueue block + * styles using this action. + */ + add_action( + 'enqueue_block_assets', + static function () { + wp_enqueue_style( 'custom-block-styles', 'https://example.com/custom-block-styles.css', array(), null ); + } + ); + if ( $set_up ) { $set_up(); } From 357f7463685b92bd1b86e730c05cd956188f0ee4 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 16 Dec 2025 12:31:53 -0800 Subject: [PATCH 04/21] Fix insertion of global styles and third-party block styles --- src/wp-includes/script-loader.php | 31 +++++++++++++++++++------------ tests/phpunit/tests/template.php | 12 ++++++------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index afc549a4ab747..5d02bf6a25a32 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3865,7 +3865,14 @@ public function remove() { $handle = $matches[1]; } + if ( 'classic-theme-styles' === $handle ) { + $processor->set_bookmark( 'classic_theme_styles' ); + } + if ( $handle && in_array( $handle, $style_handles_at_enqueued_block_assets, true ) ) { + if ( ! $processor->has_bookmark( 'first_style_at_enqueued_block_assets' ) ) { + $processor->set_bookmark( 'first_style_at_enqueued_block_assets' ); + } $processor->set_bookmark( 'last_style_at_enqueued_block_assets' ); } } @@ -3897,40 +3904,40 @@ public function remove() { $processor->set_modifiable_text( $css_text ); } - $inserted_after = $printed_core_block_styles; + $inserted_after = $printed_core_block_styles; + $printed_core_block_styles = ''; + if ( ! $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { // TODO: There is no coverage for this. $inserted_after .= $printed_other_block_styles; $inserted_after .= $printed_global_styles; - // Prevent printing them again at . $printed_other_block_styles = ''; $printed_global_styles = ''; } if ( '' !== $inserted_after ) { - $processor->insert_after( $inserted_after ); - - // Prevent printing them again at . - $printed_core_block_styles = ''; + $processor->insert_after( "\n" . $inserted_after ); } } - $inserted_after = $printed_other_block_styles . $printed_global_styles; - if ( '' !== $inserted_after && $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { + if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { $processor->seek( 'last_style_at_enqueued_block_assets' ); - $processor->insert_after( "\n" . $inserted_after ); + $processor->insert_after( "\n" . $printed_global_styles ); + $printed_global_styles = ''; + } - // Prevent printing them again at . + if ( '' !== $printed_other_block_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) { + $processor->seek( 'classic_theme_styles' ); + $processor->insert_after( "\n" . $printed_other_block_styles ); $printed_other_block_styles = ''; - $printed_global_styles = ''; } // Print all remaining styles. $remaining_styles = $printed_core_block_styles . $printed_other_block_styles . $printed_global_styles . $printed_late_styles; if ( $remaining_styles && $processor->has_bookmark( 'head_end' ) ) { $processor->seek( 'head_end' ); - $processor->insert_before( $remaining_styles ); + $processor->insert_before( $remaining_styles . "\n" ); } return $processor->get_updated_html(); } diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index e0ed0d2e801db..afe62f42941fa 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1493,12 +1493,12 @@ public function data_wp_hoist_late_printed_styles(): array { // The wp_common_block_scripts_and_styles() function also fires enqueue_block_assets, at which wp_enqueue_classic_theme_styles() runs. 'classic-theme-styles-css', // Printed at enqueue_block_assets. - // Other styles enqueued at enqueue_block_assets, which is fired by wp_common_block_scripts_and_styles(). - 'custom-block-styles-css', // Printed at enqueue_block_assets. - // Third-party block styles. 'third-party-test-block-css', // Hoisted. + // Other styles enqueued at enqueue_block_assets, which is fired by wp_common_block_scripts_and_styles(). + 'custom-block-styles-css', // Printed at enqueue_block_assets. + // Hoisted. Enqueued by wp_enqueue_global_styles() which runs at wp_enqueue_scripts priority 10 and wp_footer priority 1. 'global-styles-inline-css', @@ -1536,8 +1536,8 @@ public function data_wp_hoist_late_printed_styles(): array { 'wp-block-library-inline-css', 'wp-block-separator-inline-css', 'classic-theme-styles-inline-css', - 'custom-block-styles-css', 'third-party-test-block-css', + 'custom-block-styles-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1583,8 +1583,8 @@ static function () { 'wp-emoji-styles-inline-css', 'wp-block-library-css', 'classic-theme-styles-css', - 'custom-block-styles-css', // TODO: Test is failing here. 'third-party-test-block-css', + 'custom-block-styles-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', @@ -1682,8 +1682,8 @@ function (): void { 'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text. 'wp-block-separator-css', 'classic-theme-styles-css', - 'custom-block-styles-css', 'third-party-test-block-css', + 'custom-block-styles-css', 'global-styles-inline-css', 'normal-css', 'normal-inline-css', From c6d0732416933c3479e7ae16b5d0e92990b4e2a4 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 16 Dec 2025 12:52:04 -0800 Subject: [PATCH 05/21] Handle omission of classic-theme-styles --- src/wp-includes/script-loader.php | 19 ++++++++++++------ tests/phpunit/tests/template.php | 33 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 5d02bf6a25a32..41b4a5f0a1dc5 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3907,13 +3907,14 @@ public function remove() { $inserted_after = $printed_core_block_styles; $printed_core_block_styles = ''; - if ( ! $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { - // TODO: There is no coverage for this. - $inserted_after .= $printed_other_block_styles; - $inserted_after .= $printed_global_styles; - + if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { + $inserted_after .= "\n" . $printed_other_block_styles; $printed_other_block_styles = ''; - $printed_global_styles = ''; + + if ( ! $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { + $inserted_after .= "\n" . $printed_global_styles; + $printed_global_styles = ''; + } } if ( '' !== $inserted_after ) { @@ -3923,8 +3924,14 @@ public function remove() { if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { $processor->seek( 'last_style_at_enqueued_block_assets' ); + $processor->insert_after( "\n" . $printed_global_styles ); $printed_global_styles = ''; + + if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { + $processor->insert_after( "\n" . $printed_other_block_styles ); + $printed_other_block_styles = ''; + } } if ( '' !== $printed_other_block_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) { diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index afe62f42941fa..e505a1869b8f3 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1549,6 +1549,39 @@ public function data_wp_hoist_late_printed_styles(): array { 'BODY' => array(), ), ), + 'classic_theme_styles_omitted' => array( + 'set_up' => static function () { + // Note that wp_enqueue_scripts is used instead of enqueue_block_assets because it runs again at the former action. + add_action( + 'wp_enqueue_scripts', + static function () { + wp_dequeue_style( 'classic-theme-styles' ); + }, + 100 + ); + }, + 'inline_size_limit' => PHP_INT_MAX, + 'expected_styles' => array( + 'HEAD' => array( + 'wp-img-auto-sizes-contain-inline-css', + 'early-css', + 'early-inline-css', + 'wp-emoji-styles-inline-css', + 'wp-block-library-inline-css', + 'wp-block-separator-inline-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + 'global-styles-inline-css', + 'normal-css', + 'normal-inline-css', + 'wp-custom-css', + 'late-css', + 'late-inline-css', + 'core-block-supports-inline-css', + ), + 'BODY' => array(), + ), + ), 'standard_classic_theme_config_extra_block_library_inline_style' => array( 'set_up' => static function () { add_action( From ba78b0940d63f18705e32866dd0c3813c2d436c6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 16 Dec 2025 12:56:19 -0800 Subject: [PATCH 06/21] Add test case for no styles enqueued at enqueued_block_assets --- src/wp-includes/script-loader.php | 26 ++++++++++++------------- tests/phpunit/tests/template.php | 32 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 41b4a5f0a1dc5..1e00fc527597c 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3679,18 +3679,18 @@ function wp_hoist_late_printed_styles() { } // Capture the styles enqueued at the enqueue_block_assets action, so that non-core block styles and global styles can be inserted after at hoisting. - $style_handles_at_enqueued_block_assets = array(); + $style_handles_at_enqueue_block_assets = array(); add_action( 'enqueue_block_assets', - static function () use ( &$style_handles_at_enqueued_block_assets ) { - $style_handles_at_enqueued_block_assets = wp_styles()->queue; + static function () use ( &$style_handles_at_enqueue_block_assets ) { + $style_handles_at_enqueue_block_assets = wp_styles()->queue; }, PHP_INT_MIN ); add_action( 'enqueue_block_assets', - static function () use ( &$style_handles_at_enqueued_block_assets ) { - $style_handles_at_enqueued_block_assets = array_values( array_diff( wp_styles()->queue, $style_handles_at_enqueued_block_assets ) ); + static function () use ( &$style_handles_at_enqueue_block_assets ) { + $style_handles_at_enqueue_block_assets = array_values( array_diff( wp_styles()->queue, $style_handles_at_enqueue_block_assets ) ); }, PHP_INT_MAX ); @@ -3799,7 +3799,7 @@ static function () use ( $capture_late_styles ) { // Replace placeholder with the captured late styles. add_filter( 'wp_template_enhancement_output_buffer', - static function ( $buffer ) use ( $placeholder, &$style_handles_at_enqueued_block_assets, &$printed_core_block_styles, &$printed_other_block_styles, &$printed_global_styles, &$printed_late_styles ) { + 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 ) { // Anonymous subclass of WP_HTML_Tag_Processor which exposes underlying bookmark spans. $processor = new class( $buffer ) extends WP_HTML_Tag_Processor { @@ -3869,11 +3869,11 @@ public function remove() { $processor->set_bookmark( 'classic_theme_styles' ); } - if ( $handle && in_array( $handle, $style_handles_at_enqueued_block_assets, true ) ) { - if ( ! $processor->has_bookmark( 'first_style_at_enqueued_block_assets' ) ) { - $processor->set_bookmark( 'first_style_at_enqueued_block_assets' ); + if ( $handle && in_array( $handle, $style_handles_at_enqueue_block_assets, true ) ) { + if ( ! $processor->has_bookmark( 'first_style_at_enqueue_block_assets' ) ) { + $processor->set_bookmark( 'first_style_at_enqueue_block_assets' ); } - $processor->set_bookmark( 'last_style_at_enqueued_block_assets' ); + $processor->set_bookmark( 'last_style_at_enqueue_block_assets' ); } } } @@ -3911,7 +3911,7 @@ public function remove() { $inserted_after .= "\n" . $printed_other_block_styles; $printed_other_block_styles = ''; - if ( ! $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { + if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { $inserted_after .= "\n" . $printed_global_styles; $printed_global_styles = ''; } @@ -3922,8 +3922,8 @@ public function remove() { } } - if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueued_block_assets' ) ) { - $processor->seek( 'last_style_at_enqueued_block_assets' ); + if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { + $processor->seek( 'last_style_at_enqueue_block_assets' ); $processor->insert_after( "\n" . $printed_global_styles ); $printed_global_styles = ''; diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index e505a1869b8f3..062a6be2b439f 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1582,6 +1582,38 @@ static function () { 'BODY' => array(), ), ), + 'no_styles_at_enqueued_block_assets' => array( + 'set_up' => static function () { + add_action( + 'wp_enqueue_scripts', + static function () { + wp_dequeue_style( 'classic-theme-styles' ); + wp_dequeue_style( 'custom-block-styles' ); + }, + 100 + ); + }, + 'inline_size_limit' => PHP_INT_MAX, + 'expected_styles' => array( + 'HEAD' => array( + 'wp-img-auto-sizes-contain-inline-css', + 'early-css', + 'early-inline-css', + 'wp-emoji-styles-inline-css', + 'wp-block-library-inline-css', + 'wp-block-separator-inline-css', + 'third-party-test-block-css', + 'global-styles-inline-css', + 'normal-css', + 'normal-inline-css', + 'wp-custom-css', + 'late-css', + 'late-inline-css', + 'core-block-supports-inline-css', + ), + 'BODY' => array(), + ), + ), 'standard_classic_theme_config_extra_block_library_inline_style' => array( 'set_up' => static function () { add_action( From d6c84c0b87acb4661329c1e4be383ab415e5a9d9 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 16 Dec 2025 13:10:33 -0800 Subject: [PATCH 07/21] Add comments --- src/wp-includes/script-loader.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 1e00fc527597c..702211c9e4811 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3747,6 +3747,7 @@ static function () use ( $placeholder ) { $printed_core_block_styles = ob_get_clean(); } + // Non-core block styles get printed after the classic-theme-styles stylesheet. $enqueued_other_block_styles = array_values( array_intersect( $all_other_block_style_handles, wp_styles()->queue ) ); if ( count( $enqueued_other_block_styles ) > 0 ) { ob_start(); @@ -3754,7 +3755,7 @@ static function () use ( $placeholder ) { $printed_other_block_styles = ob_get_clean(); } - // Capture the global-styles so that it can be printed separately after classic-theme-styles, to preserve the original order, + // Capture the global-styles so that it can be printed separately after classic-theme-styles and other styles enqueued at enqueue_block_assets, if ( wp_style_is( 'global-styles' ) ) { ob_start(); wp_styles()->do_items( array( 'global-styles' ) ); @@ -3907,10 +3908,12 @@ public function remove() { $inserted_after = $printed_core_block_styles; $printed_core_block_styles = ''; + // If the classic-theme-styles is absent, then the third-party block styles cannot be inserted after it, so they get inserted here. if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { $inserted_after .= "\n" . $printed_other_block_styles; $printed_other_block_styles = ''; + // If there aren't any other styles printed at enqueue_block_assets either, then the global styles needs to also be printed here. if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { $inserted_after .= "\n" . $printed_global_styles; $printed_global_styles = ''; @@ -3922,6 +3925,7 @@ public function remove() { } } + // Insert global-styles after the styles enqueued at enqueue_block_assets. if ( '' !== $printed_global_styles && $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { $processor->seek( 'last_style_at_enqueue_block_assets' ); @@ -3934,6 +3938,7 @@ public function remove() { } } + // Insert third-party block styles right after the classic-theme-styles. if ( '' !== $printed_other_block_styles && $processor->has_bookmark( 'classic_theme_styles' ) ) { $processor->seek( 'classic_theme_styles' ); $processor->insert_after( "\n" . $printed_other_block_styles ); From a1e1649571578dd43fee9b39998cf72903f33f2c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 16 Dec 2025 13:28:54 -0800 Subject: [PATCH 08/21] Address duplication in data provider --- tests/phpunit/tests/template.php | 240 ++++++++++++++++--------------- 1 file changed, 121 insertions(+), 119 deletions(-) diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index 062a6be2b439f..a7b8783762045 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1480,41 +1480,51 @@ public function test_wp_load_classic_theme_block_styles_on_demand( string $theme * @return array */ public function data_wp_hoist_late_printed_styles(): array { - $common_expected_head_styles = array( + $early_common_styles = array( 'wp-img-auto-sizes-contain-inline-css', 'early-css', 'early-inline-css', 'wp-emoji-styles-inline-css', + ); - // Core block styles enqueued by wp_common_block_scripts_and_styles() at which runs at wp_enqueue_scripts priority 10, added first. - 'wp-block-library-css', // Inline printed. - 'wp-block-separator-css', // Hoisted. - - // The wp_common_block_scripts_and_styles() function also fires enqueue_block_assets, at which wp_enqueue_classic_theme_styles() runs. - 'classic-theme-styles-css', // Printed at enqueue_block_assets. - - // Third-party block styles. - 'third-party-test-block-css', // Hoisted. - - // Other styles enqueued at enqueue_block_assets, which is fired by wp_common_block_scripts_and_styles(). - 'custom-block-styles-css', // Printed at enqueue_block_assets. - - // Hoisted. Enqueued by wp_enqueue_global_styles() which runs at wp_enqueue_scripts priority 10 and wp_footer priority 1. - 'global-styles-inline-css', - + $common_late_in_head = array( // Styles enqueued at wp_enqueue_scripts (priority 10). 'normal-css', 'normal-inline-css', // Styles printed at wp_head priority 10. 'wp-custom-css', + ); - // Hoisted. Styles printed in the footer. + $common_late_in_body = array( 'late-css', 'late-inline-css', 'core-block-supports-inline-css', ); + $common_expected_head_styles = array_merge( + $early_common_styles, + array( + // Core block styles enqueued by wp_common_block_scripts_and_styles() at which runs at wp_enqueue_scripts priority 10, added first. + 'wp-block-library-css', // Inline printed. + 'wp-block-separator-css', // Hoisted. + + // The wp_common_block_scripts_and_styles() function also fires enqueue_block_assets, at which wp_enqueue_classic_theme_styles() runs. + 'classic-theme-styles-css', // Printed at enqueue_block_assets. + + // Third-party block styles. + 'third-party-test-block-css', // Hoisted. + + // Other styles enqueued at enqueue_block_assets, which is fired by wp_common_block_scripts_and_styles(). + 'custom-block-styles-css', // Printed at enqueue_block_assets. + + // Hoisted. Enqueued by wp_enqueue_global_styles() which runs at wp_enqueue_scripts priority 10 and wp_footer priority 1. + 'global-styles-inline-css', + ), + $common_late_in_head, + $common_late_in_body + ); + return array( 'standard_classic_theme_config_with_min_styles_inlined' => array( 'set_up' => null, @@ -1528,23 +1538,18 @@ public function data_wp_hoist_late_printed_styles(): array { 'set_up' => null, 'inline_size_limit' => PHP_INT_MAX, 'expected_styles' => array( - 'HEAD' => array( - 'wp-img-auto-sizes-contain-inline-css', - 'early-css', - 'early-inline-css', - 'wp-emoji-styles-inline-css', - 'wp-block-library-inline-css', - 'wp-block-separator-inline-css', - 'classic-theme-styles-inline-css', - 'third-party-test-block-css', - 'custom-block-styles-css', - 'global-styles-inline-css', - 'normal-css', - 'normal-inline-css', - 'wp-custom-css', - 'late-css', - 'late-inline-css', - 'core-block-supports-inline-css', + 'HEAD' => array_merge( + $early_common_styles, + array( + 'wp-block-library-inline-css', + 'wp-block-separator-inline-css', + 'classic-theme-styles-inline-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + 'global-styles-inline-css', + ), + $common_late_in_head, + $common_late_in_body ), 'BODY' => array(), ), @@ -1562,22 +1567,17 @@ static function () { }, 'inline_size_limit' => PHP_INT_MAX, 'expected_styles' => array( - 'HEAD' => array( - 'wp-img-auto-sizes-contain-inline-css', - 'early-css', - 'early-inline-css', - 'wp-emoji-styles-inline-css', - 'wp-block-library-inline-css', - 'wp-block-separator-inline-css', - 'third-party-test-block-css', - 'custom-block-styles-css', - 'global-styles-inline-css', - 'normal-css', - 'normal-inline-css', - 'wp-custom-css', - 'late-css', - 'late-inline-css', - 'core-block-supports-inline-css', + 'HEAD' => array_merge( + $early_common_styles, + array( + 'wp-block-library-inline-css', + 'wp-block-separator-inline-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + 'global-styles-inline-css', + ), + $common_late_in_head, + $common_late_in_body ), 'BODY' => array(), ), @@ -1595,21 +1595,42 @@ static function () { }, 'inline_size_limit' => PHP_INT_MAX, 'expected_styles' => array( - 'HEAD' => array( - 'wp-img-auto-sizes-contain-inline-css', - 'early-css', - 'early-inline-css', - 'wp-emoji-styles-inline-css', - 'wp-block-library-inline-css', - 'wp-block-separator-inline-css', - 'third-party-test-block-css', - 'global-styles-inline-css', - 'normal-css', - 'normal-inline-css', - 'wp-custom-css', - 'late-css', - 'late-inline-css', - 'core-block-supports-inline-css', + 'HEAD' => array_merge( + $early_common_styles, + array( + 'wp-block-library-inline-css', + 'wp-block-separator-inline-css', + 'third-party-test-block-css', + 'global-styles-inline-css', + ), + $common_late_in_head, + $common_late_in_body + ), + 'BODY' => array(), + ), + ), + 'no_global_styles' => array( + 'set_up' => static function () { + add_filter( + 'print_styles_array', + static function ( $handles ) { + return array_values( array_diff( $handles, array( 'global-styles' ) ) ); + } + ); + }, + 'inline_size_limit' => PHP_INT_MAX, + 'expected_styles' => array( + 'HEAD' => array_merge( + $early_common_styles, + array( + 'wp-block-library-inline-css', + 'wp-block-separator-inline-css', + 'classic-theme-styles-inline-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + ), + $common_late_in_head, + $common_late_in_body ), 'BODY' => array(), ), @@ -1641,25 +1662,18 @@ static function () { }, 'inline_size_limit' => 0, 'expected_styles' => array( - 'HEAD' => array( - 'wp-img-auto-sizes-contain-inline-css', - 'early-css', - 'early-inline-css', - 'wp-emoji-styles-inline-css', - 'wp-block-library-css', - 'classic-theme-styles-css', - 'third-party-test-block-css', - 'custom-block-styles-css', - 'global-styles-inline-css', - 'normal-css', - 'normal-inline-css', - 'wp-custom-css', - ), - 'BODY' => array( - 'late-css', - 'late-inline-css', - 'core-block-supports-inline-css', + 'HEAD' => array_merge( + $early_common_styles, + array( + 'wp-block-library-css', + 'classic-theme-styles-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + 'global-styles-inline-css', + ), + $common_late_in_head ), + 'BODY' => $common_late_in_body, ), ), '_wp_footer_scripts_removed' => array( @@ -1706,24 +1720,17 @@ function (): void { }, 'inline_size_limit' => 0, 'expected_styles' => array( - 'HEAD' => array( - 'wp-img-auto-sizes-contain-inline-css', - 'early-css', - 'early-inline-css', - 'wp-emoji-styles-inline-css', - 'classic-theme-styles-css', - 'third-party-test-block-css', - 'custom-block-styles-css', - 'global-styles-inline-css', - 'normal-css', - 'normal-inline-css', - 'wp-custom-css', - ), - 'BODY' => array( - 'late-css', - 'late-inline-css', - 'core-block-supports-inline-css', + 'HEAD' => array_merge( + $early_common_styles, + array( + 'classic-theme-styles-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + 'global-styles-inline-css', + ), + $common_late_in_head ), + 'BODY' => $common_late_in_body, ), ), 'override_block_library_inline_style_late' => array( @@ -1738,24 +1745,19 @@ function (): void { }, 'inline_size_limit' => 0, 'expected_styles' => array( - 'HEAD' => array( - 'wp-img-auto-sizes-contain-inline-css', - 'early-css', - 'early-inline-css', - 'wp-emoji-styles-inline-css', - 'wp-block-library-css', - 'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text. - 'wp-block-separator-css', - 'classic-theme-styles-css', - 'third-party-test-block-css', - 'custom-block-styles-css', - 'global-styles-inline-css', - 'normal-css', - 'normal-inline-css', - 'wp-custom-css', - 'late-css', - 'late-inline-css', - 'core-block-supports-inline-css', + 'HEAD' => array_merge( + $early_common_styles, + array( + 'wp-block-library-css', + 'wp-block-library-inline-css', // This contains the "OVERRIDDEN" text. + 'wp-block-separator-css', + 'classic-theme-styles-css', + 'third-party-test-block-css', + 'custom-block-styles-css', + 'global-styles-inline-css', + ), + $common_late_in_head, + $common_late_in_body ), 'BODY' => array(), ), From fac7eb931bf3daeb200d13c8a2305d5f20815ea1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 18:33:43 -0800 Subject: [PATCH 09/21] Fix typo in comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 29416c58cafa9..9b6c68db603e0 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3657,7 +3657,7 @@ static function () use ( $placeholder ) { } /* - * First print all styles related to blocks which should inserted right after the wp-block-library stylesheet + * First print all styles related to blocks which should be inserted right after the wp-block-library stylesheet * to preserve the CSS cascade. The logic in this `if` statement is derived from `wp_print_styles()`. */ $enqueued_core_block_styles = array_values( array_intersect( $all_core_block_style_handles, wp_styles()->queue ) ); From a5c51416a2f9e30daf9f2115163815e0ce30d5b6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 18:34:14 -0800 Subject: [PATCH 10/21] Remove extraneous word in comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/phpunit/tests/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index b4017ac8b06c0..823027decc0c0 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1806,7 +1806,7 @@ static function () { ); /* - * This is very old guidance about how to add enqueue styles for blocks. Certain themes still enqueue block + * This is very old guidance about how to enqueue styles for blocks. Certain themes still enqueue block * styles using this action. */ add_action( From d92b17f988158eb7ef39ec8254defc18f3322f3d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 18:34:39 -0800 Subject: [PATCH 11/21] Improve phrasing Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 9b6c68db603e0..e9070282d0b70 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3598,7 +3598,7 @@ function wp_hoist_late_printed_styles() { return; } - // Capture the styles enqueued at the enqueue_block_assets action, so that non-core block styles and global styles can be inserted after at hoisting. + // 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. $style_handles_at_enqueue_block_assets = array(); add_action( 'enqueue_block_assets', From 2dd558fe1afb072848aa617464c7344eefcbc7aa Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 18:35:17 -0800 Subject: [PATCH 12/21] Did subject-verb agreement Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index e9070282d0b70..2854c303891ab 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3833,7 +3833,7 @@ public function remove() { $inserted_after .= "\n" . $printed_other_block_styles; $printed_other_block_styles = ''; - // If there aren't any other styles printed at enqueue_block_assets either, then the global styles needs to also be printed here. + // If there aren't any other styles printed at enqueue_block_assets either, then the global styles need to also be printed here. if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { $inserted_after .= "\n" . $printed_global_styles; $printed_global_styles = ''; From 1ea3fa1b63f954309502b9acd7c93b56f862a04e Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 18:35:38 -0800 Subject: [PATCH 13/21] End comments in period not comma Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 2854c303891ab..a4827600a3b70 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3675,7 +3675,7 @@ static function () use ( $placeholder ) { $printed_other_block_styles = ob_get_clean(); } - // Capture the global-styles so that it can be printed separately after classic-theme-styles and other styles enqueued at enqueue_block_assets, + // Capture the global-styles so that it can be printed separately after classic-theme-styles and other styles enqueued at enqueue_block_assets. if ( wp_style_is( 'global-styles' ) ) { ob_start(); wp_styles()->do_items( array( 'global-styles' ) ); From 07b53d74ad2a4bd50ab071591f4abfdcfd992b42 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 20:49:45 -0800 Subject: [PATCH 14/21] Add missing ticket reference --- tests/phpunit/tests/template.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index 823027decc0c0..ee284113cbccd 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1769,6 +1769,7 @@ function (): void { * Tests that wp_hoist_late_printed_styles() adds a placeholder for delayed CSS, then removes it and adds all CSS to the head including late enqueued styles. * * @ticket 64099 + * @ticket 64354 * @covers ::wp_load_classic_theme_block_styles_on_demand * @covers ::wp_hoist_late_printed_styles * From eb33c466b3819d17319aa9896d4be2f845e8be0d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 20:50:57 -0800 Subject: [PATCH 15/21] Avoid adding extraneous newlines for empty stylesheets Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index a4827600a3b70..31616a229a44f 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3830,12 +3830,16 @@ public function remove() { // If the classic-theme-styles is absent, then the third-party block styles cannot be inserted after it, so they get inserted here. if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { - $inserted_after .= "\n" . $printed_other_block_styles; + if ( '' !== $printed_other_block_styles ) { + $inserted_after .= "\n" . $printed_other_block_styles; + } $printed_other_block_styles = ''; // If there aren't any other styles printed at enqueue_block_assets either, then the global styles need to also be printed here. if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { - $inserted_after .= "\n" . $printed_global_styles; + if ( '' !== $printed_global_styles ) { + $inserted_after .= "\n" . $printed_global_styles; + } $printed_global_styles = ''; } } From 05d10144bf16801c2b7f07e311a2d71047e14a22 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 22:20:57 -0800 Subject: [PATCH 16/21] Fix grammatical issue in comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 31616a229a44f..6c6b68873fb75 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3616,7 +3616,7 @@ static function () use ( &$style_handles_at_enqueue_block_assets ) { ); /* - * Add a placeholder comment into the inline styles for wp-block-library, after which where the late block styles + * Add a placeholder comment into the inline styles for wp-block-library, after which the late block styles * can be hoisted from the footer to be printed in the header by means of a filter below on the template enhancement * output buffer. The `wp_print_styles` action is used to ensure that if the inline style gets replaced at * `enqueue_block_assets` or `wp_enqueue_scripts` that the placeholder will be sure to be present. From 66e08333a817f5589729f7b57f4eab806c154656 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 22:21:40 -0800 Subject: [PATCH 17/21] Fix parenthesis position in comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 6c6b68873fb75..1731a57a351d5 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3816,7 +3816,7 @@ public function remove() { * A placeholder CSS comment is added to the inline style in order to force an inline STYLE tag to * be printed. Now that we've located the inline style, the placeholder comment can be removed. If * there is no CSS left in the STYLE tag after removing the placeholder (aside from the sourceURL - * comment, then remove the STYLE entirely.) + * comment), then remove the STYLE entirely. */ $css_text = str_replace( $placeholder, '', $css_text ); if ( preg_match( ':^/\*# sourceURL=\S+? \*/$:', trim( $css_text ) ) ) { From 664487fae40de607343a93905a400acbc5dc495d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 22:22:18 -0800 Subject: [PATCH 18/21] Fix grammar in comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/phpunit/tests/template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index ee284113cbccd..da18191b0983e 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1505,7 +1505,7 @@ public function data_wp_hoist_late_printed_styles(): array { $common_expected_head_styles = array_merge( $early_common_styles, array( - // Core block styles enqueued by wp_common_block_scripts_and_styles() at which runs at wp_enqueue_scripts priority 10, added first. + // Core block styles enqueued by wp_common_block_scripts_and_styles(), which runs at wp_enqueue_scripts priority 10, added first. 'wp-block-library-css', // Inline printed. 'wp-block-separator-css', // Hoisted. From dd4bb104f3d447d4defa2945e31e3018bd75c8cb Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 22:24:43 -0800 Subject: [PATCH 19/21] Avoid printing extra newline in styles in another case Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 1731a57a351d5..81af6aba161e2 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3856,7 +3856,7 @@ public function remove() { $processor->insert_after( "\n" . $printed_global_styles ); $printed_global_styles = ''; - if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { + if ( ! $processor->has_bookmark( 'classic_theme_styles' ) && '' !== $printed_other_block_styles ) { $processor->insert_after( "\n" . $printed_other_block_styles ); $printed_other_block_styles = ''; } From 8d3b852e2f87872364280cdceb6d5356e4c2326a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 11 Jan 2026 22:45:49 -0800 Subject: [PATCH 20/21] Avoid printing yet more newlines Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/script-loader.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 81af6aba161e2..c7cbc9dbfbec7 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3831,14 +3831,20 @@ public function remove() { // If the classic-theme-styles is absent, then the third-party block styles cannot be inserted after it, so they get inserted here. if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { if ( '' !== $printed_other_block_styles ) { - $inserted_after .= "\n" . $printed_other_block_styles; + if ( '' !== $inserted_after ) { + $inserted_after .= "\n"; + } + $inserted_after .= $printed_other_block_styles; } $printed_other_block_styles = ''; // If there aren't any other styles printed at enqueue_block_assets either, then the global styles need to also be printed here. if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { if ( '' !== $printed_global_styles ) { - $inserted_after .= "\n" . $printed_global_styles; + if ( '' !== $inserted_after ) { + $inserted_after .= "\n"; + } + $inserted_after .= $printed_global_styles; } $printed_global_styles = ''; } From 26154b81960de22e2f97c91972050fd4caed314b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 14 Jan 2026 14:30:39 -0800 Subject: [PATCH 21/21] Remove insertion of extra newlines Unprops Copilot (in part) --- src/wp-includes/script-loader.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 533804e2d1976..7536ce34aae06 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -3837,9 +3837,6 @@ public function remove() { // If the classic-theme-styles is absent, then the third-party block styles cannot be inserted after it, so they get inserted here. if ( ! $processor->has_bookmark( 'classic_theme_styles' ) ) { if ( '' !== $printed_other_block_styles ) { - if ( '' !== $inserted_after ) { - $inserted_after .= "\n"; - } $inserted_after .= $printed_other_block_styles; } $printed_other_block_styles = ''; @@ -3847,18 +3844,11 @@ public function remove() { // If there aren't any other styles printed at enqueue_block_assets either, then the global styles need to also be printed here. if ( ! $processor->has_bookmark( 'last_style_at_enqueue_block_assets' ) ) { if ( '' !== $printed_global_styles ) { - if ( '' !== $inserted_after ) { - $inserted_after .= "\n"; - } $inserted_after .= $printed_global_styles; } $printed_global_styles = ''; } } - - if ( '' !== $inserted_after ) { - $processor->insert_after( "\n" . $inserted_after ); - } } // Insert global-styles after the styles enqueued at enqueue_block_assets.