* 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 ) ) {
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();
* Enqueues the global styles custom css defined via theme.json.
function wp_enqueue_global_styles_custom_css() {
if ( ! wp_is_block_theme() ) {
// Don't enqueue Customizer's custom CSS separately.
remove_action( 'wp_head', 'wp_custom_css_cb', 101 );
$custom_css = wp_get_custom_css();
$custom_css .= wp_get_global_styles_custom_css();
if ( ! empty( $custom_css ) ) {
wp_add_inline_style( 'global-styles', $custom_css );
* 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 on-render.
* When this function returns true, other functions ensure that core blocks
* only load their assets on-render, and each block loads its own, individual
* assets. Third-party blocks only load their assets when rendered.
* When this function returns false, all core block assets are loaded regardless
* of whether they are rendered in a page or not, because they are all part of
* the `block-library/style.css` file. Assets for third-party blocks are always
* enqueued regardless of whether they are rendered or not.
* This only affects front end and not the block editor screens.
* @see wp_enqueue_registered_block_scripts_and_styles()
* @see register_block_style_handle()
* @return bool Whether separate 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 );
* 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_separate_core_block_assets() ) {
$load_editor_scripts_and_styles = is_admin() && wp_should_load_block_editor_scripts_and_styles();
$block_registry = WP_Block_Type_Registry::get_instance();
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 separate styles per-block, enqueue the stylesheet on render.
if ( wp_should_load_separate_core_block_assets() ) {
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 separate styles per-block, check if the block has a stylesheet registered.
if ( wp_should_load_separate_core_block_assets() ) {
$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 );
* Prints 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.
function wp_print_inline_script_tag( $data, $attributes = array() ) {
echo wp_get_inline_script_tag( $data, $attributes );
* Allows small styles to be inlined.
* This improves performance and sustainability, and is opt-in. Stylesheets can opt in
* by adding `path` data using `wp_style_add_data`, and defining the file's absolute path:
* wp_style_add_data( $style_handle, 'path', $file_path );
* @global WP_Styles $wp_styles
function wp_maybe_inline_styles() {
$total_inline_limit = 20000;
* The maximum size of inlined styles in bytes.
* @param int $total_inline_limit The file-size threshold, in bytes. Default 20000.
$total_inline_limit = apply_filters( 'styles_inline_size_limit', $total_inline_limit );
// Build an array of styles that have a path defined.
foreach ( $wp_styles->queue as $handle ) {
if ( ! isset( $wp_styles->registered[ $handle ] ) ) {
$src = $wp_styles->registered[ $handle ]->src;
$path = $wp_styles->get_data( $handle, 'path' );
$size = wp_filesize( $path );
if ( ! empty( $styles ) ) {
// Reorder styles array based on size.
static function ( $a, $b ) {
return ( $a['size'] <= $b['size'] ) ? -1 : 1;
* The total inlined size.
* On each iteration of the loop, if a style gets added inline the value of this var increases
* to reflect the total size of inlined styles.