Skip to content

Block Styles: Display inherited Global Styles and own Block Style Variation styles in Block Inspector #77595

Description

@aaronrobertshaw

Overall Goal

A block's inspector controls should display values inherited from Global Styles (theme.json defaults, user Global Styles edits, or the block's own style variation) as placeholder values when no local override is set. Today these inputs read empty when no local override is set, even though the block renders on canvas using a typography size, a link colour, or padding coming from the theme. Authors can see that a value is inherited (the field is empty), but they can't see what the inherited value is without opening DevTools or editing the field to reveal what comes back.

This iteration supersedes #64670 (@ramonjd's prior implementation tracking issue, now closed in favour of this one) and continues work surfaced in #37752 (user-facing request, updated August 2024 with per-control mockups from @jameskoster), #49278 (@annezazu's source-visualization design spec), and #43082 ("what does unset mean?").

Status

A proof-of-concept implementation is open in #77894 (draft). It demonstrates the inherited-value UX across typography, dimensions, colour, border, filters, and background controls so the direction can be reviewed before committing to broader component API work.

This work is stacked on #77279. That PR relocates the root-level colour controls out of the catch-all Color panel and into the panels they belong with (Text → Typography, Background + Gradient → Background, element-level colours → a new Elements panel). The inheritance display threads through those relocated panels, so #77894 is based on #77279 and should land after it.

Scope has grown since the first draft of this issue. Several source-attribution affordances originally listed as out of scope are now being prototyped inside the same PoC rather than split into follow-ups (see Scope below for the revised split):

  • a local-override indicator on controls whose value overrides an inherited one;
  • a "Reset to inherited value" action for those local overrides;
  • a "Push to Global Styles" action for an individual local override;
  • a source-layer breadcrumb in the inherited tooltip, attributing the value to the layer it resolved from — bounded to what the Global Styles data itself expresses (root / element / block-default / own-variation), not sources that can't be determined from that data (see Out of scope).

Known PoC tradeoffs (from #77894, to be resolved before merge): the override dot and inherited tooltip are currently portaled into existing control labels with createPortal, and some DOM-selector coupling is used to locate label elements. These are deliberately pragmatic for the PoC and avoid public API changes to ToolsPanelItem / BaseControl / colour + picker controls while the direction is evaluated. If the direction proceeds, a cleaner public component API for label adornments / inherited-state indicators is likely warranted.

Scope for This Release

In scope.

  • Built on the relocated control layout from Block Supports: Relocate text and bg color controls to Typography and Background panels #77279: inherited values are surfaced through the Typography, Background, and Elements panels rather than the former single Color panel.
  • Four inheritance layers: root, element, block-default, own-variation.
  • Inspector controls that can meaningfully render an inherited value: text / number / unit inputs, preset selects, ranges, colour swatches, BoxControl, BorderBoxControl, toggles.
  • Inherited value rendered as the control's at-rest placeholder/selection when no local override is set; inherited placeholders are not committed as local block styles until the author explicitly chooses them.
  • Single-undo reset: clearing a local override, its preset class, and any style-path reference in one atomic attribute update.
  • Correctness across the four preset serialization forms (slug string, var:preset|..., var(--wp--preset--*), raw value), and preservation of valid falsy values such as 0.
  • Local-override affordances (moved in from the previous out-of-scope list, now prototyped in Block Editor: Reflect inherited Global Styles values in block inspector controls #77894): an indicator marking a control as a local override of an inherited value, a "Reset to inherited value" action, and a per-control "Push to Global Styles" action for an individual override.
  • Source-layer breadcrumb in the inherited tooltip, attributing a value to the layer it resolved from — limited to what the Global Styles data expresses (root / element / block-default / own-variation).

Out of scope (tracked separately; planned follow-ups):

  • Source attribution beyond the Global Styles data. The inherited tooltip already shows a breadcrumb for the layer a value resolved from within the Global Styles data (root / element / block-default / own-variation). What it does not attribute are sources that can't be determined from that data: nested / ancestor block style variations, values arriving via the DOM cascade, plugin-injected CSS, or theme.json's free-form css. Deeper attribution and the final tooltip/popover treatment are coordinated on Styles: show a hierarchy of styles clearly (theme, user, global unspecific, global specific, local, etc) #49278.
  • Ancestor-variation resolution, CSS-property inheritance through the DOM cascade, plugin-injected CSS, theme.json's free-form css escape hatch.
  • Multi-select inspector state, native editor, pattern overrides, block bindings as an inheritance source.
  • Cross-block inheritance (e.g. "Content inherits from Paragraph's Global Styles").
  • Unsetting global styles (tracked at Design tools: Better handling of unset / inherited values #43082).

Carried over from #64670. The scope constraint and the resolution hierarchy @ramonjd brainstormed there carry forward verbatim (see Approach). Nothing shipped from #64670's attempted PRs: #55952 closed unmerged, and #59929 closed unmerged too, but its settings[globalStylesDataKey] pattern has since landed via #61556 and is what this iteration reads from.

Outcome. The inspector stops hiding the inherited value. Authors can see at a glance what a block will render even when they haven't set anything locally, and can tell a local override apart from an inherited value. Delivered on top of #77279; currently a proof of concept (#77894) pending design and technical review. No experiment flag. No staged rollout.

Approach

A block's inspector panel receives two value objects:

  1. Local value. The block's attributes today, unchanged.
  2. Inherited value. The same shape, populated from merged Global Styles contributed by root defaults, the block's element tag (e.g. h2 for a level-2 Heading), the block-default layer, and the block's own style variation.

When a control has no local value, it renders its placeholder from the corresponding path in the inherited value. When a local value is present, the control renders as it does today and is marked as a local override. Reset clears the local value in a single undo step; the control then renders the placeholder again.

The inherited value is built by a pure resolver over the merged Global Styles payload already available to block-editor via settings[ globalStylesDataKey ], exposed to panels through an InheritedValueProvider / useInheritedValue surface. Resolution walks the hierarchy from deeper / more specific to broader / less specific (matching the brainstorm in #64670):

  1. mergedStyles[ styleProperty ]
  2. mergedStyles.elements[ elementName ][ styleProperty ]
  3. mergedStyles.blocks[ blockName ][ styleProperty ]
  4. mergedStyles.blocks[ blockName ].variations[ variationName ][ elementName ][ styleProperty ]
  5. mergedStyles.blocks[ blockName ].variations[ variationName ][ styleProperty ]

No getComputedStyle calls, no new REST calls, no @wordpress/core-data imports in block-editor.

Tasks

Consolidated in #77894 (stacked on #77279). Items marked done are implemented in the PoC and pending review.

  • Implement the inherited-value engine as a pure resolver over merged global styles settings.
  • Ensure preset values (colours, font sizes, spacing presets) parse correctly via getValueFromVariable from @wordpress/global-styles-engine.
  • Ensure dynamic values (theme.json "ref" pointers and relative paths) resolve via the existing helper in packages/block-editor/src/hooks/block-style-variation.js.
  • Thread the inherited value through the relocated panels (typography, dimensions, colour, border, filters, background) via InheritedValueProvider.
  • Migrate block controls to render placeholders from the inherited value across the archetype set listed in Scope.
  • Local-override indicator + "Reset to inherited value" action.
  • "Push to Global Styles" action for an individual local override.
  • Source-layer breadcrumb in the inherited tooltip (bounded to the Global Styles data layers).
  • Replace the PoC createPortal label adornments / DOM-selector coupling with a cleaner component API, if the direction proceeds.
  • Visual treatment: placeholder uses a non-colour-only secondary signal satisfying WCAG AA.
  • Preset round-trip tests for all four serialization forms.
  • Design + accessibility review on the placeholder treatment. Coordinate via Styles: show a hierarchy of styles clearly (theme, user, global unspecific, global specific, local, etc) #49278.

Merge requirements

The feature PR must meet all of the following before merge:

  • Lands on top of Block Supports: Relocate text and bg color controls to Typography and Background panels #77279 (rebased onto its relocated-panel layout).
  • The PoC createPortal / DOM-selector affordances are replaced with a sustainable component API, or explicitly accepted by reviewers.
  • Zero getComputedStyle calls introduced in feature code.
  • Zero @wordpress/core-data imports introduced in block-editor.
  • hasValue() ToolsPanel semantics unchanged for third-party consumers.
  • WCAG AA contrast for the placeholder treatment on light editor chrome, dark site-editor chrome, and common swatch backgrounds.
  • Inspector-paint p95 regression ≤ 5% on representative 50-block and 200-block fixtures.
  • All four preset serialization forms round-trip correctly.

Nice to Have

  • Storybook coverage for each covered control archetype in its placeholder-at-rest / focused / committed / reset states, as a lasting visual-regression surface.
  • A reusable theme.json fixture committed under test/performance/ exercising the 50-block and 200-block inspector-paint benchmark (built ad-hoc during Phase 0; worth promoting).

Open Questions & Decisions required

Contributors

Metadata

Metadata

Labels

Global StylesAnything related to the broader Global Styles efforts, including Styles Engine and theme.json[Feature] Design ToolsTools that impact the appearance of blocks both to expand the number of tools and improve the experi[Package] Block editor/packages/block-editor[Type] IterationScoped iteration of an effort from a tracking issue or overview issue ideally for a major release.

Fields

No fields configured for Enhancement.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions