Skip to content

Style states: Fix phantom pseudo element style output - #81291

Merged
ramonjd merged 3 commits into
trunkfrom
fix/phantom-pseudo-style-output
Aug 7, 2026
Merged

Style states: Fix phantom pseudo element style output#81291
ramonjd merged 3 commits into
trunkfrom
fix/phantom-pseudo-style-output

Conversation

@talldan

@talldan talldan commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note: PR is based on the branch from #81265. It relies on the changes there.

What?

Fixes an issue where an incorrect pseudo style would be output (copying the base element's style) whenever a responsive+pseudo style was set for the element (but no pseudo style itself).

Why?

With an element styled at its root and its pseudo styled only inside a responsive state like @mobile:

{
	"styles": {
		"blocks": {
			"core/group": {
				"elements": {
					"link": { "color": { "text": "blue" } }
				},
				"@mobile": {
					"elements": {
						"link": { ":hover": { "color": { "text": "darkred" } } }
					}
				}
			}
		}
	}
}

CSS like this would be incorrectly produced:

:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){color: blue;}

The pseudo node defined by the responsive style was being included as part of the root. get_styles_for_block() falls back to the element's base styles when the pseudo key is absent — outputting a rule the theme never defined.

How?

Only output the default pseudo rule when the pseudo is styled outside a breakpoint — the same check the block-level pseudo styles already use. The breakpoint rules already decide this per breakpoint, so they're untouched.

PR also separately removes some dead code:

if ( $include_node_paths_only ) {
    $nodes[] = array(
        'path' => $element_pseudo_path,
    );
    continue;
}

The code inside the if statement is unreachable because there's a similar continue statement earlier in the loop:

if ( $include_node_paths_only ) {
$nodes[] = array(
'path' => $element_path,
);
continue;
}
.

Testing Instructions

  1. Add the snippet above to a theme.json
  2. Create a post that has a group with a paragraph inside it that has a link
  3. Preview the post.
  4. In the dev tools, resize to a browser size. Inspect the link and enable :hover styles.
  5. Check the element's CSS in the dev tools

On trunk - the block has a rogue style like this:

:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover) {
    color: blue;
}

In this PR - the block doesn't have that style

Use of AI Tools

OpenCode / Kimi K3

@talldan talldan added [Type] Bug An existing feature does not function as intended [Feature] Style States Related to block style states (currently viewport and pseudo-states) labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: tellthemachines <isabel_brison@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Comment on lines -3763 to -3771
if ( ! $has_element_pseudo ) {
foreach ( array_keys( $responsive_media_queries ) as $bp ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $bp ]['elements'][ $element ][ $pseudo_selector ] ) ) {
$has_element_pseudo = true;
break;
}
}
}

@talldan talldan Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the code that causes the issue. When there's no pseudo style on the base it then checks the breakpoints, and sets has_element_pseudo to true, but then creates a node that doesn't have the right path.

I expect it was accidentally left in during refactoring and other changes.

Comment on lines -3774 to -3779
if ( $include_node_paths_only ) {
$nodes[] = array(
'path' => $element_pseudo_path,
);
continue;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is dead code as explained in the PR description.

@talldan
talldan changed the base branch from fix/theme-json-responsive-element-styles to trunk August 6, 2026 15:35
@talldan talldan self-assigned this Aug 6, 2026
@talldan talldan added the Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Aug 6, 2026
@talldan
talldan changed the base branch from trunk to fix/theme-json-responsive-element-styles August 6, 2026 16:29
@talldan
talldan force-pushed the fix/phantom-pseudo-style-output branch from 05f74e3 to 7bf3b6d Compare August 6, 2026 16:57
@talldan
talldan force-pushed the fix/phantom-pseudo-style-output branch 2 times, most recently from 0852368 to bf1aadb Compare August 6, 2026 17:03
@talldan
talldan removed the request for review from a team August 6, 2026 17:04
@talldan talldan added the props-bot Manually triggers Props Bot to ensure the list of props is up to date. label Aug 6, 2026

@tellthemachines tellthemachines left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for fixing this, LGTM!

Base automatically changed from fix/theme-json-responsive-element-styles to trunk August 7, 2026 02:17
talldan added 2 commits August 7, 2026 12:22
…nodes

In get_block_nodes(), the element loop continues before reaching the
pseudo-selector loop when include_node_paths_only is set, so the
path-only branch inside the pseudo loop can never execute.
When an element's pseudo state was styled only inside a breakpoint,
get_block_nodes() still emitted the default pseudo node.
get_styles_for_block() then fell back to the element's base styles,
outputting a rule the theme never defined, e.g.:

    :root :where(.wp-block-group a):hover{color: blue;}

Emit the default pseudo node only when the default state styles the
pseudo, matching the block-level pseudo handling. The per-breakpoint
pseudo nodes already guard themselves.
Comment thread phpunit/class-wp-theme-json-test.php
@ramonjd

ramonjd commented Aug 7, 2026

Copy link
Copy Markdown
Member

Rebased locally, will push soon, but wanted to add that LGTM

Before

:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: pink;}
:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){color: pink;}
@media (width <= 480px){:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){color: green;}}

After

:root :where(.wp-block-group a:where(:not(.wp-element-button))){color: pink;}
@media (width <= 480px){:root :where(.wp-block-group a:where(:not(.wp-element-button)):hover){color: green;}}

@ramonjd
ramonjd force-pushed the fix/phantom-pseudo-style-output branch from bf1aadb to 5927306 Compare August 7, 2026 02:41
@ramonjd
ramonjd enabled auto-merge (squash) August 7, 2026 03:01
@ramonjd
ramonjd merged commit 102eb74 into trunk Aug 7, 2026
46 of 48 checks passed
@ramonjd
ramonjd deleted the fix/phantom-pseudo-style-output branch August 7, 2026 03:17
@github-actions github-actions Bot added this to the Gutenberg 23.8 milestone Aug 7, 2026
gutenbergplugin pushed a commit that referenced this pull request Aug 7, 2026
Co-authored-by: talldan <talldanwp@git.wordpress.org>
Co-authored-by: tellthemachines <isabel_brison@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
@github-actions github-actions Bot added Backported to WP Core Pull request that has been successfully merged into WP Core and removed Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

I just cherry-picked this PR to the wp/7.1 branch to get it included in the next release: 96f4b74

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backported to WP Core Pull request that has been successfully merged into WP Core [Feature] Style States Related to block style states (currently viewport and pseudo-states) props-bot Manually triggers Props Bot to ensure the list of props is up to date. [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants