foreach ( $styles as $style ) {
// Size check. Since styles are ordered by size, we can break the loop.
if ( $total_inline_size + $style['size'] > $total_inline_limit ) {
// Get the styles if we don't already have them.
$style['css'] = file_get_contents( $style['path'] );
* Check if the style contains relative URLs that need to be modified.
* URLs relative to the stylesheet's path should be converted to relative to the site's root.
$style['css'] = _wp_normalize_relative_css_links( $style['css'], $style['src'] );
// Set `src` to `false` and add styles inline.
$wp_styles->registered[ $style['handle'] ]->src = false;
if ( empty( $wp_styles->registered[ $style['handle'] ]->extra['after'] ) ) {
$wp_styles->registered[ $style['handle'] ]->extra['after'] = array();
array_unshift( $wp_styles->registered[ $style['handle'] ]->extra['after'], $style['css'] );
// Add the styles size to the $total_inline_size var.
$total_inline_size += (int) $style['size'];
* Makes URLs relative to the WordPress installation.
* @param string $css The CSS to make URLs relative to the WordPress installation.
* @param string $stylesheet_url The URL to the stylesheet.
* @return string The CSS with URLs made relative to the WordPress installation.
function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
return preg_replace_callback(
'#(url\s*\(\s*[\'"]?\s*)([^\'"\)]+)#',
static function ( $matches ) use ( $stylesheet_url ) {
list( , $prefix, $url ) = $matches;
// Short-circuit if the URL does not require normalization.
str_starts_with( $url, 'http:' ) ||
str_starts_with( $url, 'https:' ) ||
str_starts_with( $url, '//' ) ||
str_starts_with( $url, '#' ) ||
str_starts_with( $url, 'data:' )
// Build the absolute URL.
$absolute_url = dirname( $stylesheet_url ) . '/' . $url;
$absolute_url = str_replace( '/./', '/', $absolute_url );
// Convert to URL related to the site root.
$url = wp_make_link_relative( $absolute_url );
* Function that enqueues the CSS Custom Properties coming from theme.json.
function wp_enqueue_global_styles_css_custom_properties() {
wp_register_style( 'global-styles-css-custom-properties', false );
wp_add_inline_style( 'global-styles-css-custom-properties', wp_get_global_stylesheet( array( 'variables' ) ) );
wp_enqueue_style( 'global-styles-css-custom-properties' );
* Hooks inline styles in the proper place, depending on the active theme.
* @since 6.1.0 Added the `$priority` parameter.
* For block themes, styles are loaded in the head.
* For classic ones, styles are loaded in the body because the wp_head action happens before render_block.
* @link https://core.trac.wordpress.org/ticket/53494.
* @param string $style String containing the CSS styles to be added.
* @param int $priority To set the priority for the add_action.
function wp_enqueue_block_support_styles( $style, $priority = 10 ) {
$action_hook_name = 'wp_footer';
if ( wp_is_block_theme() ) {
$action_hook_name = 'wp_head';
static function () use ( $style ) {
echo "<style>$style</style>\n";
* Fetches, processes and compiles stored core styles, then combines and renders them to the page.
* Styles are stored via the style engine API.
* @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
* @param array $options {
* Optional. An array of options to pass to wp_style_engine_get_stylesheet_from_context().
* @type bool $optimize Whether to optimize the CSS output, e.g., combine rules.
* @type bool $prettify Whether to add new lines and indents to output.
* Default to whether the `SCRIPT_DEBUG` constant is defined.
function wp_enqueue_stored_styles( $options = array() ) {
$is_block_theme = wp_is_block_theme();
$is_classic_theme = ! $is_block_theme;
* For block themes, this function prints stored styles in the header.
* For classic themes, in the footer.
( $is_block_theme && doing_action( 'wp_footer' ) ) ||
( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
$core_styles_keys = array( 'block-supports' );
$compiled_core_stylesheet = '';
// Adds comment if code is prettified to identify core styles sections in debugging.
$should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
foreach ( $core_styles_keys as $style_key ) {
if ( $should_prettify ) {
$compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
// Chains core store ids to signify what the styles contain.
$style_tag_id .= '-' . $style_key;
$compiled_core_stylesheet .= wp_style_engine_get_stylesheet_from_context( $style_key, $options );
if ( ! empty( $compiled_core_stylesheet ) ) {
wp_register_style( $style_tag_id, false );
wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
wp_enqueue_style( $style_tag_id );
// Prints out any other stores registered by themes or otherwise.
$additional_stores = WP_Style_Engine_CSS_Rules_Store::get_stores();
foreach ( array_keys( $additional_stores ) as $store_name ) {
if ( in_array( $store_name, $core_styles_keys, true ) ) {
$styles = wp_style_engine_get_stylesheet_from_context( $store_name, $options );
if ( ! empty( $styles ) ) {
$key = "wp-style-engine-$store_name";
wp_register_style( $key, false );
wp_add_inline_style( $key, $styles );
wp_enqueue_style( $key );
* Enqueues a stylesheet for a specific block.
* If the theme has opted-in to separate-styles loading,
* then the stylesheet will be enqueued on-render,
* otherwise when the block inits.
* @param string $block_name The block-name, including namespace.
* An array of arguments. See wp_register_style() for full information about each argument.
* @type string $handle The handle for the stylesheet.
* @type string|false $src The source URL of the stylesheet.
* @type string[] $deps Array of registered stylesheet handles this stylesheet depends on.
* @type string|bool|null $ver Stylesheet version number.
* @type string $media The media for which this stylesheet has been defined.
* @type string|null $path Absolute path to the stylesheet, so that it can potentially be inlined.
function wp_enqueue_block_style( $block_name, $args ) {
* Callback function to register and enqueue styles.
* @param string $content When the callback is used for the render_block filter,
* the content needs to be returned so the function parameter
* is to ensure the content exists.
* @return string Block content.
$callback = static function ( $content ) use ( $args ) {
// Register the stylesheet.
if ( ! empty( $args['src'] ) ) {
wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
// Add `path` data if provided.
if ( isset( $args['path'] ) ) {
wp_style_add_data( $args['handle'], 'path', $args['path'] );
// Get the RTL file path.
$rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
if ( file_exists( $rtl_file_path ) ) {
wp_style_add_data( $args['handle'], 'rtl', 'replace' );
wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
// Enqueue the stylesheet.
wp_enqueue_style( $args['handle'] );
$hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
if ( wp_should_load_separate_core_block_assets() ) {
* Callback function to register and enqueue styles.
* @param string $content The block content.
* @param array $block The full block, including name and attributes.
* @return string Block content.
$callback_separate = static function ( $content, $block ) use ( $block_name, $callback ) {
if ( ! empty( $block['blockName'] ) && $block_name === $block['blockName'] ) {
return $callback( $content );
* The filter's callback here is an anonymous function because
* using a named function in this case is not possible.
* The function cannot be unhooked, however, users are still able
* to dequeue the stylesheets registered/enqueued by the callback
* which is why in this case, using an anonymous function
add_filter( 'render_block', $callback_separate, 10, 2 );
* The filter's callback here is an anonymous function because
* using a named function in this case is not possible.
* The function cannot be unhooked, however, users are still able
* to dequeue the stylesheets registered/enqueued by the callback
* which is why in this case, using an anonymous function
add_filter( $hook, $callback );
// Enqueue assets in the editor.
add_action( 'enqueue_block_assets', $callback );
* Loads classic theme styles on classic themes in the frontend.
* This is needed for backwards compatibility for button blocks specifically.
function wp_enqueue_classic_theme_styles() {
if ( ! wp_theme_has_theme_json() ) {
$suffix = wp_scripts_get_suffix();
wp_register_style( 'classic-theme-styles', '/' . WPINC . "/css/classic-themes$suffix.css" );
wp_style_add_data( 'classic-theme-styles', 'path', ABSPATH . WPINC . "/css/classic-themes$suffix.css" );
wp_enqueue_style( 'classic-theme-styles' );
* Loads classic theme styles on classic themes in the editor.
* This is needed for backwards compatibility for button blocks specifically.
* @param array $editor_settings The array of editor settings.
* @return array A filtered array of editor settings.
function wp_add_editor_classic_theme_styles( $editor_settings ) {
if ( wp_theme_has_theme_json() ) {
$suffix = wp_scripts_get_suffix();
$classic_theme_styles = ABSPATH . WPINC . "/css/classic-themes$suffix.css";
* This follows the pattern of get_block_editor_theme_styles,
* but we can't use get_block_editor_theme_styles directly as it
* only handles external files or theme files.
$classic_theme_styles_settings = array(
'css' => file_get_contents( $classic_theme_styles ),
'__unstableType' => 'core',
'isGlobalStyles' => false,
// Add these settings to the start of the array so that themes can override them.
array_unshift( $editor_settings['styles'], $classic_theme_styles_settings );
* Removes leading and trailing _empty_ script tags.
* This is a helper meant to be used for literal script tag construction
* within `wp_get_inline_script_tag()` or `wp_print_inline_script_tag()`.
* It removes the literal values of "<script>" and "</script>" from
* around an inline script after trimming whitespace. Typically this
* is used in conjunction with output buffering, where `ob_get_clean()`
* is passed as the `$contents` argument.
* // Strips exact literal empty SCRIPT tags.
* $js = '<script>sayHello();</script>;
* 'sayHello();' === wp_remove_surrounding_empty_script_tags( $js );
* // Otherwise if anything is different it warns in the JS console.
* $js = '<script type="text/javascript">console.log( "hi" );</script>';
* 'console.error( ... )' === wp_remove_surrounding_empty_script_tags( $js );
* @see wp_print_inline_script_tag()
* @see wp_get_inline_script_tag()
* @param string $contents Script body with manually created SCRIPT tag literals.
* @return string Script body without surrounding script tag literals, or
* original contents if both exact literals aren't present.
function wp_remove_surrounding_empty_script_tags( $contents ) {
$contents = trim( $contents );
strlen( $contents ) > strlen( $opener ) + strlen( $closer ) &&
strtoupper( substr( $contents, 0, strlen( $opener ) ) ) === $opener &&
strtoupper( substr( $contents, -strlen( $closer ) ) ) === $closer
return substr( $contents, strlen( $opener ), -strlen( $closer ) );
$error_message = __( 'Expected string to start with script tag (without attributes) and end with script tag, with optional whitespace.' );
_doing_it_wrong( __FUNCTION__, $error_message, '6.4' );
/* translators: %s: wp_remove_surrounding_empty_script_tags() */
__( 'Function %s used incorrectly in PHP.' ),
'wp_remove_surrounding_empty_script_tags()'