Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure base global styles are loaded before block styles. #5892

Closed
6 changes: 5 additions & 1 deletion src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,13 @@
add_filter( 'block_editor_settings_all', 'wp_add_editor_classic_theme_styles' );

// Global styles can be enqueued in both the header and the footer. See https://core.trac.wordpress.org/ticket/53494.
add_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles' );
add_action( 'init', 'wp_enqueue_global_styles', 1 );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed this to init as it seems to be the only way to get it to run before the core block styles are registered

Copy link
Member

@oandregal oandregal Jan 18, 2024

Choose a reason for hiding this comment

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

cc @scruffian @ajlende @aristath as they may have the most experience with all the scenarios, IIRC.

I couldn't experiment/test this change because my wordpress-develop env wouldn't start up, so I'm trying to recall from memory / skimming through the code. There are two scenarios: the core blocks styles are loaded with a separate stylesheet or bundled as part of the block library.

As I understand it, the order of styles after this change would be

Single stylesheet

Before:

  1. wp-block-library-css
  2. global-styles-inline-css (both top-level & block-level CSS)
  3. theme-style-css

After

  1. global-styles-inline-css (both top-level & block-level CSS)
  2. wp-block-library-css
  3. theme-style-css

Separate stylesheets

Before:

  1. wp-block-blockname-inline-css (the block styles + the block-level CSS coming from global styles)
  2. wp-block-library-inline-css
  3. global-styles-inline-css (only top-level CSS)
  4. core-block-supports-inline-css

After:

  1. global-styles-inline-css (only top-level CSS)
  2. wp-block-blockname-inline-css (the block styles + the block-level CSS coming from global styles)
  3. wp-block-library-inline-css
  4. core-block-supports-inline-css

Is this the goal / what happens?


Notes:

  • There are some block styles that are absorbed as part of the global styles. I've found some for navigation and pullquote. These are enqueued where the block-level global styles are. I presume block registration and block styles registration happen at different steps of the lifecycle. I mention it because block registration is essential for theme.json/global styles generation, but block styles registration does not affect it.
  • In testing how it works now using a different env, I've noticed that even in the case of "separate stylesheets", the global-styles-inline-css stylesheet has a lot of block styles (tested with TwentyTwentyFour). I may be a bit rusty on how this all is supposed to work, but it sounds like a bug to me. I've found styles for .wp-block-calendar, .wp-block-categories, .wp-block-post-comments-form, .wp-block-loginout, .wp-block-post-terms, .wp-block-query-title, .wp-block-quote, .wp-block-search, .wp-block-separator. Some of them do not have style rules, for example, .wp-block-separator {}.

Copy link
Contributor

@azaozz azaozz Jan 18, 2024

Choose a reason for hiding this comment

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

The change LGTM. Of course "global" styles should always be loaded before "local" styles :)
Was just wondering (nitpick) if using wp_enqueue_scripts priority 1 would be sufficient. As we are enqueueing stylesheets using the appropriate hook would be better for "educational purposes" :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for looking into this!

Was just wondering (nitpick) if using wp_enqueue_scripts priority 1 would be sufficient

I tried and it didn't work, I think because wp_enqueue_scripts always runs after init.

@oandregal the separate stylesheets scenario you outline is correct; I'll need to double-check the single stylesheet but I think it's also right.

Copy link
Member

@oandregal oandregal Jan 19, 2024

Choose a reason for hiding this comment

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

The change LGTM. Of course "global" styles should always be loaded before "local" styles :)

This is incorrect, it lacks a bit of nuance. Styles is a very loaded domain, the same words mean different things to people. It's important to me that we all share the same context, and clarify what we mean.

This is how it works now:

  1. block-level styles provided by the block (the block stylesheet)
  2. global styles (top or block-level) provided by core/blocks/theme/user (different theme.json files & user via global styles sidebar)
  3. block-level styles provided by the user (what we should call "local", these are serialized in the post as classes/style, and they should have the maximum priority)

My understanding is that we want to do this instead:

  1. global styles (top level) provided by core/blocks/theme/user
  2. block-level styles provided by the block (the block stylesheet)
  3. global styles (block-level) provided by core/blocks/theme/user
  4. block-level styles provided by the user

The top-level global styles should be: presets + some styles that are attached to the body + (block style variations?).

I mentioned above that I see still some "block styles" loaded in the "separate stylesheet" scenario (I understand this is a bug): with this change, they'll be enqueued in 1, and can create conflicts because they should be enqueued in 3.

Copy link
Contributor

@azaozz azaozz Jan 19, 2024

Choose a reason for hiding this comment

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

it lacks a bit of nuance. Styles is a very loaded domain, the same words mean different things to people

Yeaaa, can see that too :)

In my mind "global styles" means styles that apply to all similar elements, for example blocks, or image blocks, or paragraph blocks, etc. "Local styles" means styles that apply to only one of many elements. For example only to one image block when there are 5 on the page, etc.

Thinking it would be great to add some explanations in comments at the top of all CSS files in an attempt to get everybody on the same page. Can also be added in docblocks where the CSS is outputted from JS or PHP.

Another idea might be to drop the "global" and "local" terms as inadequate and use something a bit more granular by specificity. Perhaps "Top level" "Level 1", "Level 2", etc.

Copy link
Member

Choose a reason for hiding this comment

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

For reference, this is the documentation we have for styles (naming, how they work, etc.).

Copy link
Contributor Author

@tellthemachines tellthemachines Jan 22, 2024

Choose a reason for hiding this comment

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

OK I looked at this carefully and can confirm the following order of styles:

Single stylesheet

trunk

  • wp-block-library-css is loaded as an external stylesheet
  • wp-block-library-inline-css
  • global-styles-inline-css containing base global styles followed by block-specific global styles
  • core-block-supports-inline-css

this PR

  • global-styles-inline-css containing base global styles followed by block-specific global styles
  • wp-block-library-css is loaded as an external stylesheet
  • wp-block-library-inline-css
  • core-block-supports-inline-css

Separate stylesheets

trunk

  • wp-block-[blockname]-inline-css containing both the block library styles for that block and the block-specific global styles
  • in between the block style tags, external stylesheets are loaded for some blocks (e.g. I've seen Navigation, Image and Button, seems they change depending on the combination of blocks on the page)
  • wp-block-library-inline-css (sometimes this is replaced by a block library "common" external stylesheet, no idea why)
  • global-styles-inline-css containing only base global styles
  • core-block-supports-inline-css

this PR

  • global-styles-inline-css containing only base global styles
  • in between style tags, external stylesheets for some blocks
  • wp-block-library-inline-css (sometimes this is replaced by a block library "common" external stylesheet, no idea why)
  • core-block-supports-inline-css

So your assessment is essentially correct @oandregal.

I think the only suboptimal change in this PR is in the single stylesheet scenario, where block library CSS is now loaded after block-specific global styles. It should be the other way around, but ideally base global styles would also be added before block library styles. I'll look into splitting base and block-specific global styles into separate style tags as a workaround.

There are some block styles that are absorbed as part of the global styles. I've found some for navigation and pullquote.

I'm probably missing something, but I don't see those styles appearing at all either on trunk or this PR branch. Tested by adding a Pullquote block to a page and checking that a line-height of 1.6 doesn't appear anywhere in the styles cascade.

I've noticed that even in the case of "separate stylesheets", the global-styles-inline-css stylesheet has a lot of block styles

Those extra block styles added to global-styles-inline-css are coming from per-block custom css defined in the theme's theme.json, e.g. here. TT4 has a bunch of instances.

with this change, they'll be enqueued in 1, and can create conflicts because they should be enqueued in 3

Good point. You mean they should be enqueued after the block library styles, right? I'll look into it.

Copy link
Member

Choose a reason for hiding this comment

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

Those extra block styles added to global-styles-inline-css are coming from per-block custom css defined in the theme's theme.json, e.g. here. TT4 has a bunch of instances.

Oh, thanks for sharing. I think this is a bug: block styles should always be together. Why styles.block/search.css should be processed differently than styles.block/search.typography?

add_action( 'wp_footer', 'wp_enqueue_global_styles', 1 );

add_action( 'wp_enqueue_scripts', 'wp_add_global_styles_for_blocks' );
Copy link
Member

@oandregal oandregal Jan 18, 2024

Choose a reason for hiding this comment

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

Doesn't this add the block-level global styles twice to the global-styles-inline-css stylesheet when there's a single stylesheet for blocks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh that's a good point, I'll have to test that scenario better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just confirmed and there's no duplication of block-level global styles in the single stylesheet scenario. I removed it from where it was called inside wp_enqueue_global_styles in the script loader, so it's only getting called here, independently of the stylesheet setting.


add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_global_styles' );

// Global styles custom CSS.
add_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles_custom_css' );

Expand Down
11 changes: 5 additions & 6 deletions src/wp-includes/global-styles-and-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function wp_get_global_settings( $path = array(), $context = array() ) {
* is always fresh from the potential modifications done via hooks
* that can use dynamic data (modify the stylesheet depending on some option,
* settings depending on user permissions, etc.).
* See some of the existing hooks to modify theme.json behaviour:
* See some of the existing hooks to modify theme.json behavior:
* https://make.wordpress.org/core/2022/10/10/filters-for-theme-json-data/
*
* A different alternative considered was to invalidate the cache upon certain
Expand Down Expand Up @@ -300,16 +300,15 @@ function wp_get_global_styles_custom_css() {
* @since 6.1.0
*/
function wp_add_global_styles_for_blocks() {
if ( ! wp_should_load_separate_core_block_assets() ) {
return;
}

$tree = WP_Theme_JSON_Resolver::get_merged_data();
$block_nodes = $tree->get_styles_block_nodes();
foreach ( $block_nodes as $metadata ) {
$block_css = $tree->get_styles_for_block( $metadata );

if ( ! wp_should_load_separate_core_block_assets() ) {
wp_add_inline_style( 'global-styles', $block_css );
continue;
}

$stylesheet_handle = 'global-styles';
if ( isset( $metadata['name'] ) ) {
/*
Expand Down
36 changes: 27 additions & 9 deletions src/wp-includes/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2478,6 +2478,10 @@ static function ( $node ) {
* @since 5.8.0
*/
function wp_enqueue_global_styles() {
if ( is_admin() ) {
return;
}

$separate_assets = wp_should_load_separate_core_block_assets();
$is_block_theme = wp_is_block_theme();
$is_classic_theme = ! $is_block_theme;
Expand All @@ -2496,13 +2500,6 @@ function wp_enqueue_global_styles() {
return;
}

/*
* If loading the CSS for each block separately, then load the theme.json CSS conditionally.
* This removes the CSS from the global-styles stylesheet and adds it to the inline CSS for each block.
* This filter must be registered before calling wp_get_global_stylesheet();
*/
add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );

$stylesheet = wp_get_global_stylesheet();

if ( empty( $stylesheet ) ) {
Expand All @@ -2512,9 +2509,29 @@ function wp_enqueue_global_styles() {
wp_register_style( 'global-styles', false );
wp_add_inline_style( 'global-styles', $stylesheet );
wp_enqueue_style( 'global-styles' );
}

/**
* Enqueues block global styles when separate core block assets are disabled.
*
* @since 6.5.0
*/
function wp_enqueue_block_global_styles() {
if ( wp_should_load_separate_core_block_assets() ) {
return;
}

// Add each block as an inline css.
wp_add_global_styles_for_blocks();
$tree = WP_Theme_JSON_Resolver::get_merged_data();
$block_nodes = $tree->get_styles_block_nodes();

wp_register_style( 'global-styles-blocks', false );

foreach ( $block_nodes as $metadata ) {
$block_css = $tree->get_styles_for_block( $metadata );
wp_add_inline_style( 'global-styles-blocks', $block_css );
}

wp_enqueue_style( 'global-styles-blocks' );
}

/**
Expand All @@ -2538,6 +2555,7 @@ function wp_enqueue_global_styles_custom_css() {
}
}


/**
* Checks if the editor scripts and styles for all registered block types
* should be enqueued on the current screen.
Expand Down
67 changes: 3 additions & 64 deletions tests/phpunit/tests/theme/wpAddGlobalStylesForBlocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Tests_Theme_WpAddGlobalStylesForBlocks extends WP_Theme_UnitTestCase {
public function set_up() {
parent::set_up();
remove_action( 'wp_print_styles', 'print_emoji_styles' );
add_filter( 'should_load_separate_core_block_assets', '__return_true' );
}

public function tear_down() {
Expand All @@ -32,6 +33,8 @@ public function tear_down() {
$this->test_blocks = array();
}

remove_filter( 'should_load_separate_core_block_assets', '__return_true' );

parent::tear_down();
}

Expand All @@ -50,35 +53,11 @@ public function test_third_party_blocks_inline_styles_not_register_to_global_sty
);
}

/**
* @ticket 56915
*/
public function test_third_party_blocks_inline_styles_get_registered_to_global_styles() {
$this->set_up_third_party_block();

wp_register_style( 'global-styles', false, array(), true, true );

$this->assertNotContains(
'.wp-block-my-third-party-block{background-color: hotpink;}',
$this->get_global_styles(),
'Third party block inline style should not be registered before running wp_add_global_styles_for_blocks()'
);

wp_add_global_styles_for_blocks();

$this->assertContains(
'.wp-block-my-third-party-block{background-color: hotpink;}',
$this->get_global_styles(),
'Third party block inline style should be registered after running wp_add_global_styles_for_blocks()'
);
}

/**
* @ticket 56915
*/
public function test_third_party_blocks_inline_styles_get_registered_to_global_styles_when_per_block() {
$this->set_up_third_party_block();
add_filter( 'should_load_separate_core_block_assets', '__return_true' );

wp_register_style( 'global-styles', false, array(), true, true );

Expand All @@ -102,7 +81,6 @@ public function test_third_party_blocks_inline_styles_get_registered_to_global_s
*/
public function test_third_party_blocks_inline_styles_get_rendered_when_per_block() {
$this->set_up_third_party_block();
add_filter( 'should_load_separate_core_block_assets', '__return_true' );

wp_register_style( 'global-styles', false, array(), true, true );
wp_enqueue_style( 'global-styles' );
Expand All @@ -122,34 +100,11 @@ public function test_third_party_blocks_inline_styles_get_rendered_when_per_bloc
);
}

/**
* @ticket 56915
*/
public function test_blocks_inline_styles_get_rendered() {
wp_register_style( 'global-styles', false, array(), true, true );
wp_enqueue_style( 'global-styles' );
wp_add_global_styles_for_blocks();

$actual = get_echo( 'wp_print_styles' );

$this->assertStringContainsString(
'.wp-block-my-third-party-block{background-color: hotpink;}',
$actual,
'Third party block inline style should render'
);
$this->assertStringContainsString(
'.wp-block-post-featured-image',
$actual,
'Core block should render'
);
}

/**
* @ticket 57868
*/
public function test_third_party_blocks_inline_styles_for_elements_get_rendered_when_per_block() {
$this->set_up_third_party_block();
add_filter( 'should_load_separate_core_block_assets', '__return_true' );

wp_register_style( 'global-styles', false, array(), true, true );
wp_enqueue_style( 'global-styles' );
Expand All @@ -163,22 +118,6 @@ public function test_third_party_blocks_inline_styles_for_elements_get_rendered_
);
}

/**
* @ticket 57868
*/
public function test_third_party_blocks_inline_styles_for_elements_get_rendered() {
wp_register_style( 'global-styles', false, array(), true, true );
wp_enqueue_style( 'global-styles' );
wp_add_global_styles_for_blocks();

$actual = get_echo( 'wp_print_styles' );

$this->assertStringContainsString(
'.wp-block-my-third-party-block cite{color: white;}',
$actual
);
}

/**
* @ticket 57868
*
Expand Down
116 changes: 116 additions & 0 deletions tests/phpunit/tests/theme/wpEnqueueBlockGlobalStyles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

require_once __DIR__ . '/base.php';

/**
* Tests wp_enqueue_block_global_styles().
*
* @group themes
*
* @covers ::wp_enqueue_block_global_styles
*/

class Tests_Theme_WpEnqueueBlockGlobalStyles extends WP_Theme_UnitTestCase {

/**
* Test blocks to unregister at cleanup.
*
* @var array
*/
private $test_blocks = array();

public function set_up() {
parent::set_up();
remove_action( 'wp_print_styles', 'print_emoji_styles' );
}

public function tear_down() {
// Unregister test blocks.
if ( ! empty( $this->test_blocks ) ) {
foreach ( $this->test_blocks as $test_block ) {
unregister_block_type( $test_block );
}
$this->test_blocks = array();
}

parent::tear_down();
}

/**
* @ticket 56915
* @ticket 60280
*/
public function test_third_party_blocks_inline_styles_get_registered_to_global_styles() {
$this->set_up_third_party_block();

$this->assertNotContains(
'.wp-block-my-third-party-block{background-color: hotpink;}',
$this->get_global_styles_blocks(),
'Third party block inline style should not be registered before running wp_enqueue_block_global_styles()'
);

wp_enqueue_block_global_styles();

$this->assertContains(
'.wp-block-my-third-party-block{background-color: hotpink;}',
$this->get_global_styles_blocks(),
'Third party block inline style should be registered after running wp_enqueue_block_global_styles()'
);
}

/**
* @ticket 56915
* @ticket 60280
*/
public function test_blocks_inline_styles_get_rendered() {
wp_enqueue_block_global_styles();

$actual = get_echo( 'wp_print_styles' );

$this->assertStringContainsString(
'.wp-block-my-third-party-block{background-color: hotpink;}',
$actual,
'Third party block inline style should render'
);
$this->assertStringContainsString(
'.wp-block-post-featured-image',
$actual,
'Core block should render'
);
}

/**
* @ticket 57868
* @ticket 60280
*/
public function test_third_party_blocks_inline_styles_for_elements_get_rendered() {
wp_enqueue_block_global_styles();

$actual = get_echo( 'wp_print_styles' );

$this->assertStringContainsString(
'.wp-block-my-third-party-block cite{color: white;}',
$actual
);
}


private function set_up_third_party_block() {
switch_theme( 'block-theme' );

$name = 'my/third-party-block';
$settings = array(
'icon' => 'text',
'category' => 'common',
'render_callback' => 'foo',
);
register_block_type( $name, $settings );

$this->test_blocks[] = $name;
}

private function get_global_styles_blocks() {
$actual = wp_styles()->get_data( 'global-styles-blocks', 'after' );
return is_array( $actual ) ? $actual : array();
}
}
Loading