Skip to content

Block Editor: Reflect inherited Global Styles values in block inspector controls - #77894

Merged
t-hamano merged 48 commits into
trunkfrom
update/global-styles-in-inspector-controls
Jul 20, 2026
Merged

Block Editor: Reflect inherited Global Styles values in block inspector controls#77894
t-hamano merged 48 commits into
trunkfrom
update/global-styles-in-inspector-controls

Conversation

@aaronrobertshaw

@aaronrobertshaw aaronrobertshaw commented May 3, 2026

Copy link
Copy Markdown
Contributor

Closes: #77595

What?

Inspector controls now reflect the Global Styles values a block inherits, so the panels line up closer to what you actually see on the block in the canvas.

  • No local value with inherited value:
    • the control shows the inherited value at rest via a placeholder, preselected option, or resolved value, depending on the control
    • it will have a dotted underline on the label to mark it as inherited
  • Local value over an inherited one:
    • an override indicator appears on the label as a small blue dot
    • the blue dot turns into a Reset to inherited value button on hover/focus
    • clicking the blue dot reset button clears the local override (a single undo) to fall back to the inherited value
  • No inherited value:
    • no override indicator will show, it only appears when there's an inherited value to override
    • normal control and panel reset behaviour has been kept

Inherited global styles values only come from the merged Global Styles data:

  • theme.json
  • user global styles edits
  • the block type global styles
  • a block style variation applied directly to the block

There are no computed styles involved, so custom CSS, enqueued stylesheets, and ancestor variations still can't be reflected even though they paint the canvas.

Scoped to an MVP

To land the core of this in 7.1 without first having to extend heavily-used @wordpress/components (like BaseControl) to accept injected label nodes and settle the related design and portal questions that come with it. Two things are deliberately out of scope here and moved to follow-ups:

  • The portalled label adornment and the "inherited from…" path tooltip. Marking where a value is inherited from needs an adornment in side the control label, but neither BaseControl nor InputControl exposes one today. Rather than push that public-API change through now, this PR keeps only the dotted underline at rest and moves the portaled adornment + path tooltip to a follow-up, where the approach and design can be settled properly.
  • Pushing an individual block style up to Global Styles. The design direction from the issue (#77595 / #49278) is now its own feature in #79839. This is still hopeful for 7.1, just tracked separately.

Why?

Right now a control only reflects what's set on the block, never what it inherits.

A Paragraph block renders with the theme's colour, size, and padding on the canvas while the controls sit empty, reading as "unset" when they're anything but. You can't tell a local override apart from an inherited value.

It's a long-standing rough edge (#37752, #43082, #49278), tracked under #77595.

How?

  • A single function, buildInheritedValue, merges the value a block inherits in precedence order; theme.json/user defaults ‹ block type ‹ applied block style variation. It's pure: all value resolution ({ ref } envelopes and theme-file image links) happens here.
  • The block support hooks (background, border, dimensions, duotone, typography, elements) read the Global Styles data via useInheritedValue, resolve the cascade for the selected block, and pass the result to the shared inspector panels as a prop.
  • The panels stay presentational. They render the value at rest, mark the control as inherited or overridden, and wire up the reset. They have no store access, so they remain liftable out of block-editor.
  • 0 is preserved, inherited values aren't saved until a user picks them, and reset stays a single undo.
  • The inherited underline and the override dot/reset render inline as label siblings, no portal.
  • The canvas is untouched. Rendering still relies on the normal CSS cascade, so this is display-only.

Architecture

The flow is a straight line from the store to the control:

  1. Block support hooks (background, border, dimensions, duotone, typography, elements) own the wiring for each control group. Through useInheritedValue, they read the merged Global Styles data (and theme-file links) from the store in a single useSelect, then call build InheritedValue to resolve the Root ‹ Block ‹ Variation cascade for the selected block, dropping root-level values that don't actually cascade to it (e.g. a root background).
  2. That resolved set of inherited styles is passed into the shared inspector panels as a prop.
  3. The panels' controls use it to reflect the values currently applied to the block, inherited or local, and to indicate when a value is inherited (and, in the follow-up, show a tooltip with the inheritance breadcrumb).

So store access lives only in the hooks, cascade resolution lives only in the pure function, and the panels stay presentational. No data dependency and liftable out of block-editor.

APIs

  • No new public APIs on this PR or the follow-up. A public API change would only be needed if we had to extend @wordpress/components (e.g. BaseControl) to wrap control labels in tooltips. This PR doesn't need that, and the follow-up avoids it too by portaling the adornment instead. Neither branch touches @wordpress/components.
  • useInheritedValue and buildInheritedValue are internal to block-editor, not re-exported from the package entry or privateApis.
  • The only change to the existing (already private) panels is one optional, backwards-compatible prop, showInheritanceLabelIndicators, which flags whether the inheritance labels and indicators should be shown. The inheritedValue prop is pre-existing.

Notes

Testing Instructions

Inherited values

  1. Select a block styled through Global Styles and open the inspector.
  2. Confirm controls across Typography, Background, Elements, Dimensions, Border, and Filters show the inherited values at rest, each with the dotted-underline label, and that they match the canvas.
  3. Check it holds for both preset values (colour slugs, font-size presets) and raw values.

Local overrides

  1. Set a local value on an inherited control and confirm the blue override dot appears on the label.
  2. Hover or focus the dot: it becomes a Reset to inherited value button. Activate it, the local value clears, the control falls back to the inherited value, and a single undo brings it back.

Edge cases

  1. Confirm a 0 value is treated as a real value, not an empty one.
  2. Confirm a plain local value with no inherited value behind it uses the normal panel reset, with no override dot.

Example theme.json

To help with defining global style values to check different controls the following theme.json might be of use. It is a modified version of the emptytheme theme.json, so applying this there and switching to emptytheme is a quick way to get up and running.

Example 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" ]
}

@aaronrobertshaw aaronrobertshaw self-assigned this May 3, 2026
@github-actions github-actions Bot added [Package] Components /packages/components [Package] Block editor /packages/block-editor labels May 3, 2026
@aaronrobertshaw aaronrobertshaw added [Type] Enhancement A suggestion for improvement. Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json [Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi and removed [Package] Components /packages/components labels May 3, 2026
@github-actions github-actions Bot added the [Package] Components /packages/components label May 3, 2026
@ramonjd

ramonjd commented May 4, 2026

Copy link
Copy Markdown
Member

I've given this a very quick test run and I really like that it's focussed on fixing the most immediate trust problem: the canvas shows a styled block, but the inspector controls say "empty".

The tooltips and reset button make it clear about where the styles come from too

Screenshot 2026-05-04 at 12 18 23 pm Screenshot 2026-05-04 at 12 26 11 pm

Styles can come from anywhere, e.g., computed CSS, cascades, custom CSS, plugin styles etc etc, and it can get messy pretty quickly so in my mind, communicating this block-theme specific relationship (which is the only one we can really control) is important.

One question I had was whether "Inherited from Global Styles" feels helpful enough without saying "element", "block", or "variation". But now I think that's getting into the weeds.

Fundamentally, I want to know if this block is using the global value, what happens when I change it locally (now it is overriding), and how I can reset it back to the default inheritance. So I think you've covered the basic here in my opinion.

Nice work and thanks for getting a POC up for folks to play around with.

@aaronrobertshaw

Copy link
Copy Markdown
Contributor Author

Thanks for the early feedback @ramonjd, appreciate it 🙇

One question I had was whether "Inherited from Global Styles" feels helpful enough without saying "element", "block", or "variation". But now I think that's getting into the weeds.

This is on the cards to investigate but ensuring we can accurately trace where the inheritance comes from and then also pass that data along to the tooltip being injected into the DOM was another. So I left that to follow-up iterations that I'm in the process of exploring.

@p-jackson p-jackson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was playing around with this and noticed how, the underlined/purple text now appears everywhere. It's the default state to have your style inherited from somewhere.

Image

What's good about the bullet icon for overridden styles

Image

is that the default shows nothing special, and it's the override that changes up the UI. This inverts that. Now the default behaviour looks special and the override looks less special. You could just have a tooltip and no visual style change, but then it becomes very hard to discover.

What I'm imagining is that if the hit box for the tooltip was much bigger, like the entire panel was a hitbox, then any hover over the panel would show the detailed info.

Image

This works with a mouse which isn't ideal. The panels which have a click which expands another side panel are better for showing extra info.

Image

I'm sure you have plenty of UI proposals to choose from :)

Comment thread docs/reference-guides/block-api/block-supports.md Outdated
helper returns `{ className, title }` to spread onto the wrapping
`ToolsPanelItem` (or another wrapping element). The class hook drives the
label-level visual treatment described above; the `title` attribute carries
the i18n string `__( 'Inherited from Global Styles' )` for native browser

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tooltip says things other than "Inherited from Global Styles" too, so I think just needs updated to "e.g. Inherited from ..."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I missed going back over all this when I iterated on the PoC adding the style breadcrumbs to the tooltip.

@aaronrobertshaw

aaronrobertshaw commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks so much for the thoughtful review and feedbacjk @p-jackson 🙇

I was playing around with this and noticed how, the underlined/purple text now appears everywhere. It's the default state to have your style inherited from somewhere.

For the controls highlighted, you're right there are values inherited, in this case the theme's theme.json file acting as the base for Global Styles. So the purple and underlined labels are "correct" in that regard.

Whether it is "too much", that is a question I might direct to @jameskoster and his expertise. For now it is inline with the original design issue and recent feedback.

What I'm imagining is that if the hit box for the tooltip was much bigger, like the entire panel was a hitbox, then any hover over the panel would show the detailed info.

Initially, I looked at something like this too but wrapping more than the label meant that the tooltip there swallowed all tooltips on subcomponents which are as or more important to end users. So I deliberately kept the scope for this narrow.

@jameskoster

Copy link
Copy Markdown
Contributor

Perhaps the underline could be more subtle (text-decoration-thickness, text-decoration-color)? With the underline applied we might also try without the purple accent—it's doing the same job as the underline but not as accessibly.

@aaronrobertshaw

Copy link
Copy Markdown
Contributor Author

I've iterated further here and fix a few bugs too.

Here's a quick glimpse:

Screen.Recording.2026-05-08.at.10.57.24.pm.mp4

And here is how controls look without the purple color for reflecting inherited global styles (current PR state):

Screenshot 2026-05-08 at 11 28 40 pm

@aaronrobertshaw
aaronrobertshaw force-pushed the update/global-styles-in-inspector-controls branch from 9bfb3d7 to 9313738 Compare May 8, 2026 15:30
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown

Size Change: +11.6 kB (+0.15%)

Total Size: 7.74 MB

📦 View Changed
Filename Size Change
build/scripts/block-editor/index.min.js 426 kB +4.53 kB (+1.08%)
build/scripts/block-library/index.min.js 354 kB +2.25 kB (+0.64%)
build/scripts/edit-site/index.min.js 304 kB +1.26 kB (+0.42%)
build/scripts/editor/index.min.js 500 kB +1.17 kB (+0.23%)
build/styles/block-editor/style-rtl.css 20 kB +653 B (+3.38%)
build/styles/block-editor/style-rtl.min.css 17 kB +534 B (+3.24%)
build/styles/block-editor/style.css 20 kB +643 B (+3.32%)
build/styles/block-editor/style.min.css 17 kB +536 B (+3.25%)

compressed-size-action

@aaronrobertshaw

Copy link
Copy Markdown
Contributor Author

Quick update:

I'll be away for a little while but I'll address any feedback collected over the next two weeks as well as the failing tests when i'm back.

It is worth keeping in mind this is a proof of concept at this stage and the code and approach hasn't yet been polished to a production ready state. That said, please don't be shy in leaving feedback, especially on the design and UX side of things.

@jameskoster

Copy link
Copy Markdown
Contributor

I think I'd lean toward omitting the purple colorisation (we can easily add it back later if the underline proves inadequate). Not a strong feeling though.

@aaronrobertshaw

Copy link
Copy Markdown
Contributor Author

Thanks for the continued design direction here. I'll address the current feedback in the next couple of days once I wrangle the new wave of conflicts after other recent global styles projects landed.

@aaronrobertshaw

Copy link
Copy Markdown
Contributor Author

It's been a while coming but I think I have this successfully rebased onto trunk. The state-based / responsive styling work that landed on trunk reshaped basically every panel and hook this PR touches, so just about all of the old code was in conflict.

As this PR stands now, the inherited global styles display has been relayed and updated on top of trunk and the state/responsive styles feature. A few notes:

  • Inherited value lookup is now state-aware
  • Didn't touch the per-state CSS rendering
  • Found and fixed a regression on gradient clearing

Unit and e2es are green locally but treat this as an updated PoC. With as much conflict as the previous working version had, this needs proper manual testing to confirm the inherited value UX actually makes sense across every panel and every state.

I'll continue to give this some testing and polish before flagging for review in the near future.

@aaronrobertshaw
aaronrobertshaw force-pushed the update/global-styles-in-inspector-controls branch from 68fda90 to 1f96f54 Compare June 9, 2026 01:38
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

Flaky tests detected in 8cb4172.
Some 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.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/29720648963
📝 Reported issues:

@t-hamano t-hamano added the Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Jul 20, 2026
@github-actions github-actions Bot added this to the Gutenberg 23.7 milestone Jul 20, 2026
@t-hamano

Copy link
Copy Markdown
Contributor

At the very least, let's try this Joen's suggestion as soon as possible. We still have some time until Beta 3.

@github-actions

Copy link
Copy Markdown

There was a conflict while trying to cherry-pick the commit to the wp/7.1 branch. Please resolve the conflict manually and create a PR to the wp/7.1 branch.

PRs to wp/7.1 are similar to PRs to trunk, but you should base your PR on the wp/7.1 branch instead of trunk.

# Checkout the wp/7.1 branch instead of trunk.
git checkout wp/7.1

# Create a new branch for your PR.
git checkout -b my-branch

# Cherry-pick the commit.
git cherry-pick cd89b1aa523677313d7e27ea95aa5d5be5522dff

# Check which files have conflicts.
git status

# Resolve the conflict...
# Add the resolved files to the staging area.
git status
git add .
git cherry-pick --continue

# Push the branch to the repository
git push origin my-branch

# Create a PR and set the base to the wp/7.1 branch.
# See https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request.

@t-hamano

Copy link
Copy Markdown
Contributor

Manual backport PR for wp/7.1; #80481

@t-hamano

Copy link
Copy Markdown
Contributor

This PR was backported to wp/7.1 by #80481.

@t-hamano t-hamano added Backported to WP Core Pull request that has been successfully merged into WP Core and removed Backport to WP 7.1 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta labels Jul 21, 2026
@t-hamano

Copy link
Copy Markdown
Contributor

Now imagine instead of the dashed line, we add another button, icon TBD, purple rhomboid for now:
That button would afford a tooltip on hover or click:
As soon as you make a change, that same button changes from one icon, to the dot we already have:
Same behavior, click to unset:

I tried to implement this new behaviour. #80506

Concurrently, I plan to highlight this feature in the Gutenberg 23.6 release post. The content of the article is written under the assumption that #80506 will be backported to the Gutenberg 23.6 release.

https://make.wordpress.org/core/?p=124627&preview=1&_ppp=87af14b31d#inherited-global-styles-values-in-the-block-inspector

@jasmussen

Copy link
Copy Markdown
Contributor

Following up on yesterday's mockup with an update on the style inheritance symbolism.

We have good tech landed, and a good PR followup ready for impriving it further. That PR uses a diamond symbolism for "inherited" and a dot for "local". It can work and move forward.

The following meditation looks a bit further, and is based on partial discussions I have had with @fcoveram about how to clarify and elevate this further. The goal is to create a shared language to help delineate local/inherited, but also source/instance.

The idea: embrace the diamond as the symbol for both:

symbol

This idea is to be simple in the symbolism:

  • ◆ filled means the value is defined at the level where you are interacting.
  • ◇ outline means the value comes from somewhere else.

This is the distillation of the visual language, and it should translate well to the style context:

i1

I'm deliberately being minimalist here, because I know that Francisco has instincts that can elevate this further. So I'm carving out a space for him to expand upon this. That's the main reason I'm using shape only for now, because I know there are incoming thoughts on enhancing with colour as well. It's a minimalist starting point: one shape, filled or not.

But the perspective of the symbolism can still be considered, and its vernacular where it can get applied:

◆ filled ◇ outline
Styles Local (set on this block) Inherited (from theme, global styles, or an ancestor)
Synced patterns Source (you are editing the origin) Instance (a placed copy)

The vernacular would differentiate the concepts: local/inherited for styles and source/instance for patterns, but the relationship is the same: filled means authored here, outline means reflected from elsewhere.

No action item on patterns today, but the goal is to create a shared language with potent silhouette. One that remains the same whether styled or unstyled, and yet is clearly distinguishable between states. That's the main reason for replacing the dot: inheritance doesn't have to stop here: if a font size is set on a group, a paragraph inside will inherit it. A unified symbol with filled/unfilled state differentiation, ◆/◇, retains the singular language.

To say again, this deliberately does not preclude further improvements, including adding colour. or otherwise. My hope is also it's a small fix we can still ship in 7.1.

Some details:

  • The diamond is inside a 24x24px box.
  • The diamond is a 6x6px square with 1px rounded corners, rotated 45 degrees.
  • The solid is just a fill, the outline has a 1px inner border.

SVG if helpful:

<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="8.46443" y="12" width="5" height="5" rx="0.5" transform="rotate(-45 8.46443 12)" stroke="black"/>
</svg>

To note, I don't think of that SVG as something to add the the icon library, but rather a local component that lives with this tech. Let me know what you think.

t-hamano added a commit that referenced this pull request Jul 21, 2026
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>
@t-hamano

Copy link
Copy Markdown
Contributor

Concurrently, I plan to highlight this feature in the Gutenberg 23.6 release post. The content of the article is written under the assumption that #80506 will be backported to the Gutenberg 23.6 release.

https://make.wordpress.org/core/?p=124627&preview=1&_ppp=87af14b31d#inherited-global-styles-values-in-the-block-inspector

This PR should at least ship with Gutenberg 23.6. However, looking at #80506, I believe there's still room for discussion on how the UI should be handled. For now, I will not highlight this feature in the Gutenberg 23.6 release post.

@t-hamano

t-hamano commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Looking back at the comments on #80506, I'm starting to think that merging this PR might have been premature, considering its impact on the design system and the accessibility issues that need to be addressed.

@annezazu @joedolson, I understand that due to time zone differences, you may not be able to check this comment immediately. However, I'm considering removing all UI related to global style inheritance, leaving only internal APIs, for the Beta 3 and Gutenberg 23.6 releases. Just in case, I will prepare the PR for that now.

andrewserong added a commit that referenced this pull request Jul 22, 2026
…ector controls (#80495)

* Global Styles: resolve per-level heading element styles in inspector controls

Completes the heading-family portion of the element inheritance layer tracked
in #80438 (follow-up to #77894). The block inspector now reflects styles set on
a specific heading level (`styles.elements.h1`-`h6`) in addition to the shared
`heading` element.

Previously the resolver folded in only `styles.elements.heading` for
`core/heading`, so anything written by the per-level tabs in Styles >
Typography > Headings was invisible to inspector controls. Because `fontSize`
is deliberately disabled at the "All headings" level, every Global Styles
heading font size lands in `elements.hN` and never reached a control at all.

- Engine: replace the hardcoded `BLOCK_TO_ROOT_ELEMENT` map with an optional,
  caller-supplied `elements` list on the resolve context, ordered low to high
  precedence so `h2` wins over `heading`. This keeps the engine agnostic of
  block-to-element mappings and removes core block names from the package. The
  element list joins the memo cache key so two heading levels no longer share
  an entry.
- block-editor: map block name + `level` to element keys for the heading family
  (`core/heading`, `site-title`, `post-title`, `query-title`, `comments-title`,
  `term-name`, `site-tagline`). A level of 0 (a block rendered as `<p>`) folds
  no heading layer. `clientId` is read from the block edit context, so
  `useResolvedStyle`'s signature is unchanged and no new store subscription is
  added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Update comment

* Try to fix tests

* Handle accordion heading

* Add a test for the 0 heading level case, tidy up changelog

---------

Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: getdave <get_dave@git.wordpress.org>
gutenbergplugin pushed a commit that referenced this pull request Jul 22, 2026
…ector controls (#80495)

* Global Styles: resolve per-level heading element styles in inspector controls

Completes the heading-family portion of the element inheritance layer tracked
in #80438 (follow-up to #77894). The block inspector now reflects styles set on
a specific heading level (`styles.elements.h1`-`h6`) in addition to the shared
`heading` element.

Previously the resolver folded in only `styles.elements.heading` for
`core/heading`, so anything written by the per-level tabs in Styles >
Typography > Headings was invisible to inspector controls. Because `fontSize`
is deliberately disabled at the "All headings" level, every Global Styles
heading font size lands in `elements.hN` and never reached a control at all.

- Engine: replace the hardcoded `BLOCK_TO_ROOT_ELEMENT` map with an optional,
  caller-supplied `elements` list on the resolve context, ordered low to high
  precedence so `h2` wins over `heading`. This keeps the engine agnostic of
  block-to-element mappings and removes core block names from the package. The
  element list joins the memo cache key so two heading levels no longer share
  an entry.
- block-editor: map block name + `level` to element keys for the heading family
  (`core/heading`, `site-title`, `post-title`, `query-title`, `comments-title`,
  `term-name`, `site-tagline`). A level of 0 (a block rendered as `<p>`) folds
  no heading layer. `clientId` is read from the block edit context, so
  `useResolvedStyle`'s signature is unchanged and no new store subscription is
  added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Update comment

* Try to fix tests

* Handle accordion heading

* Add a test for the 0 heading level case, tidy up changelog

---------

Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: getdave <get_dave@git.wordpress.org>
@ramonjd

ramonjd commented Jul 22, 2026

Copy link
Copy Markdown
Member

Looking back at the comments on #80506, I'm starting to think that merging this PR might have been premature, considering its impact on the design system and the accessibility issues that need to be addressed.
However, I'm considering removing all UI related to global style inheritance, leaving only internal APIs, for the Beta 3 and Gutenberg 23.6 releases. Just in case, I will prepare the PR for that now.

Thanks @t-hamano We are at a limited capacity to work on this, so if you feel the risks are too great then I'd support rolling back the UI.

But I'd want us to do it for the right reasons.

Are we rolling it back to prevent shipping a worse user experience or because of some future risk to the design system? E.g., as far as I can see #80506 affects panels not the underlying components.

I'm not arguing for or against any design and will defer to the release folks here.

If we need more time so that the design system can adapt to the UX, then that's great. Whether it needs to block this feature is another question.

Are the accessibility issues inextricably linked or can they be separated out for now?

@t-hamano

Copy link
Copy Markdown
Contributor

I plan to submit a PR soon, but since there is limited time before the synchronization between Core and Gutenberg, I would like to re-evaluate the strategy.

  • Guard the UI and internal value calculations for inherited global style values ​​using IS_GUTENBERG_PLUGIN. This means the feature will NOT ship in WP 7.1 Beta 3 but will be released in GB 23.6.
  • This ensures that ongoing development is not hindered. If follow-up PRs for this feature are merged, they can also be backported to the wp/7.1 branch safely.
  • If you wish to verify the global style behavior in Core, run IS_GUTENBERG_PLUGIN=false npm run dev
  • Before next week's Beta 4, reconsider whether it is worth shipping this feature in Core. If it is not yet ready, take no action.
  • If we do decide to ship it in Core, remove the IS_GUTENBERG_PLUGIN guard.

Please let me know your opinion!

@ramonjd

ramonjd commented Jul 22, 2026

Copy link
Copy Markdown
Member

Guard the UI and internal value calculations for inherited global style values ​​using IS_GUTENBERG_PLUGIN. This means the feature will NOT ship in WP 7.1 Beta 3 but will be released in GB 23.6.

This seems very reasonable to me. Thanks again @t-hamano Please let us know how we can help.

@andrewserong

Copy link
Copy Markdown
Contributor

This seems very reasonable to me. Thanks again @t-hamano Please let us know how we can help.

+1 — IMO whatever helps make the release process more streamlined for folks! Whether that's adding the guard now and removing if we decide to include for 7.1, or skip the guard to make Beta 3 easier to release and then potentially punt in later phases, I don't mind personally.

@t-hamano

Copy link
Copy Markdown
Contributor

Thank you all for your feedback. The PR is ready. #80555

t-hamano added a commit that referenced this pull request Jul 22, 2026
…lugin (#80555)

* Global Styles: Gate the inherited value treatment on the plugin flag

The inherited Global Styles treatment added in #77894 and #80495 lives in
`block-editor`, which is synced to WordPress Core. Keep it to the Gutenberg
plugin so Core builds keep showing locally-set values alone.

`useResolvedStyle` resolves nothing when the flag is off, so each panel's
`inheritedValue = value` default restores the pre-feature wiring, and the
block-supports panels default `showInheritanceLabelIndicators` to the same
flag, switching off every affordance routed through `getInheritanceProps`.
Both are required: gating only the value leaves `inheritedValue === value`,
which reads as a local override on every locally-set control and renders a
reset dot.

The typography panel's link-color sync reads `inheritedValue` outside that
prop, and collapsing it onto `value` would write a link color where trunk
wrote none, so it falls back to the pre-feature comparison when the flag is
off.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Note why the inheritance gate returns early

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Add the PR number to the changelog entry

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Restore comment on inheritance className gating

The comment documented a bug found in the original implementation: the
layout className must always be passed through, and only the inheritance
treatment is gated on `showInheritanceLabelIndicators`. Dropping it loses
that context since the code itself is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Cover the Core path of ENABLE_GLOBAL_STYLES_INHERITANCE

The unit test environment sets `IS_GUTENBERG_PLUGIN` to true, so every
existing test exercises the plugin branch only. Nothing guarded the Core
behaviour the flag is meant to preserve, which is the behaviour that ships
to WordPress.

Add two files that mock the constant to false. `jest.mock` is file-scoped,
so they cannot live alongside the plugin-path tests.

- `useResolvedStyle` resolves nothing, so the panels fall back to their
  `inheritedValue = value` default and Core shows locally-set values only.
- `TypographyPanel` applies no inherited treatment and no reset dot by
  default, and its link colour sync falls back to comparing the local pair,
  which is what Core did before the inheritance treatment landed.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Compare inherited values in the Core link colour sync

The Core fallback compared `value`, but the check this replaced compared
`inheritedValue`. The two are only the same in the block inspector, where
`useResolvedStyle` resolves nothing and the prop falls back to `value`.

In Global Styles they diverge: `value` is the user config and
`inheritedValue` is the merged config. Comparing `value` there meant a
theme defining a mismatched text and link colour pair would still sync the
link colour, because both sides of the comparison were undefined until the
user set something.

Restore the original comparison, which is correct on both paths.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Guard the Core link colour sync against reading the user config

The existing link colour tests all render without an `inheritedValue`, the
block inspector shape where the prop falls back to `value`. On that path
the two configs are identical, so none of them notice if the comparison
switches from `inheritedValue` to `value`.

Add a case with the divergent Global Styles shape: an empty user config
and a merged config whose text and link colours differ. Reading `value`
there finds undefined on both sides and syncs, which the assertion catches.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
gutenbergplugin pushed a commit that referenced this pull request Jul 22, 2026
…lugin (#80555)

* Global Styles: Gate the inherited value treatment on the plugin flag

The inherited Global Styles treatment added in #77894 and #80495 lives in
`block-editor`, which is synced to WordPress Core. Keep it to the Gutenberg
plugin so Core builds keep showing locally-set values alone.

`useResolvedStyle` resolves nothing when the flag is off, so each panel's
`inheritedValue = value` default restores the pre-feature wiring, and the
block-supports panels default `showInheritanceLabelIndicators` to the same
flag, switching off every affordance routed through `getInheritanceProps`.
Both are required: gating only the value leaves `inheritedValue === value`,
which reads as a local override on every locally-set control and renders a
reset dot.

The typography panel's link-color sync reads `inheritedValue` outside that
prop, and collapsing it onto `value` would write a link color where trunk
wrote none, so it falls back to the pre-feature comparison when the flag is
off.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Note why the inheritance gate returns early

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Add the PR number to the changelog entry

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Restore comment on inheritance className gating

The comment documented a bug found in the original implementation: the
layout className must always be passed through, and only the inheritance
treatment is gated on `showInheritanceLabelIndicators`. Dropping it loses
that context since the code itself is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Cover the Core path of ENABLE_GLOBAL_STYLES_INHERITANCE

The unit test environment sets `IS_GUTENBERG_PLUGIN` to true, so every
existing test exercises the plugin branch only. Nothing guarded the Core
behaviour the flag is meant to preserve, which is the behaviour that ships
to WordPress.

Add two files that mock the constant to false. `jest.mock` is file-scoped,
so they cannot live alongside the plugin-path tests.

- `useResolvedStyle` resolves nothing, so the panels fall back to their
  `inheritedValue = value` default and Core shows locally-set values only.
- `TypographyPanel` applies no inherited treatment and no reset dot by
  default, and its link colour sync falls back to comparing the local pair,
  which is what Core did before the inheritance treatment landed.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Compare inherited values in the Core link colour sync

The Core fallback compared `value`, but the check this replaced compared
`inheritedValue`. The two are only the same in the block inspector, where
`useResolvedStyle` resolves nothing and the prop falls back to `value`.

In Global Styles they diverge: `value` is the user config and
`inheritedValue` is the merged config. Comparing `value` there meant a
theme defining a mismatched text and link colour pair would still sync the
link colour, because both sides of the comparison were undefined until the
user set something.

Restore the original comparison, which is correct on both paths.

Co-Authored-By: Claude <noreply@anthropic.com>

* Global Styles: Guard the Core link colour sync against reading the user config

The existing link colour tests all render without an `inheritedValue`, the
block inspector shape where the prop falls back to `value`. On that path
the two configs are identical, so none of them notice if the comparison
switches from `inheritedValue` to `value`.

Add a case with the divergent Global Styles shape: an empty user config
and a merged config whose text and link colours differ. Reading `value`
there finds undefined on both sides and syncs, which the assertion catches.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
t-hamano pushed a commit that referenced this pull request Jul 22, 2026
…ector controls (#80495)

* Global Styles: resolve per-level heading element styles in inspector controls

Completes the heading-family portion of the element inheritance layer tracked
in #80438 (follow-up to #77894). The block inspector now reflects styles set on
a specific heading level (`styles.elements.h1`-`h6`) in addition to the shared
`heading` element.

Previously the resolver folded in only `styles.elements.heading` for
`core/heading`, so anything written by the per-level tabs in Styles >
Typography > Headings was invisible to inspector controls. Because `fontSize`
is deliberately disabled at the "All headings" level, every Global Styles
heading font size lands in `elements.hN` and never reached a control at all.

- Engine: replace the hardcoded `BLOCK_TO_ROOT_ELEMENT` map with an optional,
  caller-supplied `elements` list on the resolve context, ordered low to high
  precedence so `h2` wins over `heading`. This keeps the engine agnostic of
  block-to-element mappings and removes core block names from the package. The
  element list joins the memo cache key so two heading levels no longer share
  an entry.
- block-editor: map block name + `level` to element keys for the heading family
  (`core/heading`, `site-title`, `post-title`, `query-title`, `comments-title`,
  `term-name`, `site-tagline`). A level of 0 (a block rendered as `<p>`) folds
  no heading layer. `clientId` is read from the block edit context, so
  `useResolvedStyle`'s signature is unchanged and no new store subscription is
  added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Update comment

* Try to fix tests

* Handle accordion heading

* Add a test for the 0 heading level case, tidy up changelog

---------

Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
Co-authored-by: getdave <get_dave@git.wordpress.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backported to WP Core Pull request that has been successfully merged into WP Core [Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi Global Styles Anything related to the broader Global Styles efforts, including Styles Engine and theme.json [Package] Block editor /packages/block-editor [Type] Enhancement A suggestion for improvement.

Projects

Development

Successfully merging this pull request may close these issues.

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