Global Styles: Replace the inherited-value underline with an indicator button - #80506
Global Styles: Replace the inherited-value underline with an indicator button#80506t-hamano wants to merge 6 commits into
Conversation
…ndicator button The dotted underline on control labels had no tooltip, and could not gain one: making an interactive cue out of a label costs the label its own focus behaviour, and hover-only affordances do not work on touch. Replace it with a single small button rendered next to the control, in the slot the local-override reset dot already occupied: - Inheriting: a rhombus in the synced color, with an "Inherited from Global Styles" tooltip. `disabled` + `accessibleWhenDisabled`, so it carries no action but stays focusable and reachable by keyboard. - Locally overridden: the existing blue dot, flattening into a reset dash on hover/focus, with a "Reset to inherited value" tooltip. It is deliberately one button across both states. Resetting an override leaves the control inheriting again, so a second button taking over the slot would unmount the one the user just activated and drop focus. For the same reason the reset paths that used to hand focus back to the dropdown toggle no longer do — only the plain reset button, which really does unmount, still moves focus. Rename `showLocalOverrideActionsInLabel` to `showInheritanceAffordance` now that it gates both states. Co-Authored-By: Claude <noreply@anthropic.com>
The indicator stays focusable while inheriting (`accessibleWhenDisabled`), so it needs a visible focus ring. Recolor the browser default to match the rhombus rather than leaving it on the admin accent, which reads as the local-override state. Also drop a `border-color` declaration that had no border to apply to. Co-Authored-By: Claude <noreply@anthropic.com>
`INHERITED_LABEL` had a single call site, so extracting it only moved the string away from where it is read. Inline it beside its sibling `__( 'Reset to inherited value' )` so both labels are visible together. The "No focus move" comments on the duotone and shadow toggles restated what the `InheritanceIndicatorButton` doc block already explains. Co-Authored-By: Claude <noreply@anthropic.com>
|
Size Change: -607 B (-0.01%) Total Size: 7.75 MB 📦 View Changed
|
|
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. |
|
Followed up with more detail and motivation here, and while I would like us to pursue that more minimal approach, it doesn't necessarily have to happen in this PR. I'll defer to you all on when and how. Thank you! |
The trigger's dimensions were hardcoded at 14px, which is off the base styles grid. Use $grid-unit-20 (16px) so the circle stays aligned with the shared spacing scale. Co-Authored-By: Claude <noreply@anthropic.com>
Design feedback on #77894 asks for one silhouette across the whole inheritance language rather than two unrelated shapes: a diamond, filled when the value is defined at the level you are interacting with, outline when it comes from somewhere else. The same pairing is intended to carry over to synced patterns (source vs. instance) later. Render the diamond as a local SVG child of the indicator button and drop the ::before pseudo-elements that drew the purple rhombus and the blue reset dot. Because fill alone now separates the two states, the symbol uses a single neutral color and no longer tints itself with --wp-block-synced-color. The now-unstyled has-local-override class goes with it. Co-Authored-By: Claude <noreply@anthropic.com>
|
@jasmussen Thanks for the feedback!
The button was actually 14px, but I increased it to 16px. Making it any larger might cause the focus outline to collide with the form fields.
I think you are referring to the font size UI. To align it perfectly, the rightmost button would need to be moved down by 1 or 2 pixels. However, this could potentially cause a conflict with the UI below it, I we haven't made any changes for now.
I gave it a try; what do you think?
|
|
Lovely fast work, good to ship, we can always improve further. I would love to have 24x24 be the minimum, it seems like through negative margins or otherwise, we can bump that further. Fast work! |
|
@jasmussen Thanks for the review! I plan to sync the core and Gutenberg approximately 20 hours from now to prepare for the Beta3 release. However, since there is still some time, I would like to keep this PR open until tomorrow. |
|
Flaky tests detected in e13337b. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/29827352383
|
|
Just a quick drive by comment. This is looking good so far. It might be of interest to check out some of the previous PRs exploring tooltips for displaying source or provenance data for global styles via a "style path breadcrumb". This could be used on the "inherited value" button state here. Either now or in a follow-up.
Hope that helps 🤞 |
|
Thanks for the feedback, Aaron! Since the Beta 3 release is imminent, I'm leaning towards shipping this PR as is for now, but we can consider tooltip improvements in a follow-up. |
|
Thank you everyone for your feedback!
To clarify, this was not introduced by this PR. The purpose of this PR is solely to replace the underline with an indicator button. The important thing is that the Beta 3 commit freeze will begin in about 13 hours. Even earlier, a few hours before that, the synchronization of Core and Gutenberg, and the release of Gutenberg 23.6 will start. We need to decide what to ship by then. For now, I would like to keep this PR on hold. This means that #77894 will ship with Beta3 and Gutenberg 23.6. At least #77894 should have been agreed upon by the release leads to be shipped. |
Perhaps the most cautious approach would be this. At the very least, we can expose a way to communicate the necessary information to the user without introducing exceptions into the design system. |
I believe we should strongly consider this option, especially if we're not fully convinced by the UI changes landed in #77894 and the ones in this PR. |
Maybe I'm not envisioning it correctly. A mockup or demo might be handy. It's not clear to me that it's any better for accessibility, a popover with only information is about equivalent to a tooltip. If it does have focusable elements within, then it needs an opener element that can be used to transfer focus, which means still having some new UI with each field. |
I'm also interested to learn the real blocker. Design or UX direction/accessibility? I get if we need to revisit the interactions here and really nail them down. For me a design system is rather a means to delivering the user the experience. I'm not convinced "no exceptions" is a goal at all. Rather how can the design system serve the UX. No worries if it can't right now. Is there way it can in the future? |
|
Note that I am considering enabling this UI exclusively on the Gutenberg plugin. #80555 |
|
Good, careful thoughts. Tricky feature. I desire to ship it as much as anyone, but will also trust and defer you all on what's best to do at this point, plugin and 7.2 or if something can land in 7.1. I would like to expand a little on the very valid pushback, and explain why I still think the single button, and diamond shape (even if we might make it bigger), can be a good path forward.
This is very valid. Honestly I feel the same with the underline and dot, however: dashed underline to me says "tooltip", but there is none. And the dot says "there's new stuff here that's important for you to look at". The diamond has been used for synced patterns for a while to indicate, well, symbolism (source and instance), but more importantly, connection. Is that too abstract to pack into a diamond shape? Perhaps. But that is intentional because this feature will touch every single style we have, it will be present in every design control and every style panel. And arguably it is a feature that will be used by theme developers and even then, only occasionally. When reviewing a brand new feature and focusing on just that one thing, it can be hard to remember also: this will be a permanent new resident in the UI, even when you're not using it. It can be hard to connect: is this clear enough, with does this have the right prominence? Underline and unread dot, vs. outline and solid diamond, both are trying to tread that tightrope of clarity and presence with reduced prominence to match what is a very advanced feature that is crucial when you need it, and completely unnecessary when you don't. They are interfaces of discoverability: you interact with them to understand what they do. While I think the diamond shape is strong symbolism for this, and that its low-footprint balances well the "there when you need it, out of mind when you don't", as the designer I'm also not married to it, and I'm prepared to explore a variety of other options. But given the accessibility and design system constraints both of which are important to take seriously, we aren't left with a ton of options. And so far the one that resonates the most with me is one button, shows inherited or local style, and has a tooltip. How big we make it, what we show in it, and where we place it: I think that's the main challenge. |
|
Please note that this UI has been changed to be Gutenberg-exclusive. See #80555 |
|
I've been mulling this problem over (probably too much!). At the root is this: users don't see inherited styles in their settings and struggle to make the connection to the global styles system. A complete full solution would cover the full spectrum of the problem: show inherited styles in settings, indicate when something is inherited and that changing it will "disconnect" it from that, enable a quick option to reset to global, and show when you've overridden the option locally. We don't necessarily need a complete solution in my opinion to land something meaningful for 7.1. Even showing the first part (show inherited styles in settings) without the other two would be a step forward than what's in place today. With that in mind, I want to offer two different ideas to keep the conversation going and to have a chance to react to something visually. Diamond with the ability to reset to inherited style This option shows no indication of something inherited globally but it just reflects the styles you see in the canvas. If you override an inherited setting then a diamond appears to indicate an override (this should be a filled in diamond, sorry!). When you hover over it, you see an option to reset to the inherited styles. This reduces visual noise by not showing an inherited style while still allowing folks to see when they have an override and to reset to global. what.about.this.mp4Diamond with only a tooltip This option also shows no indication of something inherited globally but it just reflects the styles you see in the canvas. If you override an inherited setting then a diamond appears to indicate an override. When you hover over it though, you ONLY see an indication that this local change is overriding global styles (copy is rough on this tooltip). To reset, you rely on the tools panel which we know is accessible and is a current known pathway. This is a more scoped down version of the former item in that it only shows a tooltip but without any ability to reset since that already exists in the tools panel. another.way.mp4 |
|
Nice work, Anne. I appreciate you carefully sweating the details here. I'd say you go in perhaps an even bolder direction than I did: showing only overridden styles, and simply letting the inherited styles be implied by what the control shows. I like this a lot not just because it's less interface to confuse, but because it's less to start with: if we find the need to show more than this, we can always add it at a later time. It is harder to take something away that wasn't necessary after all. Let me describe the two prototypes you have, so that I can verbalise my understanding and you can correct me if I get it wrong.
I like aspects of both, and will come back to my recommendation for next steps at the end. For now I want to elaborate on tooltip and reset functionality. One of the things that @mirka has brought up a few times is: hover, and therefore tooltips, don't work on mobile. So if the tooltip plays an important role, then the button it is attached to should do nothing else than open the tooltip when you tap it (or hover it). That's the reason it can be risky to combine features here, and especially when the diamond shape changes to a minus on hover, that's an experience touch users will not get. As far as what we do next, I'm inspired by your work, and mostly want validation on the accessibility and system side for this. From what I see, this can now be a single button that indicates a local override. Whether it can double as a reset button, my understanding is no, but ultimately I will defer to Lena, Joe, and others. As far as the behavior you landed on: I find it elegant. Inheritance is already shown implicitly: design tools are populated with inherited values, and unless there's a diamond dot, that means they are inherited. It's cleaner than what I made, and also much more visible when you do make an override, that's when the dot appears. To that end, I'd recommend only two changes:
There's also still some vertical alignment issues to work out, but nothing that has to happen in this PR. So to summarise: Anne's second option, IMO, feels like a pretty safe bet to start with, and something we can continue to build on. I'd love thoughts from you to see if that can unblock this. |
|
Thank you for digging into this and for such kind words. I have opened a draft PR for the second option to keep us moving: #80649 There's a strong chance the code is bad (it's AI generated) but it's the UX I want to nail first. |
|
@annezazu's second suggestion has a lot of appeal to me. I like the fact that it uses existing paths and familiar mechanisms for handling resets, rather than introducing something new, and I definitely like that it doesn't depend on a reset mechanism that is destructive and could be triggered without knowledge of the results. Resetting styles may not lose a lot of work, but it could certainly be a hassle. I agree with @jasmussen that it's not a good idea for the single button to act as a reset. It does seem like we end up with a button that has no function other than to show information, which is a little unusual. The button literally has nothing but a name that indicates a state. There isn't really a native HTML pattern for this, and I'm not sure what the best way would be to express it. Logically, it now makes the most sense for screen readers as an |
|
Follow-up issue for proper UI support: #80652 |
|
I feel that the approach being attempted in #80649 is one of the best approaches for the 7.1 release, and I intend to proceed with it today. I would like to close this PR to prioritize #80649. However, I would like to share some examples of implementations in other products as a reference when considering what an ideal UI would look like. Please note that these UIs are not necessarily applicable to global style UIs, and I have not actually used these products myself. WebflowThe orange label indicates inheritance from ancestors, and the blue label indicates local overrides. Clicking the label displays a popup indicating where the value is inherited from.
FramerLabels for controls overridden at specific breakpoints are blue. Clicking on such a label displays a popover that resets the override.
Sketch
There are reset buttons to the right of each control to reset overridden values in Symbol (equivalent to Component in Figma) to their source-inherited styles. A button to reset the entire panel settings is located in the upper right corner.
|
…values Iterates on how block-inspector controls signal that a value locally overrides one inherited from Global Styles (follow-up to WordPress#77894 / WordPress#80506). - Nothing at rest: an inheriting control shows the inherited value with no marker (removes the at-rest dotted-underline label treatment). - On a local override, a small filled diamond appears next to the control with an "Overrides inherited styles" tooltip. - The diamond is a status indicator only and performs no action; clearing a local override relies on the panel's existing reset options. Replaces the interactive InheritanceResetButton with a shared InheritanceOverrideIndicator (a Button hosting a filled-diamond SVG wrapped in a @wordpress/ui Tooltip), used by the standard InheritanceToolsPanelItem controls and the custom color, gradient, shadow, duotone, and background image controls. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thanks @t-hamano Happy to jump in next week to help test, was a bit swamped today. |











What?
Follow up to #77894 — addresses the design feedback in #77894 (comment)
Represents the "inherited from Global Styles" state with a purple rhombus indicator next to the control, instead of a dotted underline on the control label.
Why?
The inherited state and the local-override state were expressed in two different places and two different visual languages. Moving both into a single slot makes the affordance consistent.
How?
InheritanceResetButtonis renamed toInheritanceIndicatorButtonand now renders both states.Testing Instructions
There is no logic change, so what needs testing is essentially the same as #77894. Use the
theme.jsonbelow for a smoke test.theme.json
{ "$schema": "../../schemas/json/theme.json", "version": 3, "settings": { "appearanceTools": true, "typography": { "writingMode": true, "fontFamilies": [ { "slug": "georgia", "name": "Georgia", "fontFamily": "Georgia, 'Times New Roman', serif" }, { "slug": "system", "name": "System", "fontFamily": "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" } ] }, "layout": { "contentSize": "840px", "wideSize": "1100px" } }, "styles": { "blocks": { "core/paragraph": { "color": { "text": "#1e1e1e", "background": "#f6f7f7", "gradient": "linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%)" }, "typography": { "fontFamily": "var:preset|font-family|georgia", "fontSize": "1.125rem", "fontStyle": "italic", "fontWeight": "600", "lineHeight": "1.7", "letterSpacing": "0.01em", "textAlign": "justify", "textColumns": "2", "textDecoration": "underline", "textIndent": "1.5em", "textTransform": "capitalize" }, "spacing": { "margin": { "top": "0", "right": "0", "bottom": "1.5rem", "left": "0" }, "padding": { "top": "0.5rem", "right": "1rem", "bottom": "0.5rem", "left": "1rem" } }, "border": { "color": "#e0e0e0", "style": "solid", "width": "1px", "radius": "4px" }, "elements": { "link": { "color": { "text": "#0073aa" }, ":hover": { "color": { "text": "#005177" } } } } }, "core/group": { "background": { "backgroundImage": { "url": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMDAnIGhlaWdodD0nMjAwJz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9J2cnIHgxPScwJyB5MT0nMCcgeDI9JzEnIHkyPScxJz48c3RvcCBvZmZzZXQ9JzAnIHN0b3AtY29sb3I9JyNjOWU3ZmYnLz48c3RvcCBvZmZzZXQ9JzAuNScgc3RvcC1jb2xvcj0nI2VhZTRmZicvPjxzdG9wIG9mZnNldD0nMScgc3RvcC1jb2xvcj0nI2ZmZDZlOCcvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHdpZHRoPScyMDAnIGhlaWdodD0nMjAwJyBmaWxsPSd1cmwoI2cpJy8+PGNpcmNsZSBjeD0nNjAnIGN5PSc3MCcgcj0nMjgnIGZpbGw9JyNmZmZmZmYnIG9wYWNpdHk9JzAuMzUnLz48Y2lyY2xlIGN4PScxNTAnIGN5PScxNDAnIHI9JzQwJyBmaWxsPScjZmZmZmZmJyBvcGFjaXR5PScwLjI1Jy8+PC9zdmc+" }, "backgroundSize": "cover", "backgroundPosition": "center center", "backgroundRepeat": "no-repeat", "backgroundAttachment": "fixed" }, "color": { "text": "#1e1e1e", "background": "#ffffff", "gradient": "linear-gradient(135deg, #f6f7f7 0%, #e5f0f5 100%)" }, "typography": { "fontFamily": "var:preset|font-family|system", "fontSize": "1rem", "fontStyle": "italic", "fontWeight": "500", "lineHeight": "1.6", "letterSpacing": "0.02em", "textDecoration": "underline", "textTransform": "uppercase" }, "spacing": { "blockGap": "1.5rem", "margin": { "top": "2rem", "bottom": "2rem" }, "padding": { "top": "2rem", "right": "2rem", "bottom": "2rem", "left": "2rem" } }, "dimensions": { "minHeight": "120px", "minWidth": "200px" }, "border": { "color": "#d5d5d5", "style": "solid", "width": "1px", "radius": "8px" }, "elements": { "heading": { "color": { "text": "#0a1e2e" }, "typography": { "fontWeight": "700", "lineHeight": "1.2" } }, "button": { "color": { "text": "#ffffff", "background": "#0073aa" }, "border": { "radius": "4px" }, ":hover": { "color": { "background": "#005177" } } }, "link": { "color": { "text": "#0073aa" }, ":hover": { "color": { "text": "#005177" } } } } } } }, "customTemplates": [ { "name": "custom-template", "title": "Custom", "postTypes": [ "post" ] } ], "patterns": [ "short-text-surrounded-by-round-images", "partner-logos" ] }Testing Instructions for Keyboard
Screenshots or screencast
Use of AI Tools
Claude Code was used for part of the implementation, the unit test updates, and drafting this description. All changes were reviewed and manually tested by the author.