Skip to content

Script Loader: Restore init hook so block theme styles load on demand in classic themes - #11887

Open
itzmekhokan wants to merge 6 commits into
WordPress:trunkfrom
itzmekhokan:fix/65272
Open

Script Loader: Restore init hook so block theme styles load on demand in classic themes#11887
itzmekhokan wants to merge 6 commits into
WordPress:trunkfrom
itzmekhokan:fix/65272

Conversation

@itzmekhokan

@itzmekhokan itzmekhokan commented May 20, 2026

Copy link
Copy Markdown

What

Restores the init priority 8 hook for wp_load_classic_theme_block_styles_on_demand() while keeping the wp_default_styles priority 0 hook added in [61981]. Adds guards so filters and actions are not registered twice when the function runs from both hooks.

Why

In WordPress 7.0, commit [61981] moved wp_load_classic_theme_block_styles_on_demand() from init priority 8 to wp_default_styles priority 0 only. That introduced a regression (#65272):

  1. register_core_block_style_handles() runs at init priority 9.
  2. It checks wp_should_load_separate_core_block_assets() before calling wp_styles().
  3. With only the wp_default_styles hook, those filters are not added until WP_Styles is first constructed.
  4. register_core_block_style_handles() returns early, so per-block opinionated styles (blocks/*/theme.css, e.g. wp-block-quote-theme) are never registered or enqueued on the frontend.

Symptom: In classic themes with wp-block-styles support (e.g. Twenty Fifteen), Quote block right alignment works in the editor but the border stays on the left on the frontend because theme.css never loads.

The wp_default_styles hook remains so filters are still present when WP_Styles is constructed before init priority 9 (#64846).

How

  • Re-add add_action( 'init', 'wp_load_classic_theme_block_styles_on_demand', 8 ) in default-filters.php.
  • Keep add_action( 'wp_default_styles', 'wp_load_classic_theme_block_styles_on_demand', 0 ).
  • Use has_filter() / has_action() before adding hooks to avoid duplicate registration.
  • Add regression test: Tests_Blocks_ClassicThemeBlockStylesOnDemand::test_register_core_block_style_handles_without_prior_wp_styles.

Trac ticket: https://core.trac.wordpress.org/ticket/65272

Use of AI Tools

AI assistance: Yes
Tool(s): Claude


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

…mand.

Register on-demand block asset filters at init priority 8 again so
register_core_block_style_handles() can opt in before WP_Styles is
constructed. Keep the wp_default_styles hook from [61981] for early
WP_Styles instantiation and guard against duplicate hook registration.

Fixes #65272.
@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props khokansardar, wildworks, westonruter.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@westonruter

Copy link
Copy Markdown
Member

Does this cause a regression in what r61981 was committed to fix Core-64846?

Comment thread src/wp-includes/script-loader.php Outdated
* wish to stream responses can more easily turn this off.
*/
add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true', 0 );
if ( ! has_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true' ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem it will work properly. The has_filter() function returns false|int, with the int being the priority. Since the priority 0 is falsey, this will still end up adding the filter multiple times. So you'd need to instead do:

Suggested change
if ( ! has_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true' ) ) {
if ( false === has_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true' ) ) {

Comment thread src/wp-includes/script-loader.php Outdated
* separate block styles, then abort.
*/
add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
if ( ! has_filter( 'should_load_separate_core_block_assets', '__return_true' ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( ! has_filter( 'should_load_separate_core_block_assets', '__return_true' ) ) {
if ( false === has_filter( 'should_load_separate_core_block_assets', '__return_true' ) ) {

Comment thread src/wp-includes/script-loader.php Outdated
* has explicitly opted out of loading block styles on demand, then abort.
*/
add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
if ( ! has_filter( 'should_load_block_assets_on_demand', '__return_true' ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( ! has_filter( 'should_load_block_assets_on_demand', '__return_true' ) ) {
if ( false === has_filter( 'should_load_block_assets_on_demand', '__return_true' ) ) {

Comment thread src/wp-includes/script-loader.php Outdated

// Add hooks which require the presence of the output buffer. Ideally the above two filters could be added here, but they run too early.
add_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' );
if ( ! has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( ! has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ) ) {
if ( false === has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ) ) {

…hooks.

has_filter() and has_action() return the priority when a callback is
registered, so priority 0 must be compared with false === instead of !.

Adds regression tests for duplicate hook registration and for #64846
when WP_Styles is constructed before init.

Props westonruter.
Fixes #65272.
@itzmekhokan

Copy link
Copy Markdown
Author

Does this cause a regression in what r61981 was committed to fix Core-64846?

Thanks @westonruter — good catch on has_filter() / has_action() returning priority 0, which is falsy. Updated all four checks to use false ===.

Re #64846: this should not regress r61981 because the wp_default_styles priority 0 hook is retained. That hook ensures filters are present when WP_Styles is constructed before init priority 9, so wp-block-library still registers as common.css.

Copilot AI review requested due to automatic review settings July 28, 2026 07:22
Comment thread src/wp-includes/script-loader.php Outdated
*
* @since 6.9.0
* @since 7.0.0 This is now invoked at the `wp_default_styles` action with priority 0 instead of at `init` with priority 8.
* @since 7.0.0 This is also invoked at the `wp_default_styles` action with priority 0 so that filters are present when

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @since 7.0.0 This is also invoked at the `wp_default_styles` action with priority 0 so that filters are present when
* @since 7.1.0 This is also invoked at the `wp_default_styles` action with priority 0 so that filters are present when

If this PR can make it into the 7.1 release.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores wp_load_classic_theme_block_styles_on_demand() on init (priority 8) while keeping the wp_default_styles (priority 0) invocation, addressing a regression where classic themes may fail to register/enqueue per-block “theme.css” styles when WP_Styles has not yet been constructed.

Changes:

  • Re-adds init (priority 8) hook for wp_load_classic_theme_block_styles_on_demand() alongside the existing wp_default_styles (priority 0) hook.
  • Adds duplicate-registration guards inside wp_load_classic_theme_block_styles_on_demand() when invoked from both hooks.
  • Adds PHPUnit regression coverage for the “no prior wp_styles() before init” scenario and for duplicate-hook prevention.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
tests/phpunit/tests/blocks/classicThemeBlockStylesOnDemand.php Adds regression tests for classic-theme block style handle registration timing and for duplicate hook registration prevention.
src/wp-includes/script-loader.php Updates wp_load_classic_theme_block_styles_on_demand() docs and adds guards to avoid registering filters/actions twice when called from multiple hooks.
src/wp-includes/default-filters.php Restores the init priority 8 hook while retaining the wp_default_styles priority 0 hook.
Comments suppressed due to low confidence (3)

src/wp-includes/script-loader.php:3720

  • Same as above: false === has_filter() will also skip adding __return_true at priority 0 if it exists at a non-zero priority already. If priority 0 is required (per the inline comment), gate on 0 !== has_filter() so the callback is guaranteed to exist at priority 0.
	if ( false === has_filter( 'should_load_separate_core_block_assets', '__return_true' ) ) {
		add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
	}

src/wp-includes/script-loader.php:3732

  • Same guard issue here: to ensure __return_true is present at priority 0 (not merely present somewhere), compare has_filter() to 0 rather than false.
	if ( false === has_filter( 'should_load_block_assets_on_demand', '__return_true' ) ) {
		add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
	}

src/wp-includes/script-loader.php:3740

  • Similarly for actions: false === has_action() will skip adding at the default priority (10) if the same callback was added at any other priority. If the intent is to ensure it exists at priority 10, compare against 10 instead.
	if ( false === has_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' ) ) {
		add_action( 'wp_template_enhancement_output_buffer_started', 'wp_hoist_late_printed_styles' );
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to +30
/**
* @var WP_Styles|null
*/
protected $original_wp_styles;

public function set_up() {
parent::set_up();

global $wp_styles;
$this->original_wp_styles = $wp_styles;
}

public function tear_down() {
global $wp_styles;
$wp_styles = $this->original_wp_styles;

parent::tear_down();
}
Comment on lines +3701 to +3703
if ( false === has_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true' ) ) {
add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true', 0 );
}
…` entry.

Adds a separate `@since 7.1.0` entry to `wp_load_classic_theme_block_styles_on_demand()`
noting that the `init` action at priority 8 is restored alongside the `wp_default_styles`
action at priority 0, and restores the original `@since 7.0.0` line so the history of the
7.0 move to `wp_default_styles` is preserved.

Follow-up to [61981].

Props t-hamano.
See #65272.
Copilot AI review requested due to automatic review settings July 29, 2026 03:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

src/wp-includes/script-loader.php:3704

  • The duplicate-hook guard for this filter only checks for the callback’s presence anywhere. If a plugin/theme has already added __return_true at a non-zero priority, this will skip adding the priority-0 default, which can change override ordering compared to the documented intent (“do so at priority zero…”). Consider checking specifically for priority 0 instead of strict false.
	if ( false === has_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true' ) ) {
		add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true', 0 );
	}

src/wp-includes/script-loader.php:3721

  • Same issue as above: using false === has_filter() will skip adding the priority-0 default if __return_true is already present at another priority, which can alter opt-out behavior/order. Prefer a priority-0 check (or always add at 0) to preserve the intended default.
	if ( false === has_filter( 'should_load_separate_core_block_assets', '__return_true' ) ) {
		add_filter( 'should_load_separate_core_block_assets', '__return_true', 0 );
	}

src/wp-includes/script-loader.php:3733

  • Same as the other guards: this currently skips adding the priority-0 default when __return_true is already present at a different priority, which can change how later overrides behave. Checking for priority 0 keeps the function’s documented priority semantics intact.
	if ( false === has_filter( 'should_load_block_assets_on_demand', '__return_true' ) ) {
		add_filter( 'should_load_block_assets_on_demand', '__return_true', 0 );
	}

tests/phpunit/tests/blocks/classicThemeBlockStylesOnDemand.php:30

  • The tests add wp-block-styles theme support but never remove it, which can leak global theme-support state into subsequent tests. Clean it up in tear_down() (similar to other dependency/theme-support tests).
	public function tear_down() {
		global $wp_styles;
		$wp_styles = $this->original_wp_styles;

		parent::tear_down();
	}

tests/phpunit/tests/blocks/classicThemeBlockStylesOnDemand.php:23

  • These tests assume a classic (non-block) theme; wp_load_classic_theme_block_styles_on_demand() bails early for block themes. To keep the tests isolated from other tests that may have switched themes, set the theme explicitly in set_up().
	public function set_up() {
		parent::set_up();

		global $wp_styles;
		$this->original_wp_styles = $wp_styles;
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants