Block states: pair focus states with a focus-within rule - #81268
Block states: pair focus states with a focus-within rule#81268amitraj2203 wants to merge 1 commit into
Conversation
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: 0 B Total Size: 7.82 MB |
tellthemachines
left a comment
There was a problem hiding this comment.
Thanks for the PR! However, I don't think we should try to fix this with a generic approach such as this. We may want to add focus-within as a pseudo state in its own right in the future for more complex blocks like tabs or cards, and this would complicate that needlessly. Also, for other blocks that may adopt the focus state we can't be sure this behaviour is always desirable.
It would be less risky to try a Navigation-specific fix for this. We have precedent for adding block-specific responsive styles in Gallery block; for this case a similar solution should work well.
|
@tellthemachines I tried the Gallery-style approach locally: Navigation Link's render callback builds its own focus rules and registers them with the style engine, scoped to the link inside the item. That part works on the front end. One issue though. Generating the CSS in the block means redoing the bits the shared states code already handles, and I missed four of them on the first pass:
None of those are reachable through Navigation Link's supports today (it only supports typography), but the block now carries its own copy of that pipeline, and the editor would need a second copy in JS. That feels like a lot to maintain So would you be open to a per-block selector instead? Navigation Link would declare that its state styles target On the editor side: hovering a link previews the hover style fine, but clicking it doesn't preview the focus style. Clicking puts focus on the editable label inside the link, so the styled element never matches So the editor needs to reflect focus some other way. I have a version that tracks focus in the block's subtree and applies the styles while focus is inside, so the generated CSS carries no focus pseudo-class at all and Happy to go either way — just wanted to check before redoing it. |
Block state styles are generated against the block's style root, which for a Navigation Link is the `<li>` wrapper. `:hover` and `:active` match an element while a descendant of it is hovered or activated, so those states work on the wrapper. Focus does not: it matches only the element that received focus, the `<a>` inside. `:focus` and `:focus-visible` therefore had no effect at all on the front end. The block's render callback now builds its own focus state rules, scoped to the link, and registers them with the style engine under an instance class — the pattern the Gallery block uses for its responsive block gap. `:hover` and `:active` are left to the states block support and emit no extra rule.
2ab0e68 to
38a77fc
Compare
|
Let's consider fixing this issue in a minor release, as 7.1 RC3 is scheduled for release tomorrow. |
What?
Closes #81256
Pairs a block's
:focusand:focus-visiblestate styles with a rule that matches focus inside the styled element, so focus states apply to Navigation Link — on the front end as well as in the editor.Why?
Block state styles are generated against the block's style root.
core/navigation-linkdeclares noselectors.root, so its state CSS targets the block wrapper — the<li>::hoverand:activematch an element while a descendant of it is hovered or activated, so those states work: hovering the link hovers the item. Focus does not propagate that way — it matches only the element that received focus, the<a>inside. So:focusand:focus-visibleon a Navigation Link had no effect at all, in the editor or on the front end.core/buttonis unaffected because itsselectors.root(.wp-block-button .wp-block-button__link) already points at the element that receives focus. That is the inconsistency reported in the issue.How?
For each focus state, emit a second rule with the equivalent "focus is inside me" selector —
:focus-withinfor:focus,:has(:focus-visible)for:focus-visible:lib/block-supports/states.php: newgutenberg_get_state_styles_with_focus_pairs(), applied in the pseudo-state and responsive pseudo-state loops ofgutenberg_render_block_states_support().packages/block-editor/src/hooks/style.js: the same map ingetPseudoStateCSSRules(), for the editor's block-instance state CSS.:hoverand:activeare left alone — they already match while a descendant is hovered or activated. The paired rules are emitted separately, not as a selector list, so a browser without:has()support still honours the plain focus state. For blocks whose state styles already target the focusable element, the paired selector matches the same element and changes nothing. No block selectors change, so Global Styles output is untouched.Screen.Recording.2026-08-06.at.12.41.58.PM.mov
Testing Instructions
trunkthe focus style does nothing.trunkit never does.trunk.Testing Instructions for Keyboard
Step 4 is the keyboard path: Tab through the navigation until the styled link receives focus and confirm the configured focus style applies. In the editor, move the caret into the link's text with the keyboard and confirm the focus style previews.