Repository navigation
Block Style States: Add -current custom state for Nav Link - #78769
MaggieCabrera wants to merge 11 commits into
Conversation
|
Size Change: +801 B (+0.01%) Total Size: 8.44 MB 📦 View Changed
|
9e53351 to
80ad5da
Compare
|
Flaky tests detected in 4223a4b. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27193382063
|
|
@MaggieCabrera not sure if it's this PR or not, but I'd love the ability to style the active menu item of a navigation. Is this mockup helpful, and can we do it?
Let me know if it's useful to attach to an issue instead. |
We probably need to have multiple PRs to also bring the pseudo states to the block too. It's in my plans, hopefully I can get to it for 7.2 |
|
Fantastic. Please ping me if there's anything you need that I can help with. |
Adds a custom state group, labeled "Item", between the Viewport and Pseudo state groups of StateControl, and teaches StateControlBadges to show and summarize the selected custom state. The three groups now share one StateMenuGroup renderer. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4223a4b to
965ce13
Compare
Adds `custom` to the selected block style state, next to `viewport` and `pseudo`, so the store and the style path helpers can address a custom state such as Navigation Link's `-current`. The style path order is viewport, custom, pseudo, so Mobile, Current and Hover are stored at `style['@mobile']['-current'][':hover']`. `hasSelectedBlockStyleState` now also returns true for a selected custom state. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generates per-block CSS for a custom state such as Navigation Link's `-current`, including its nested pseudo states and inside a viewport. The rules scope the block-instance selector to the selector the block declares in `selectors.states`, wrapped in `:is()`, so feature selectors keep the state on the block's wrapper, as in `.wp-elements-1:is(.wp-block-navigation .current-menu-item) > a`. Custom state keys are no longer treated as root declarations, and the canvas preview forces a selected custom state onto the block, layering `-current` and `-current.:hover` when both are selected. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Renders per-block CSS for a custom state such as Navigation Link's `-current`, including its nested pseudo states and inside a viewport, mirroring the editor. The rules scope the block's `wp-states-*` class to the selector the block declares in `selectors.states`, wrapped in `:is()`, so a feature selector keeps the state on the block's wrapper. Custom state keys are no longer treated as root declarations. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds `customState` to the private `resolveStyle` context and passes the selected block style state's `custom` value through, so the block inspector shows the Global Styles values a block inherits in a custom state such as Navigation Link's `-current`. The state path order is viewport, custom, pseudo, matching the block-editor state helpers. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offers Navigation Link's `-current` state as "Current" in an Item group of the block card's State dropdown, when the block declares a selector for it, and shows it in the state badges. A selected custom state counts as a style state being edited, can be previewed on the canvas, and hides the layout and position controls the same way a pseudo state does. The Custom CSS panel is hidden for custom states because their custom CSS isn't output yet. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offers Navigation Link's `-current` state as "Current" in an Item group of the block screen's States dropdown, so the current menu item can be styled in Global Styles. The state path order is viewport, custom, pseudo, as in the block editor. theme.json only accepts a custom state at the top level of a block's styles, so Current isn't offered for a viewport or a block style variation, or when the block declares no selector for it. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 PR meta 🤖🎉 PropsUnlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @itsdavidmorgan, @le-sgs. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. 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. Updated as activity occurs, without notifying anyone named here. Add the 📦 Bundle sizeSize Change: +728 B (+0.01%) Total Size: 8.96 MB 📦 View Changed
⚡ PerformanceShow the resultsClient side metrics exclude the server response time. front-end-block-theme
front-end-classic-theme
media-processing
media-upload
post-editor
site-editor
🏁 Flaky testsSome tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information. Regression: updating a specific option will only change its value and will not corrupt others in
|
Offers Navigation Link's `-current` state as "Current" in an Item group of the block card's State dropdown, when the block declares a selector for it, and shows it in the state badges. A selected custom state counts as a style state being edited, can be previewed on the canvas, and hides the layout and position controls the same way a pseudo state does. The Custom CSS panel is hidden for custom states because their custom CSS isn't output yet. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offers Navigation Link's `-current` state as "Current" in an Item group of the block screen's States dropdown, so the current menu item can be styled in Global Styles. The state path order is viewport, custom, pseudo, as in the block editor. theme.json only accepts a custom state at the top level of a block's styles, so Current isn't offered for a viewport or a block style variation, or when the block declares no selector for it. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
965ce13 to
f24e802
Compare
Links WordPress/wordpress-develop#14285, which backports the custom state output in `lib/block-supports/states.php`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mikachan
left a comment
There was a problem hiding this comment.
Thanks for this! Scoping the instance class with :is() is a nice approach. The block's selector and the theme.json output are staying the same, the PHP and JS sides build the scoped selector the same way, and the scope goes into the wp-states-* hash. Also, limiting Global Styles to the default viewport and no style variations makes sense based on what theme.json accepts.
Most of my inline comments are about how styles stack when Current is combined with Hover, I don't think there's anything major here.
| * @param {Object} selectedState Selected block style state. | ||
| * @return {Object|undefined} Style value for the canvas preview. | ||
| */ | ||
| export function getCanvasStateStyleValue( style, selectedState ) { |
There was a problem hiding this comment.
On the frontend, a current link being hovered matches three rules:
| Rule | Specificity |
|---|---|
.wp-states-x:hover (Hover) |
0,2,0 |
.wp-states-x:is(.wp-block-navigation .current-menu-item) (Current) |
0,3,0 |
.wp-states-x:is(…):hover (Current + Hover) |
0,4,0 |
For Current + Hover, this preview merges only -current and -current.:hover. A plain :hover is left out, so the canvas and the frontend can be different. For example, if the following is set:
- Hover: underline
- Current: red text
- Current + Hover: bold
Hovering the current link on the frontend gives underline, red and bold. The Current + Hover preview shows red and bold, with no underline.
Could the plain pseudo state go in at the bottom, so the merge follows the frontend's specificity (Hover, then Current, then Current + Hover)? Something like:
states = [
{ ...selectedState, custom: DEFAULT_BLOCK_STYLE_STATE.custom },
{ ...selectedState, pseudo: DEFAULT_BLOCK_STYLE_STATE.pseudo },
...states,
];
There was a problem hiding this comment.
thanks for the suggestion! I did this on fad31bb
| ]; | ||
|
|
||
| // For a non-default state (pseudo and/or viewport), layer each layer's | ||
| // For a non-default state (viewport, custom and/or pseudo), layer each layer's |
There was a problem hiding this comment.
With Current + Hover selected, each layer adds only its ['-current'][':hover'] slice on top of the base, because getStateSlice() (line 300) reads the exact path. The -current and plain :hover slices are never added. So if Global Styles sets a Current font size, the panel won't show it as inherited under Current + Hover, even though the frontend applies it.
This should probably follow the same order as the canvas preview in getCanvasStateStyleValue(): Hover, then Current, then Current + Hover, each above the base layers.
There was a problem hiding this comment.
Addressed on 1b49627. Also added a unit test for the new helper function
| await expect( currentItem ).toHaveClass( /wp-states-/ ); | ||
| // The default "Large" font size preset is 36px. | ||
| await expect( currentItem ).toHaveCSS( 'font-size', '36px' ); | ||
| await expect( otherItem ).not.toHaveCSS( 'font-size', '36px' ); |
There was a problem hiding this comment.
This negative assertion can't fail. otherItem has no block styles, so it would fail the 36px check even without the :is() scope. We would need to test that the styled link loses the style when it isn't current.
Could you add a check that visits another page that shows the same menu (or the front page) and asserts that "Current Link" isn't 36px there?
Adds a custom state group, labeled "Item", between the Viewport and Pseudo state groups of StateControl, and teaches StateControlBadges to show and summarize the selected custom state. The three groups now share one StateMenuGroup renderer. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds `custom` to the selected block style state, next to `viewport` and `pseudo`, so the store and the style path helpers can address a custom state such as Navigation Link's `-current`. The style path order is viewport, custom, pseudo, so Mobile, Current and Hover are stored at `style['@mobile']['-current'][':hover']`. `hasSelectedBlockStyleState` now also returns true for a selected custom state. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generates per-block CSS for a custom state such as Navigation Link's `-current`, including its nested pseudo states and inside a viewport. The rules scope the block-instance selector to the selector the block declares in `selectors.states`, wrapped in `:is()`, so feature selectors keep the state on the block's wrapper, as in `.wp-elements-1:is(.wp-block-navigation .current-menu-item) > a`. Custom state keys are no longer treated as root declarations, and the canvas preview forces a selected custom state onto the block, layering `-current` and `-current.:hover` when both are selected. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Renders per-block CSS for a custom state such as Navigation Link's `-current`, including its nested pseudo states and inside a viewport, mirroring the editor. The rules scope the block's `wp-states-*` class to the selector the block declares in `selectors.states`, wrapped in `:is()`, so a feature selector keeps the state on the block's wrapper. Custom state keys are no longer treated as root declarations. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds `customState` to the private `resolveStyle` context and passes the selected block style state's `custom` value through, so the block inspector shows the Global Styles values a block inherits in a custom state such as Navigation Link's `-current`. The state path order is viewport, custom, pseudo, matching the block-editor state helpers. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offers Navigation Link's `-current` state as "Current" in an Item group of the block card's State dropdown, when the block declares a selector for it, and shows it in the state badges. A selected custom state counts as a style state being edited, can be previewed on the canvas, and hides the layout and position controls the same way a pseudo state does. The Custom CSS panel is hidden for custom states because their custom CSS isn't output yet. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Offers Navigation Link's `-current` state as "Current" in an Item group of the block screen's States dropdown, so the current menu item can be styled in Global Styles. The state path order is viewport, custom, pseudo, as in the block editor. theme.json only accepts a custom state at the top level of a block's styles, so Current isn't offered for a viewport or a block style variation, or when the block declares no selector for it. Ported from #78769. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Links WordPress/wordpress-develop#14285, which backports the custom state output in `lib/block-supports/states.php`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When Current and Hover are both selected, the canvas preview now merges the plain `:hover` styles first, then `-current`, then `-current.:hover`. This follows the front end's specificity, so a hovered current link looks the same on the canvas as it does on the site. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Current and Hover both selected, each layer now adds its `:hover`, `-current` and `-current.:hover` slices in that order, instead of only `-current.:hover`. A Current font size set in Global Styles now shows as inherited under Current + Hover, as it applies on the front end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous negative check used a link without block styles, so it couldn't fail. The test now visits the post that shows the same menu, where the styled link isn't the current menu item, and checks its Current font size doesn't apply there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c34fe5d to
13d3197
Compare

What?
Closes #38277. Part of #79283.
You can now style the current menu item of a Navigation Link, in Global Styles and per block. Pick Current in the new Item group of the States dropdown, next to Viewport and Pseudo state.
Why?
Styling the current menu item meant writing theme.json or custom CSS by hand. theme.json already supports
-currentfor Navigation Link, but there was no UI for it.How?
-current, wrapped in:is():.wp-states-1a2b3c4d:is(.wp-block-navigation .current-menu-item). The block's selector and the theme.json output don't change.-currentinside either. Per-block styles can combine Current with a viewport.Testing Instructions
Testing Instructions for Keyboard
Screenshots or screencast
Global Styles:
Block inspector, with the Current state previewed on the canvas:
Front end, where About is the current menu item:
Use of AI Tools
Ported to trunk with Claude Code (Opus 5.5). All output reviewed and validated by the author.
🤖 Generated with Claude Code