static function ( $node ) {
return ! in_array( 'blocks', $node['path'], true );
* Enqueues the global styles defined via theme.json.
function wp_enqueue_global_styles() {
$assets_on_demand = wp_should_load_block_assets_on_demand();
$is_block_theme = wp_is_block_theme();
$is_classic_theme = ! $is_block_theme;
* Global styles should be printed in the head for block themes, or for classic themes when loading assets on
* demand is disabled, which is the default.
* The footer should only be used for classic themes when loading assets on demand is enabled.
* See https://core.trac.wordpress.org/ticket/53494 and https://core.trac.wordpress.org/ticket/61965.
( $is_block_theme && doing_action( 'wp_footer' ) ) ||
( $is_classic_theme && doing_action( 'wp_footer' ) && ! $assets_on_demand ) ||
( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) && $assets_on_demand )
* 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();
* Dequeue the Customizer's custom CSS
* and add it before the global styles custom CSS.
remove_action( 'wp_head', 'wp_custom_css_cb', 101 );
// Get the custom CSS from the Customizer and add it to the global stylesheet.
$custom_css = wp_get_custom_css();
$stylesheet .= $custom_css;
// Add the global styles custom CSS at the end.
$stylesheet .= wp_get_global_stylesheet( array( 'custom-css' ) );
if ( empty( $stylesheet ) ) {
wp_register_style( 'global-styles', false );
wp_add_inline_style( 'global-styles', $stylesheet );
wp_enqueue_style( 'global-styles' );
// Add each block as an inline css.
wp_add_global_styles_for_blocks();
* Checks if the editor scripts and styles for all registered block types
* should be enqueued on the current screen.
* @global WP_Screen $current_screen WordPress current screen object.
* @return bool Whether scripts and styles should be enqueued.
function wp_should_load_block_editor_scripts_and_styles() {
$is_block_editor_screen = ( $current_screen instanceof WP_Screen ) && $current_screen->is_block_editor();
* Filters the flag that decides whether or not block editor scripts and styles
* are going to be enqueued on the current screen.
* @param bool $is_block_editor_screen Current value of the flag.
return apply_filters( 'should_load_block_editor_scripts_and_styles', $is_block_editor_screen );
* Checks whether separate styles should be loaded for core blocks.
* When this function returns true, other functions ensure that core blocks use their own separate stylesheets.
* When this function returns false, all core blocks will use the single combined 'wp-block-library' stylesheet.
* As a side effect, the return value will by default result in block assets to be loaded on demand, via the
* {@see wp_should_load_block_assets_on_demand()} function. This behavior can be separately altered via that function.
* This only affects front end and not the block editor screens.
* @see @see wp_should_load_block_assets_on_demand()
* @see wp_enqueue_registered_block_scripts_and_styles()
* @see register_block_style_handle()
* @return bool Whether separate core block assets will be loaded.
function wp_should_load_separate_core_block_assets() {
if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) {
* Filters whether block styles should be loaded separately.
* Returning false loads all core block assets, regardless of whether they are rendered
* in a page or not. Returning true loads core block assets only when they are rendered.
* @param bool $load_separate_assets Whether separate assets will be loaded.
* Default false (all block assets are loaded, even when not used).
return apply_filters( 'should_load_separate_core_block_assets', false );
* Checks whether block styles should be loaded only on-render.
* When this function returns true, other functions ensure that blocks only load their assets on-render.
* When this function returns false, all block assets are loaded regardless of whether they are rendered in a page.
* The default return value depends on the result of {@see wp_should_load_separate_core_block_assets()}, which controls
* whether Core block stylesheets should be loaded separately or via a combined 'wp-block-library' stylesheet.
* This only affects front end and not the block editor screens.
* @see wp_should_load_separate_core_block_assets()
* @return bool Whether to load block assets only when they are rendered.
function wp_should_load_block_assets_on_demand() {
if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) {
* For backward compatibility, the default return value for this function is based on the return value of
* `wp_should_load_separate_core_block_assets()`. Initially, this function used to control both of these concerns.
$load_assets_on_demand = wp_should_load_separate_core_block_assets();
* Filters whether block styles should be loaded on demand.
* Returning false loads all block assets, regardless of whether they are rendered in a page or not.
* Returning true loads block assets only when they are rendered.
* The default value of the filter depends on the result of {@see wp_should_load_separate_core_block_assets()},
* which controls whether Core block stylesheets should be loaded separately or via a combined 'wp-block-library'
* @param bool $load_assets_on_demand Whether to load block assets only when they are rendered.
return apply_filters( 'should_load_block_assets_on_demand', $load_assets_on_demand );
* Enqueues registered block scripts and styles, depending on current rendered
* context (only enqueuing editor scripts while in context of the editor).
* @global WP_Screen $current_screen WordPress current screen object.
function wp_enqueue_registered_block_scripts_and_styles() {
if ( wp_should_load_block_assets_on_demand() ) {
$load_editor_scripts_and_styles = is_admin() && wp_should_load_block_editor_scripts_and_styles();
$block_registry = WP_Block_Type_Registry::get_instance();
* Block styles are only enqueued if they're registered. For core blocks, this is only the case if
* `wp_should_load_separate_core_block_assets()` returns true. Otherwise they use the single combined
* 'wp-block-library` stylesheet. See also `register_core_block_style_handles()`.
* Since `wp_enqueue_style()` does not trigger warnings if the style is not registered, it is okay to not cater for
* this behavior here and simply call `wp_enqueue_style()` unconditionally.
foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
// Front-end and editor styles.
foreach ( $block_type->style_handles as $style_handle ) {
wp_enqueue_style( $style_handle );
// Front-end and editor scripts.
foreach ( $block_type->script_handles as $script_handle ) {
wp_enqueue_script( $script_handle );
if ( $load_editor_scripts_and_styles ) {
foreach ( $block_type->editor_style_handles as $editor_style_handle ) {
wp_enqueue_style( $editor_style_handle );
foreach ( $block_type->editor_script_handles as $editor_script_handle ) {
wp_enqueue_script( $editor_script_handle );
* Function responsible for enqueuing the styles required for block styles functionality on the editor and on the frontend.
* @global WP_Styles $wp_styles
function enqueue_block_styles_assets() {
$block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();
foreach ( $block_styles as $block_name => $styles ) {
foreach ( $styles as $style_properties ) {
if ( isset( $style_properties['style_handle'] ) ) {
// If the site loads block styles on demand, enqueue the stylesheet on render.
if ( wp_should_load_block_assets_on_demand() ) {
static function ( $html, $block ) use ( $block_name, $style_properties ) {
if ( $block['blockName'] === $block_name ) {
wp_enqueue_style( $style_properties['style_handle'] );
wp_enqueue_style( $style_properties['style_handle'] );
if ( isset( $style_properties['inline_style'] ) ) {
// Default to "wp-block-library".
$handle = 'wp-block-library';
// If the site loads block styles on demand, check if the block has a stylesheet registered.
if ( wp_should_load_block_assets_on_demand() ) {
$block_stylesheet_handle = generate_block_asset_handle( $block_name, 'style' );
if ( isset( $wp_styles->registered[ $block_stylesheet_handle ] ) ) {
$handle = $block_stylesheet_handle;
// Add inline styles to the calculated handle.
wp_add_inline_style( $handle, $style_properties['inline_style'] );
* Function responsible for enqueuing the assets required for block styles functionality on the editor.
function enqueue_editor_block_styles_assets() {
$block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();
$register_script_lines = array( '( function() {' );
foreach ( $block_styles as $block_name => $styles ) {
foreach ( $styles as $style_properties ) {
'name' => $style_properties['name'],
'label' => $style_properties['label'],
if ( isset( $style_properties['is_default'] ) ) {
$block_style['isDefault'] = $style_properties['is_default'];
$register_script_lines[] = sprintf(
' wp.blocks.registerBlockStyle( \'%s\', %s );',
wp_json_encode( $block_style )
$register_script_lines[] = '} )();';
$inline_script = implode( "\n", $register_script_lines );
wp_register_script( 'wp-block-styles', false, array( 'wp-blocks' ), true, array( 'in_footer' => true ) );
wp_add_inline_script( 'wp-block-styles', $inline_script );
wp_enqueue_script( 'wp-block-styles' );
* Enqueues the assets required for the block directory within the block editor.
function wp_enqueue_editor_block_directory_assets() {
wp_enqueue_script( 'wp-block-directory' );
wp_enqueue_style( 'wp-block-directory' );
* Enqueues the assets required for the format library within the block editor.
function wp_enqueue_editor_format_library_assets() {
wp_enqueue_script( 'wp-format-library' );
wp_enqueue_style( 'wp-format-library' );
* Sanitizes an attributes array into an attributes string to be placed inside a `<script>` tag.
* Automatically injects type attribute if needed.
* Used by {@see wp_get_script_tag()} and {@see wp_get_inline_script_tag()}.
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* @return string String made of sanitized `<script>` tag attributes.
function wp_sanitize_script_attributes( $attributes ) {
$html5_script_support = ! is_admin() && ! current_theme_supports( 'html5', 'script' );
* If HTML5 script tag is supported, only the attribute name is added
* to $attributes_string for entries with a boolean value, and that are true.
foreach ( $attributes as $attribute_name => $attribute_value ) {
if ( is_bool( $attribute_value ) ) {
if ( $attribute_value ) {
$attributes_string .= $html5_script_support ? sprintf( ' %1$s="%2$s"', esc_attr( $attribute_name ), esc_attr( $attribute_name ) ) : ' ' . esc_attr( $attribute_name );
$attributes_string .= sprintf( ' %1$s="%2$s"', esc_attr( $attribute_name ), esc_attr( $attribute_value ) );
return $attributes_string;
* Formats `<script>` loader tags.
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* @return string String containing `<script>` opening and closing tags.
function wp_get_script_tag( $attributes ) {
if ( ! isset( $attributes['type'] ) && ! is_admin() && ! current_theme_supports( 'html5', 'script' ) ) {
// Keep the type attribute as the first for legacy reasons (it has always been this way in core).
$attributes = array_merge(
array( 'type' => 'text/javascript' ),
* Filters attributes to be added to a script tag.
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* Only the attribute name is added to the `<script>` tag for
* entries with a boolean value, and that are true.
$attributes = apply_filters( 'wp_script_attributes', $attributes );
return sprintf( "<script%s></script>\n", wp_sanitize_script_attributes( $attributes ) );
* Prints formatted `<script>` loader tag.
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
function wp_print_script_tag( $attributes ) {
echo wp_get_script_tag( $attributes );
* Constructs an inline script tag.
* It is possible to inject attributes in the `<script>` tag via the {@see 'wp_script_attributes'} filter.
* Automatically injects type attribute if needed.
* @param string $data Data for script tag: JavaScript, importmap, speculationrules, etc.
* @param array $attributes Optional. Key-value pairs representing `<script>` tag attributes.
* @return string String containing inline JavaScript code wrapped around `<script>` tag.
function wp_get_inline_script_tag( $data, $attributes = array() ) {
$is_html5 = current_theme_supports( 'html5', 'script' ) || is_admin();
if ( ! isset( $attributes['type'] ) && ! $is_html5 ) {
// Keep the type attribute as the first for legacy reasons (it has always been this way in core).
$attributes = array_merge(
array( 'type' => 'text/javascript' ),
* XHTML extracts the contents of the SCRIPT element and then the XML parser
* decodes character references and other syntax elements. This can lead to
* misinterpretation of the script contents or invalid XHTML documents.
* Wrapping the contents in a CDATA section instructs the XML parser not to
* transform the contents of the SCRIPT element before passing them to the
* <script>console.log('…');</script>
* In an HTML document this would print "…" to the console,
* but in an XHTML document it would print "…" to the console.
* <script>console.log('An image is <img> in HTML');</script>
* In an HTML document this would print "An image is <img> in HTML",
* but it's an invalid XHTML document because it interprets the `<img>`
* as an empty tag missing its closing `/`.
* @see https://www.w3.org/TR/xhtml1/#h-4.8
! isset( $attributes['type'] ) ||
'module' === $attributes['type'] ||
str_contains( $attributes['type'], 'javascript' ) ||
str_contains( $attributes['type'], 'ecmascript' ) ||
str_contains( $attributes['type'], 'jscript' ) ||
str_contains( $attributes['type'], 'livescript' )
* If the string `]]>` exists within the JavaScript it would break
* out of any wrapping CDATA section added here, so to start, it's
* necessary to escape that sequence which requires splitting the
* content into two CDATA sections wherever it's found.
* Note: it's only necessary to escape the closing `]]>` because
* an additional `<![CDATA[` leaves the contents unchanged.
$data = str_replace( ']]>', ']]]]><![CDATA[>', $data );
// Wrap the entire escaped script inside a CDATA section.
$data = sprintf( "/* <![CDATA[ */\n%s\n/* ]]> */", $data );
$data = "\n" . trim( $data, "\n\r " ) . "\n";
* Filters attributes to be added to a script tag.
* @param array $attributes Key-value pairs representing `<script>` tag attributes.
* Only the attribute name is added to the `<script>` tag for
* entries with a boolean value, and that are true.
* @param string $data Inline data.
$attributes = apply_filters( 'wp_inline_script_attributes', $attributes, $data );
return sprintf( "<script%s>%s</script>\n", wp_sanitize_script_attributes( $attributes ), $data );