Repository navigation
Global Styles: explore a cascade popover for inherited-value overrides - #80993
JosVelasco wants to merge 1 commit into
Conversation
Exploration, not proposed for merge as-is. Builds on the override-indicator work in WordPress#80649 and stays behind the `gutenberg-global-styles-inheritance-ui` experiment added in WordPress#80815. Where WordPress#80649 marks an override with a diamond and an "Overrides inherited styles" tooltip, this makes the indicator a way in: activating it opens a popover showing the property's whole cascade — every layer that contributed, the losing ones struck through — with a Clear action beside the value it would restore. Engine: `resolveStyle` retains the full per-path cascade rather than only the winning layer. The ordered `contributions` array was already built and then collapsed during the merge; this records what it discarded. `sources` is unchanged, so nothing downstream is affected. Layers carry structural metadata (block name, element, variation) instead of a pre-built string, so the UI composes and translates the labels. UI: origin labels describe scope rather than structure — "Site-wide", "All Paragraph blocks", "Subtitle style", "Just this block", "Custom CSS". Two things the cascade alone could not explain: - A block's own custom CSS overrides a property without touching its style path, so it appears as its own cascade layer rather than as an unexplained discrepancy. - Everything outside the Global Styles data model (theme stylesheets, plugin styles, Customizer CSS) is invisible to the engine. The popover compares the winning value against the element's computed style and says so when they disagree, rather than asserting a winner the page contradicts. Also drops the at-rest dotted-underline treatment, matching the "nothing at rest, something on override" direction settled in WordPress#80649, and adopts the review feedback there: a larger diamond (the 6px square read as a dot) and accessible names that identify the property.
|
Thanks for continuing this, and thanks for the tag 🙏 To contextualise a little bit my previous comments, they are softly felt, and in fact I'd personally also be fine with—to start—landing with nothing but the actual inheritance shown as values: no indicators at all. We might find that's enough. And zooming out a bit, one of the primary challenges with the previous implementations was where to put this indicator so that it could be accessible on mobile with a tap as well: given the mix of controls with labels and ItemGroup controls with flyouts, that was the primary challenge. Some of the concepts discussed late in the thread suggest moving away from that dot indicator and more towards an element that can live inside the ellipsis dropdown for style control panels: there could be something there too. I'm subscribed and listening in here, rather than offer too much guidance at this point, so tagging also: @WordPress/gutenberg-design, @richtabor, @annezazu. |
|
Thanks for kicking this off and driving forward this area of work! I like the idea of surfacing both the global option and showing an obvious reset path (I'd name that "Reset" rather than "Clear) in the pop over. It does feel like we need to embrace a bigger footprint in terms of the infotip pattern and I like the idea of showing what's global so folks know what they are resetting (rather than resetting without knowing the global option). At the same time, Joen points to the visual clutter this current approach can create and we don't have a great solution. I want to give folks time in #80652 to determine a solid path. |
|
@annezazu @jasmussen — opened a separate exploration in #81044, mainly for design input rather than review. It takes the inheritance out of the control rows entirely — nothing at rest — and puts it in each style panel's options (ellipsis) menu instead. Every control overriding an inherited value shows the layers behind it under that control's own row, beside the Scope, since it's a prototype: it's wired through the shared Global Styles panels — Typography, Dimensions, Border & Shadow, Background, Filters, and Color/Elements — so it applies to whichever blocks use those rather than to a particular list. The demo is a Paragraph. It does not cover block-specific settings panels, or the Site Editor's Global Styles sidebar — the layers resolve from the selected block, so it's block inspector only. @mirka — flagging it for #80652 rather than for review: to render anything under a menu item it needs a new Not asking for review time while #80652 is open — just leaving it somewhere it can be looked at. |
Important note
This is an exploration, opened as a draft to keep the conversation going. It is not proposed for merge as-is. It builds directly on the discussion in #80649 and is meant as something concrete to react to — take it over, cherry-pick from it, or close it.
What?
Follow-up exploration to #80649. That PR settles on nothing at rest, a diamond on override, with the diamond as a status indicator that performs no action and shows "Overrides inherited styles" on hover.
This keeps that model and asks what happens if the indicator becomes a way in rather than a dead end. Activating it opens a popover showing the property's whole cascade — every layer that contributed, the losing ones struck through — with a Clear action beside the value it would restore.
Everything stays behind the
gutenberg-global-styles-inheritance-uiexperiment added in #80815.styles.mp4
Why?
Two threads from #80649 pointed here.
@mirka, on why the infotip pattern was chosen over a plain tooltip:
@t-hamano, on a problem with the status-only indicator:
with two options offered: add a reset action to the indicator, or restore the reset button beside it. This explores the first, which he noted "deviates from the original purpose of this PR" — hence a separate draft rather than a push to that branch. Note @jasmussen preferred restoring the button, so this takes a side in a live disagreement.
Beyond that, a one-bit marker can say overridden but not what was overridden, what the value would fall back to, or why the control disagrees with the canvas. Those are the questions that send people to the browser inspector.
How?
Engine.
resolveStyleretains the full per-path cascade rather than only the winning layer. The orderedcontributionsarray was already built and then collapsed during the merge; this records what it discarded, ascascade: Record<path, CascadeEntry[]>.sourcesis unchanged, so nothing downstream is affected. Layers carry structural metadata (block name, element, variation) rather than a pre-built string, so the UI composes and translates the labels — the engine stays i18n-agnostic.Labels describe scope rather than structure, so reading top to bottom narrows: Site-wide → All Paragraph blocks → Subtitle style → Just this block → Custom CSS.
Two things the cascade alone could not explain:
style.css(top-level declarations only; nested selectors target other states).cssoutright viaTREE_STRUCTURAL_KEYS. Rather than assert a winner the page contradicts, the popover compares the winning value against the element's computed style and says so when they disagree.Also adopted from the #80649 review:
Testing Instructions
font-size: 7rem !importantunder Advanced → Custom CSS. It appears as its own layer, and the diamond shows even when custom CSS is the only override.Known gaps
Stated plainly, since this is a draft:
clamp(), and custom CSS bypasses that pipeline entirely. Both produced false positives before being handled. The property allowlist excludes the obviously messy cases but has not been audited property by property;spacing.*andborder.*in particular are unverified.getByRole('button', { name: /Color/ })ambiguous, since it now matches both the control and its indicator. One existing test needed anchoring. Any contributor querying controls by property name will hit this.opacity: 0at rest), inherited from when that slot held a reset button. Every other property signals its override at rest; colour does not.titleattribute used for full values is not keyboard or touch accessible; it wants a realTooltip.CHANGELOG.mdentries, deliberately — this is not proposed for merge in its current shape.AI disclosure