Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
86ceeea
feat(styles): filter inline style attributes
Sukhendu2002 Aug 24, 2026
0ef5802
Merge branch 'trunk' into ticket/51325-inline-style-attributes
Sukhendu2002 Aug 24, 2026
c5e0b64
Merge branch 'trunk' into ticket/51325-inline-style-attributes
Sukhendu2002 Oct 7, 2026
1eb9b67
refactor: tighten inline style API types
Sukhendu2002 Oct 7, 2026
3ba1389
Merge branch 'trunk' into ticket/51325-inline-style-attributes
westonruter Oct 10, 2026
b3c0fa5
Address PHPStan errors in tests
westonruter Oct 10, 2026
0468fec
Make inline style tag tests less brittle
westonruter Oct 10, 2026
9461b76
Document inline style tag attribute values as string|bool
westonruter Oct 10, 2026
7167a17
Filter attributes of hoisted extra block library inline style
westonruter Oct 10, 2026
0b080ef
Avoid recomputing inline style when printing a stylesheet
westonruter Oct 10, 2026
b2d9c62
Use inline style tag API for other core STYLE tags
westonruter Oct 10, 2026
2f547ec
Restore theme features after inline style tag tests
westonruter Oct 10, 2026
cee3db6
Use inline style tag API in default wp_die() handler
westonruter Oct 10, 2026
3de2648
Use inline style tag API for interim login style
westonruter Oct 10, 2026
b727644
Use inline style tag API on Multisite signup and activation
westonruter Oct 10, 2026
4a2c1d0
Use inline style tag API for plugin information banner
westonruter Oct 10, 2026
d810194
Use inline style tag API for site icon preview in General Settings
westonruter Oct 10, 2026
a6799e8
Use inline style tag API in Customizer
westonruter Oct 10, 2026
5ac7b82
Use inline style tag API for site icon crop preview template
westonruter Oct 10, 2026
dfa73a5
Use inline style tag API for default gallery styles
westonruter Oct 10, 2026
b290c96
Use inline style tag API for Legacy Widget previews
westonruter Oct 10, 2026
1a312c6
Use inline style tag API in deprecated functions
westonruter Oct 10, 2026
db14096
Use inline style tag API in theme compat header
westonruter Oct 10, 2026
8dc2425
Use nowdoc for one-line CSS in deprecated functions
westonruter Oct 11, 2026
bd7687b
Use nowdoc for one-line CSS in interim login and Recent Comments widget
westonruter Oct 11, 2026
a976fe2
Use esc_url_raw() for plugin banner URLs in inline style
westonruter Oct 11, 2026
a6ee044
Use esc_url_raw() for site icon URL in General Settings inline style
westonruter Oct 11, 2026
7c51302
Use esc_url_raw() for theme compat background URL
westonruter Oct 11, 2026
a0b5382
Use double quotes for site icon URL in Customizer control template
westonruter Oct 11, 2026
14902b9
Escape URLs late inside inline style heredocs
westonruter Oct 11, 2026
ee5df21
Restore display context for theme compat background URL
westonruter Oct 11, 2026
aca233e
Escape percent signs in graceful_fail() inline style tag
westonruter Oct 11, 2026
22a99e2
Revert inline style tag API change in Legacy Widget block
westonruter Oct 11, 2026
e47bd44
Merge branch 'trunk' of https://github.com/WordPress/wordpress-develo…
westonruter Oct 11, 2026
f6b0261
Twenty Ten: Use inline style tag API when available
westonruter Oct 11, 2026
b8387e4
Twenty Eleven: Use inline style tag API when available
westonruter Oct 11, 2026
46f8f2b
Twenty Twelve: Use inline style tag API when available
westonruter Oct 11, 2026
e1f061c
Twenty Thirteen: Use inline style tag API when available
westonruter Oct 11, 2026
8e419cb
Twenty Fourteen: Use inline style tag API when available
westonruter Oct 11, 2026
bcf2f8a
Twenty Fifteen: Use inline style tag API when available
westonruter Oct 11, 2026
f2866da
Twenty Sixteen: Use inline style tag API when available
westonruter Oct 11, 2026
445b585
Twenty Seventeen: Use inline style tag API when available
westonruter Oct 11, 2026
35242b3
Twenty Nineteen: Use inline style tag API when available
westonruter Oct 11, 2026
4d63462
Twenty Twenty-One: Use inline style tag API when available
westonruter Oct 11, 2026
e965370
Avoid fatal error for non-string inline style data
westonruter Oct 11, 2026
37b6bab
Print inline style via WP_Styles::print_inline_style() when not conca…
westonruter Oct 11, 2026
a7e0bb8
Fix PHPStan errors in changed themes lines
westonruter Oct 11, 2026
a958ba2
Sanitize header text color for CSS instead of escaping as attribute
westonruter Oct 11, 2026
7a6b8e4
Validate header text color inline in Twenty Thirteen and Twenty Fourteen
westonruter Oct 11, 2026
595418c
Validate header text color in Twenty Eleven and Twenty Twelve
westonruter Oct 11, 2026
f503337
Twenty Twenty-One: Escape dark mode labels as CSS strings
westonruter Oct 11, 2026
c67e40c
Twenty Twenty-One: Escape dark mode labels late inside heredoc
westonruter Oct 11, 2026
45739df
Escape late inside heredocs in bundled theme header styles
westonruter Oct 11, 2026
b56f432
Merge branch 'trunk' into ticket/51325-inline-style-attributes
westonruter Oct 12, 2026
dc2267b
Limit this change to the inline style tag API
westonruter Oct 12, 2026
bfdccf9
Trim all CSS whitespace from inline style tag data
westonruter Oct 12, 2026
af978bd
Test whitespace normalization of inline style tag data
westonruter Oct 12, 2026
1aa54cc
Remove redundant test for filtered inline style attributes
westonruter Oct 12, 2026
9fb547f
Narrow types
westonruter Oct 12, 2026
770dc4e
Pass escaped CSS to the inline style attributes filter
Sukhendu2002 Oct 15, 2026
95faaff
Merge branch 'trunk' into ticket/51325-inline-style-attributes
Sukhendu2002 Oct 15, 2026
82ae44d
Merge branch 'trunk' into ticket/51325-inline-style-attributes
westonruter Oct 17, 2026
6655639
Tests: Use MockAction to capture inline style attributes filter args.
westonruter Oct 17, 2026
d028d71
Script Loader: Remove unreachable failure branch from `wp_get_inline_…
westonruter Oct 17, 2026
8841042
Script Loader: Ignore a non-array return from the `wp_inline_style_at…
westonruter Oct 17, 2026
8db7a9c
Script Loader: Keep hoisted block library inline style consistent wit…
westonruter Oct 17, 2026
243adb5
Revert "Script Loader: Ignore a non-array return from the `wp_inline_…
westonruter Oct 17, 2026
ea09b6e
Script Loader: Locate the hoisting placeholder STYLE tags by their CS…
westonruter Oct 17, 2026
aa46626
Script Loader: Preserve existing attributes when rebuilding the hoist…
westonruter Oct 17, 2026
1434399
Docs: Note that `wp_inline_style_attributes` may run more than once f…
westonruter Oct 17, 2026
44dfa2b
Script Loader: Stop reading STYLE contents once all hoisting placehol…
westonruter Oct 17, 2026
58fc960
Revert "Script Loader: Stop reading STYLE contents once all hoisting …
westonruter Oct 17, 2026
d1adb85
Revert "Script Loader: Locate the hoisting placeholder STYLE tags by …
westonruter Oct 17, 2026
269304a
Script Loader: Prevent the `wp_inline_style_attributes` filter from c…
westonruter Oct 17, 2026
c159a88
Script Loader: Locate the block library inline style by its ID again …
westonruter Oct 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions src/wp-includes/class-wp-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,6 @@ public function do_item( $handle, $group = false ) {
$src = $obj->src;
$inline_style = $this->print_inline_style( $handle, false );

if ( $inline_style ) {
$processor = new WP_HTML_Tag_Processor( '<style></style>' );
$processor->next_tag();
$processor->set_attribute( 'id', "{$handle}-inline-css" );
$processor->set_modifiable_text( "\n{$inline_style}\n" );
$inline_style_tag = "{$processor->get_updated_html()}\n";
} else {
$inline_style_tag = '';
}

if ( $this->do_concat ) {
if ( is_string( $src ) && $this->in_default_dir( $src ) && ! isset( $obj->extra['alt'] ) ) {
$this->concat .= "$handle,";
Expand All @@ -182,7 +172,12 @@ public function do_item( $handle, $group = false ) {

// A single item may alias a set of items, by having dependencies, but no source.
if ( ! $src ) {
if ( $inline_style_tag ) {
if ( $inline_style ) {
$inline_style_tag = wp_get_inline_style_tag(
$inline_style,
array( 'id' => "{$handle}-inline-css" )
);

if ( $this->do_concat ) {
$this->print_html .= $inline_style_tag;
} else {
Expand Down Expand Up @@ -249,8 +244,11 @@ public function do_item( $handle, $group = false ) {

if ( $this->do_concat ) {
$this->print_html .= $tag;
if ( $inline_style_tag ) {
$this->print_html .= $inline_style_tag;
if ( $inline_style ) {
$this->print_html .= wp_get_inline_style_tag(
$inline_style,
array( 'id' => "{$handle}-inline-css" )
);
}
} else {
echo $tag;
Expand Down Expand Up @@ -376,11 +374,10 @@ public function print_inline_style( $handle, $display = true ) {
return $output;
}

$processor = new WP_HTML_Tag_Processor( '<style></style>' );
$processor->next_tag();
$processor->set_attribute( 'id', "{$handle}-inline-css" );
$processor->set_modifiable_text( "\n{$output}\n" );
echo "{$processor->get_updated_html()}\n";
wp_print_inline_style_tag(
$output,
array( 'id' => "{$handle}-inline-css" )
);

return true;
}
Expand Down
148 changes: 132 additions & 16 deletions src/wp-includes/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2453,12 +2453,9 @@ function _print_styles() {
echo "<link rel='stylesheet' href='" . esc_attr( $href ) . "' media='all' />\n";

if ( ! empty( $wp_styles->print_code ) ) {
$processor = new WP_HTML_Tag_Processor( '<style></style>' );
$processor->next_tag();
$style_tag_contents = "\n{$wp_styles->print_code}\n"
. sprintf( "/*# sourceURL=%s */\n", rawurlencode( $concat_source_url ) );
$processor->set_modifiable_text( $style_tag_contents );
echo "{$processor->get_updated_html()}\n";
$style_tag_contents = $wp_styles->print_code . "\n"
. sprintf( '/*# sourceURL=%s */', rawurlencode( $concat_source_url ) );
wp_print_inline_style_tag( $style_tag_contents );
}
}

Expand Down Expand Up @@ -3672,6 +3669,110 @@ function wp_print_inline_script_tag( $data, $attributes = array() ) {
echo wp_get_inline_script_tag( $data, $attributes );
}

/**
* Constructs an inline style tag.
*
* It is possible to inject attributes in the `<style>` tag via the {@see 'wp_inline_style_attributes'} filter.
*
* @since 7.2.0
*
* @param string $data CSS for the style tag.
* @param array<string, string|bool> $attributes Optional. Key-value pairs representing `<style>` tag attributes.
* @return string HTML style tag containing the provided CSS. Any `</style` in the CSS is escaped as `\3c\2fstyle`.
*
* @phpstan-param array<non-empty-string, string|bool> $attributes
* @phpstan-return non-falsy-string
*/
function wp_get_inline_style_tag( $data, array $attributes = array() ): string {
$data = "\n" . trim( (string) $data, " \t\f\r\n" ) . "\n";

$processor = new WP_HTML_Tag_Processor( '<style></style>' );
$processor->next_tag();

/*
* Set the text first so the filter receives the CSS as it will be printed, which may differ from $data after escaping.
* This cannot fail for a STYLE tag, since any closing tag in the CSS is escaped rather than rejected.
*/
$processor->set_modifiable_text( $data );

/**
* Filters attributes to be added to an inline style tag.
*
* This filter may run more than once for the same inline style, in which case only the last result is printed.
* For example, the `wp-block-library` inline style is built again by {@see wp_hoist_late_printed_styles()}
* after a placeholder is removed from its CSS.
*
* The `id` attribute, when supplied, cannot be changed or removed by this filter, since other code may locate the
* `<style>` tag by it. For example, the Customizer preview locates the `wp-custom-css` style to update it.
*
* @since 7.2.0
*
* @param array<string, string|bool> $attributes Key-value pairs representing `<style>` tag attributes.
* Only the attribute name is added to the `<style>` tag for
* entries with a boolean value, and that are true.
* @param string $data Inline CSS, as it will be printed in the `<style>` tag.
*/
$filtered_attributes = apply_filters( 'wp_inline_style_attributes', $attributes, $processor->get_modifiable_text() );

Comment thread
westonruter marked this conversation as resolved.
// Ensure the supplied ID is retained. Setting it first also prevents a lexical variation of the name from overriding it.
if ( isset( $attributes['id'] ) ) {
if ( ( $filtered_attributes['id'] ?? null ) !== $attributes['id'] ) {
_doing_it_wrong(
__FUNCTION__,
sprintf(
/* translators: 1: id, 2: wp_inline_style_attributes */
__( 'The %1$s attribute cannot be changed by the %2$s filter.' ),
'<code>id</code>',
'<code>wp_inline_style_attributes</code>'
),
'7.2.0'
);
}
$processor->set_attribute( 'id', $attributes['id'] );
}

foreach ( $filtered_attributes as $name => $value ) {
/*
* Lexical variations of an attribute name may represent the
* same attribute in HTML, therefore it is possible that the
* input array might contain duplicate attributes even though
* it is keyed on their name. Calling code should rewrite an
* attribute's value rather than sending a duplicate attribute.
*
* Example:
*
* array( 'id' => 'main', 'ID' => 'nav' )
*
* In this example, there are two keys both describing the `id`
* attribute. PHP array iteration is in key-insertion order so
* the 'id' value will be set in the STYLE tag.
*/
if ( null !== $processor->get_attribute( $name ) ) {
continue;
}

$processor->set_attribute( $name, $value ?? true );
}

return "{$processor->get_updated_html()}\n";
}

/**
* Prints an inline style tag.
*
* It is possible to inject attributes in the `<style>` tag via the {@see 'wp_inline_style_attributes'} filter.
*
* @since 7.2.0
*
* @param string $data CSS for the style tag.
* @param array<string, string|bool> $attributes Optional. Key-value pairs representing `<style>` tag attributes.
*
* @phpstan-param array<non-empty-string, string|bool> $attributes
*/
function wp_print_inline_style_tag( $data, array $attributes = array() ): void {
echo wp_get_inline_style_tag( $data, $attributes );
}

/**
* Allows small styles to be inlined.
*
Expand Down Expand Up @@ -3869,10 +3970,7 @@ function wp_enqueue_block_support_styles( $style, $priority = 10 ) {
add_action(
$action_hook_name,
static function () use ( $style ) {
$processor = new WP_HTML_Tag_Processor( '<style></style>' );
$processor->next_tag();
$processor->set_modifiable_text( $style );
echo "{$processor->get_updated_html()}\n";
wp_print_inline_style_tag( $style );
},
$priority
);
Expand Down Expand Up @@ -4584,7 +4682,26 @@ public function replace( string $text ): void {
if ( '' === trim( $css_text ) ) {
$processor->remove();
} else {
$processor->set_modifiable_text( $css_text );
/*
* Rebuild the STYLE tag rather than only replacing its text, so that the 'wp_inline_style_attributes'
* filter runs again with the CSS as it is now printed. Otherwise, any attribute derived from the CSS,
* such as a hash for a Content Security Policy, would no longer match it. The attributes already on
* the tag are passed along so that they are preserved, while the filter may still override them. The
* trailing newline is removed since the newline which followed the original tag remains in the buffer.
*/
$attributes = array();
foreach ( $processor->get_attribute_names_with_prefix( '' ) ?? array() as $attribute_name ) {
$attribute_value = $processor->get_attribute( $attribute_name );
if ( null !== $attribute_value ) {
$attributes[ $attribute_name ] = $attribute_value;
}
}
$processor->replace(
rtrim(
wp_get_inline_style_tag( $css_text, $attributes ),
"\n"
)
);
}

$inserted_after = $printed_core_block_styles;
Expand All @@ -4597,11 +4714,10 @@ public function replace( string $text ): void {
* a sourceURL comment after the placeholder above is removed.
*/
if ( ! preg_match( ':^\s*(/\*# sourceURL=\S+? \*/\s*)?$:s', $extra_inline_styles ) ) {
$style_processor = new WP_HTML_Tag_Processor( '<style></style>' );
$style_processor->next_tag();
$style_processor->set_attribute( 'id', 'wp-block-library-inline-css-extra' );
$style_processor->set_modifiable_text( $extra_inline_styles );
$inserted_after .= "{$style_processor->get_updated_html()}\n";
$inserted_after .= wp_get_inline_style_tag(
$extra_inline_styles,
array( 'id' => 'wp-block-library-inline-css-extra' )
);
}

if ( '' !== $inserted_after ) {
Expand Down
Loading
Loading