Skip to content

Global Styles: Resolve per-level heading element styles in block inspector controls - #80495

Merged
andrewserong merged 5 commits into
trunkfrom
fix/global-styles-element-layers
Jul 22, 2026
Merged

Global Styles: Resolve per-level heading element styles in block inspector controls#80495
andrewserong merged 5 commits into
trunkfrom
fix/global-styles-element-layers

Conversation

@andrewserong

@andrewserong andrewserong commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Part of:

Follows:

What?

Resolve per-level heading element styles in the block inspector style inheritance logic.

Note: I haven't touched link styles yet as I'm trying to keep this fairly contained while getting up to speed on the feature.

Why?

The was a noted gap in the big global styles inheritance PR and an intended follow-up. It allows Heading blocks (or those that use heading elements) to more accurately reflect inherited colors and styles in the block inspector sidebar, i.e. those values that are set at an individual heading level in global styles (e.g. h2) rather than at the root (heading) level.

How?

Basically, attempt to implement the ideas that Aaron raised in #80438 for adding in this elements layer. Any errors in the approach here are likely mine as I attempt to get up to speed with how this is hooked together. Broadly:

  • Move the logic where we're defining the element layers into block-editor/.../global-styles so that the global-styles-engine accepts elements as a prop (it's up to the caller to specify for the different blocks)
  • Grab the headingLevel from block attributes (via the clientId retrieved from blockEdit context) so that we can grab the right element layers to pass along to resolveStyle — in my mind this is the main "bit" of this PR as it's where we tell the resolver which heading level we're using for this particular block
  • Over in the global styles engine, update the resolveStyle context object to accept an elements array so that the caller can pass in a low-to-high hierarchy of elements used by the block. While this is different to the other props, in that it's an array instead of a single value, I think it has to be an array because for headings we have heading and a specific heading value like h2

Testing Instructions

To be thorough, test with the following core blocks:

  • Button (core/button) — make sure this one works as in trunk

Heading blocks:

  • Heading (core/heading)
  • Post Title (core/post-title)
  • Site Title (core/site-title)
  • Query Title (core/query-title)
  • Comments Title (core/comments-title)
  • Term Name (core/term-name)
  • Site Tagline (core/site-tagline)
  1. In global styles, go to the Typography section > Headings and select a particular heading level (e.g. h2)
  2. Give it some styles that look pretty obvious and save
  3. In the block editor try adding the above blocks and be sure to select the appropriate heading level for them (e.g. h2)
  4. Check that the styles you applied in global styles flow through to the block inspector controls
  5. Note that styles set at the block level in global styles should take precedence over elements styles, so if your theme already sets block-level styles, then you might not see the elements styles
  6. If you go to manually set values at the block level within post content (i.e. in the block inspector) then they should overwrite global styles values just as in the previous big PR that introduced this feature

Screenshots or screencast

Setting some styling rules at the numbered heading level in Global Styles > Headings elements > Typography

image

Then, when you're in a post and you add a heading block of the appropriate level, the controls should pick up that inherited value (they don't on trunk):

2026-07-21.14.49.45.mp4

Use of AI Tools

Claude Code

andrewserong and others added 2 commits July 21, 2026 12:02
…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>
@andrewserong andrewserong self-assigned this Jul 21, 2026
@andrewserong andrewserong added [Type] Enhancement A suggestion for improvement. [Package] Block editor /packages/block-editor 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 labels Jul 21, 2026
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

Size Change: +326 B (0%)

Total Size: 7.75 MB

📦 View Changed
Filename Size Change
build/scripts/block-editor/index.min.js 427 kB +330 B (+0.08%)
build/scripts/block-library/index.min.js 354 kB +3 B (0%)
build/scripts/edit-site/index.min.js 304 kB -14 B (0%)
build/scripts/editor/index.min.js 500 kB +7 B (0%)

compressed-size-action

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

Flaky tests detected in f1a60ff.
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/29889956803
📝 Reported issues:

@andrewserong
andrewserong requested a review from ramonjd July 21, 2026 05:31
@andrewserong
andrewserong marked this pull request as ready for review July 21, 2026 05:33
@andrewserong
andrewserong requested a review from ellatrix as a code owner July 21, 2026 05:33
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

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 props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

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>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@ramonjd ramonjd 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 gave this a smoke test and it's working really well for top level elements. Also checked:

  • buttons
  • custom preset colors
  • hover states inside elements
  • the list of blocks in pr desc

It gets a bit weird when applying styles to elements inside blocks, e.g., Group > Elements > h2. Controls will show the top level value rather than the block's.

Not a huge deal I guess. Could be a follow up.

Thanks for getting this started! 🚀

case 'core/query-title':
case 'core/comments-title':
case 'core/term-name':
case 'core/site-tagline':

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.

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.

I think the accordion one might need a little more work, but I'll double-check tomorrow. Thanks for taking this for a spin!

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.

I got it working for the accordion heading: it needed different handling as it derives its heading level via context rather than it being set on the individual accordion heading (so that all accordion headings in the set of accordion items receive the same heading level). It's a bit hard-coded and possibly smelly, but I tried to keep it neat where I could. Let me know if you think it's too much, and we can roll back and look at it in a follow-up if need be.

I'm a little on the fence about all this hard-coding, but at least it's fairly internal for now, and we could always look at improving it in the future... in any case, here's how it's looking now, inheriting the heading element style:

image

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'm a little on the fence about all this hard-coding, but at least it's fairly internal for now

Nice! I think it's okay. Is there an alternative in code?

My 2c is that this small fix makes headings consistent, and therefore better for the user. That to me is more important.

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.

Nice! I think it's okay. Is there an alternative in code?
My 2c is that this small fix makes headings consistent, and therefore better for the user. That to me is more important.

Same here. I think I was just musing out loud about the idea that at some point we might codify this in block.json in some way and make it a bit more API-ey than hard-coded.

But agreed — for now I think focusing on improving the UX while keeping the code fairly self-contained is likely the right path forward.

I haven't looked at an alternative in code, mostly I wanted to confidence check that we're okay with the hard-coding. It seems we both are for now 😄

// block's own styles. `elements` is ordered low to high precedence, so a
// level-specific `h2` correctly wins over the generic `heading`.
const elementLayers = ( elements ?? [] )
.map( ( elementName ) => styles.elements?.[ elementName ] ?? null )

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.

So we only getting layers from styles.elements so the resolver doesn't know about block's ancestors.

So changes to styles.blocks['core/group'].elements.h2 will show the inherited style it knows about, not the one that is applied.

Image

I suspect it could get rabbit holey. Not sure.

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.

So we only getting layers from styles.elements so the resolver doesn't know about block's ancestors.

Yeah, that's a limitation of the current approach for sure. Not sure how well it'll work to walk up the block hierarchy to see what's there 🤔

Will poke at the idea tomorrow and make a call on whether we should go down the rabbithole now, or defer it to a future voyage!

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.

whether we should go down the rabbithole now, or defer it to a future voyage!

It is looking a bit hairy performing block lookups up the block hierarchy to make sure we're inheriting things correctly. My vote would be to leave blocks-within-blocks behaviour to a follow up and mark it as a task in the linked issue. What do you reckon?

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.

Agree. A follow up sounds best. Thanks for digging in.

@getdave getdave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I manually tested an number of scenarios and this worked as expected.

Noting @ramonjd's feedback about ancestor styles which will need to be resolved either here or in a follow up - will this be tracked somewhere?

As noted elsewhere I think we need some UI explanation about inherited styles other than a dotted line 😅 but I appreciate that's still in the works.

Great work here - this feature is a big win for the Editor 👏

@ramonjd

ramonjd commented Jul 22, 2026

Copy link
Copy Markdown
Member

As noted elsewhere I think we need some UI explanation about inherited styles other than a dotted line

Thanks @getdave. There are things coming out of the sausage factory on this point.

Some suggested visual tweaks to the icons (#77894 (comment) and #80506).

Aaron had a crack at tooltips, but I think that needs more attention in the next cycle as well as figuring out the trade off between UX and the components/design systems.

@andrewserong

Copy link
Copy Markdown
Contributor Author

+1 thanks for taking a look, Dave!

feedback about ancestor styles which will need to be resolved either here or in a follow up - will this be tracked somewhere?

Agreed this should be looked into. My vote is to leave it for a follow-up and for us use the linked issue #80438 to track it. My gut feeling is that for 7.1 we won't have perfect coverage here, and I think the task will be to improve how this works iteratively across small PRs and see at what point the coverage feels "good enough".

Which means, for now, is the coverage in this PR a good step forward, or does it feel too gappy?

@ramonjd ramonjd 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.

  • Top level element values flow through to the inspector controls as advertised
  • Switching heading levels updates the swatches
  • Block-level global styles still win over element styles

As mentioned supporting paths like Group block > elements > h2 would be a nice follow up but I don't think it's a blocker here.

Nice work 🚀

LGTM ✅

### New Features

- Added a private `resolveStyle` API that resolves the merged Global Styles cascade (root, block type, and applied block style variation) for a block, returning the inherited value and a per-leaf source map.
- Added a private `resolveStyle` API that resolves the merged Global Styles cascade (root, root-level element layers, block type, and applied block style variation) for a block, returning the inherited value and a per-leaf source map. Element layers are supplied by the caller as an ordered `elements` list on the resolve context (low to high precedence, e.g. `[ 'heading', 'h2' ]`), keeping the engine agnostic of block-to-element mappings.

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.

Needs a #80495 link I think

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.

Added the PR to a separate changelog entry. I left the existing entries alone just in case it affects branch conflicts when we go to backport this (but I might be overthinking this one 😄)

function mockStores( rawGlobalStyles, blockStyles = [] ) {
function mockStores(
rawGlobalStyles,
{ blockStyles = [], attributes = {} } = {}

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.

Can be later, but you do you think a test with { level: 2 } (or one with { level: 0 }) would catch a future regression in the path between useVariationAndElements and getElementLayers.

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.

We've already got a level: 2 so I've added one with level: 0, good catch!

@andrewserong

Copy link
Copy Markdown
Contributor Author

As mentioned supporting paths like Group block > elements > h2 would be a nice follow up but I don't think it's a blocker here.

Cheers! Yes, let's take a look in a follow-up and I'll jot down some notes in the linked issue.

Thanks for the reviews! I'll merge this in once tests pass

@andrewserong andrewserong 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 22, 2026
@andrewserong andrewserong added [Type] Bug An existing feature does not function as intended and removed [Type] Enhancement A suggestion for improvement. labels Jul 22, 2026
@andrewserong
andrewserong merged commit 76749bf into trunk Jul 22, 2026
55 of 57 checks passed
@andrewserong
andrewserong deleted the fix/global-styles-element-layers branch July 22, 2026 04:40
@github-project-automation github-project-automation Bot moved this from 🔎 Needs Review to ✅ Done in WordPress 7.1 Editor Tasks Jul 22, 2026
@github-actions github-actions Bot added this to the Gutenberg 23.7 milestone Jul 22, 2026
@github-actions github-actions Bot removed 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 22, 2026
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>
@github-actions github-actions Bot added the Backported to WP Core Pull request that has been successfully merged into WP Core label Jul 22, 2026
@github-actions

Copy link
Copy Markdown

I just cherry-picked this PR to the wp/7.1 branch to get it included in the next release: 26b39c0

@t-hamano

Copy link
Copy Markdown
Contributor

Sharing for visibility; I'm considering removing the UI for global style inheritance from WP 7.1 and GB 23.6 releases, although it is not yet finalized. Please refer to #77894 (comment) for details.

@andrewserong

Copy link
Copy Markdown
Contributor Author

Sharing for visibility; I'm considering removing the UI for global style inheritance from WP 7.1 and GB 23.6 releases, although it is not yet finalized. Please refer to #77894 (comment) for details.

Oh, thanks for the heads-up @t-hamano! Do you want me to hold off on working on fixes like this for now? (My main interest is just in helping out with whatever's going to help with the release — whether that's trying to stabilize or fix things, or get out of your way 😄)

@t-hamano t-hamano added the Backport to Gutenberg RC Pull request that needs to be backported to a Gutenberg release candidate (RC) label Jul 22, 2026
@t-hamano

Copy link
Copy Markdown
Contributor

I'm considering introducing the Gutenberg plugin check as I do not want to stop continuous development.

See #77894 (comment)

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>
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>
@t-hamano

Copy link
Copy Markdown
Contributor

I just cherry-picked this PR to the release/23.6 branch to get it included in the next release: 6cd4ef0

@t-hamano t-hamano removed the Backport to Gutenberg RC Pull request that needs to be backported to a Gutenberg release candidate (RC) label Jul 22, 2026
dd32 pushed a commit to dd32/wordpress-develop that referenced this pull request Jul 22, 2026
This updates the pinned commit hash of the Gutenberg repository from `e73c3c481db0650183f092af157f6e42efe9ee2d` to `4997026b75c922d8a6f77a03d72ed7cad04c7073`.

A full list of changes included in this commit can be found on GitHub: 
WordPress/gutenberg@e73c3c4...4997026

- Notes: Replace blur-deselect bookkeeping with useFocusOutside (WordPress/gutenberg#80222)
- Playlist: Update @SInCE tags to 7.1.0 (WordPress/gutenberg#80317)
- fix playlist block Dimensions Design (WordPress/gutenberg#80312)
- UI: Backport compat overlay fixes to WordPress 7.1 (WordPress/gutenberg#80322)
- Editor: allow selecting which block styles to apply globally (WordPress/gutenberg#79839)
- Global Styles: Reject non-string custom CSS in the REST controller (WordPress/gutenberg#80338)
- Open inspector sidebar when toggling responsive editing (WordPress/gutenberg#80307)
- Client Side Media: Honor image_strip_meta and image_max_bit_depth on the client upload path (WordPress/gutenberg#80218)
- Hide block style variations when state is enabled in global styles (WordPress/gutenberg#80341)
- Media REST API: Fix sideload and finalize for EXIF rotated images (WordPress/gutenberg#80295)
- Fix upload snackbar stuck in uploading state on server-side uploads (WordPress/gutenberg#80345)
- Try fixing responsive layout in Nav block (WordPress/gutenberg#80305)
- Responsive styles: Use viewport dropdown to control states for in-editor global styles sidebar (WordPress/gutenberg#80339)
- RichTextControl: Replace DOM focus tracking with a single React-tree focus boundary (WordPress/gutenberg#80324)
- Notes: Finish WPDS treatment for mention chips (WordPress/gutenberg#80300)
- Notes: Add placeholders to the RichText fields (WordPress/gutenberg#80296)
- Fix upload hang when converting long animated GIFs: decode only the first frame for still outputs (WordPress/gutenberg#80260) (WordPress/gutenberg#80342)
- Device preview dropdown: use active color for device icon when responsive styles are active (WordPress/gutenberg#80346)
- Fix default aspect ratio for lazy loaded Featured image (WordPress/gutenberg#80386)
- Vips/upload-media: consolidate optional params into options objects (WordPress/gutenberg#80330)
- Autocompleters: Don't pre-encode mention search terms (WordPress/gutenberg#80377)
- Animated GIF uploads: generate sub-sizes from the first frame, matching core (WordPress/gutenberg#80268)
- Custom CSS: Fix cascade order against block style variations (WordPress/gutenberg#80340)
- Rich Text: Restore the selection when focus returns to the editable (WordPress/gutenberg#80396)
- Notes: Arm the mention kses allowance on REST note creation (WordPress/gutenberg#80221)
- Fix upload snackbar double-counting a single HEIC upload in Safari (WordPress/gutenberg#80436)
- ContentEditableControl: fix invalid label association with contenteditable div (WordPress/gutenberg#80441)
- Editor: Disable canvas resizing while zoomed out (WordPress/gutenberg#80391)
- Fix Color Picker Cursor Shaking Issue (WordPress/gutenberg#80205) (WordPress/gutenberg#80435)
- Misc fixes for WordPress-Develop 7.0 merges (WordPress/gutenberg#80444)
- Style Book: Restore live global styles updates on the styles route (WordPress/gutenberg#80459)
- Worker threads: reject pending RPC calls on worker failure or termination (WordPress/gutenberg#79955) (WordPress/gutenberg#80421)
- Media: Add timeout and size guardrails to client-side GIF to video conversion (WordPress/gutenberg#80420)
- Post Content: Use the default block appender for empty content (WordPress/gutenberg#80026)
- Block Supports: Handle nested array block gap values properly (WordPress/gutenberg#80464)
- Editor: Restore fixed device preview height for mobile and tablet (WordPress/gutenberg#80466)
- Block Editor: Guard against non-string spacing preset values (WordPress/gutenberg#80467)
- Writing flow: fully select the ancestor when a text selection crosses a nesting boundary (WordPress/gutenberg#80462)
- Block Editor: Reflect inherited Global Styles values in block inspector controls (WordPress/gutenberg#80481)
- Autocomplete: Reference the suggestions list with `aria-controls` and `aria-haspopup` (WordPress/gutenberg#80403) (WordPress/gutenberg#80499)
- Media: Remove the redundant __heicUploadSupport flag (WordPress/gutenberg#80486)
- State control - avoid tertiary variant on toggle to match style of other dropdown toggles (WordPress/gutenberg#80505)
- Icons: Store the sanitized SVG content when registering an icon (WordPress/gutenberg#80508)
- Fix `useHomeEnd` on tabs in mac testing (WordPress/gutenberg#80374)
- Playlist: Fix playback of tracks served without CORS headers (WordPress/gutenberg#80533)
- Redirect editing events to extension handlers under editableRoot (WordPress/gutenberg#80287)
- Writing flow: fully select the items when a selection extends down into a nested item (WordPress/gutenberg#80492)
- Global Styles panels: fix wrong preset committed and shown when two color presets share a hex (WordPress/gutenberg#80497)
- Replaces the `title` attributes used by revision inline diff annotations with `aria-describedby` (WordPress/gutenberg#80440)
- Notes: Remove "Add note" from the inline styles dropdown (WordPress/gutenberg#80531)
- Global Styles: Resolve per-level heading element styles in block inspector controls (WordPress/gutenberg#80495)
- Notes: Render @ mentions as span chips and narrow the kses class allowance (WordPress/gutenberg#80528)
- Revisions: Specify block level diff status via aria-label (WordPress/gutenberg#77779)
- Backport from Core: improve icon name unit tests (WordPress/gutenberg#80552)
- Device type preview: fix collapsing to content height (WordPress/gutenberg#80553)
- Wrap notices in ThemeProvider with 0 corner radius (WordPress/gutenberg#80557)
- Global Styles: Limit the inherited value treatment to the Gutenberg plugin (WordPress/gutenberg#80555)
- Fix crashes when manipulating locked blocks (WordPress/gutenberg#80509)
- Notes: align floating threads with their inline marker (WordPress/gutenberg#79877)

Props wildworks.
See #65529.

git-svn-id: https://develop.svn.wordpress.org/trunk@62824 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Jul 22, 2026
This updates the pinned commit hash of the Gutenberg repository from `e73c3c481db0650183f092af157f6e42efe9ee2d` to `4997026b75c922d8a6f77a03d72ed7cad04c7073`.

A full list of changes included in this commit can be found on GitHub: 
WordPress/gutenberg@e73c3c4...4997026

- Notes: Replace blur-deselect bookkeeping with useFocusOutside (WordPress/gutenberg#80222)
- Playlist: Update @SInCE tags to 7.1.0 (WordPress/gutenberg#80317)
- fix playlist block Dimensions Design (WordPress/gutenberg#80312)
- UI: Backport compat overlay fixes to WordPress 7.1 (WordPress/gutenberg#80322)
- Editor: allow selecting which block styles to apply globally (WordPress/gutenberg#79839)
- Global Styles: Reject non-string custom CSS in the REST controller (WordPress/gutenberg#80338)
- Open inspector sidebar when toggling responsive editing (WordPress/gutenberg#80307)
- Client Side Media: Honor image_strip_meta and image_max_bit_depth on the client upload path (WordPress/gutenberg#80218)
- Hide block style variations when state is enabled in global styles (WordPress/gutenberg#80341)
- Media REST API: Fix sideload and finalize for EXIF rotated images (WordPress/gutenberg#80295)
- Fix upload snackbar stuck in uploading state on server-side uploads (WordPress/gutenberg#80345)
- Try fixing responsive layout in Nav block (WordPress/gutenberg#80305)
- Responsive styles: Use viewport dropdown to control states for in-editor global styles sidebar (WordPress/gutenberg#80339)
- RichTextControl: Replace DOM focus tracking with a single React-tree focus boundary (WordPress/gutenberg#80324)
- Notes: Finish WPDS treatment for mention chips (WordPress/gutenberg#80300)
- Notes: Add placeholders to the RichText fields (WordPress/gutenberg#80296)
- Fix upload hang when converting long animated GIFs: decode only the first frame for still outputs (WordPress/gutenberg#80260) (WordPress/gutenberg#80342)
- Device preview dropdown: use active color for device icon when responsive styles are active (WordPress/gutenberg#80346)
- Fix default aspect ratio for lazy loaded Featured image (WordPress/gutenberg#80386)
- Vips/upload-media: consolidate optional params into options objects (WordPress/gutenberg#80330)
- Autocompleters: Don't pre-encode mention search terms (WordPress/gutenberg#80377)
- Animated GIF uploads: generate sub-sizes from the first frame, matching core (WordPress/gutenberg#80268)
- Custom CSS: Fix cascade order against block style variations (WordPress/gutenberg#80340)
- Rich Text: Restore the selection when focus returns to the editable (WordPress/gutenberg#80396)
- Notes: Arm the mention kses allowance on REST note creation (WordPress/gutenberg#80221)
- Fix upload snackbar double-counting a single HEIC upload in Safari (WordPress/gutenberg#80436)
- ContentEditableControl: fix invalid label association with contenteditable div (WordPress/gutenberg#80441)
- Editor: Disable canvas resizing while zoomed out (WordPress/gutenberg#80391)
- Fix Color Picker Cursor Shaking Issue (WordPress/gutenberg#80205) (WordPress/gutenberg#80435)
- Misc fixes for WordPress-Develop 7.0 merges (WordPress/gutenberg#80444)
- Style Book: Restore live global styles updates on the styles route (WordPress/gutenberg#80459)
- Worker threads: reject pending RPC calls on worker failure or termination (WordPress/gutenberg#79955) (WordPress/gutenberg#80421)
- Media: Add timeout and size guardrails to client-side GIF to video conversion (WordPress/gutenberg#80420)
- Post Content: Use the default block appender for empty content (WordPress/gutenberg#80026)
- Block Supports: Handle nested array block gap values properly (WordPress/gutenberg#80464)
- Editor: Restore fixed device preview height for mobile and tablet (WordPress/gutenberg#80466)
- Block Editor: Guard against non-string spacing preset values (WordPress/gutenberg#80467)
- Writing flow: fully select the ancestor when a text selection crosses a nesting boundary (WordPress/gutenberg#80462)
- Block Editor: Reflect inherited Global Styles values in block inspector controls (WordPress/gutenberg#80481)
- Autocomplete: Reference the suggestions list with `aria-controls` and `aria-haspopup` (WordPress/gutenberg#80403) (WordPress/gutenberg#80499)
- Media: Remove the redundant __heicUploadSupport flag (WordPress/gutenberg#80486)
- State control - avoid tertiary variant on toggle to match style of other dropdown toggles (WordPress/gutenberg#80505)
- Icons: Store the sanitized SVG content when registering an icon (WordPress/gutenberg#80508)
- Fix `useHomeEnd` on tabs in mac testing (WordPress/gutenberg#80374)
- Playlist: Fix playback of tracks served without CORS headers (WordPress/gutenberg#80533)
- Redirect editing events to extension handlers under editableRoot (WordPress/gutenberg#80287)
- Writing flow: fully select the items when a selection extends down into a nested item (WordPress/gutenberg#80492)
- Global Styles panels: fix wrong preset committed and shown when two color presets share a hex (WordPress/gutenberg#80497)
- Replaces the `title` attributes used by revision inline diff annotations with `aria-describedby` (WordPress/gutenberg#80440)
- Notes: Remove "Add note" from the inline styles dropdown (WordPress/gutenberg#80531)
- Global Styles: Resolve per-level heading element styles in block inspector controls (WordPress/gutenberg#80495)
- Notes: Render @ mentions as span chips and narrow the kses class allowance (WordPress/gutenberg#80528)
- Revisions: Specify block level diff status via aria-label (WordPress/gutenberg#77779)
- Backport from Core: improve icon name unit tests (WordPress/gutenberg#80552)
- Device type preview: fix collapsing to content height (WordPress/gutenberg#80553)
- Wrap notices in ThemeProvider with 0 corner radius (WordPress/gutenberg#80557)
- Global Styles: Limit the inherited value treatment to the Gutenberg plugin (WordPress/gutenberg#80555)
- Fix crashes when manipulating locked blocks (WordPress/gutenberg#80509)
- Notes: align floating threads with their inline marker (WordPress/gutenberg#79877)

Props wildworks.
See #65529.
Built from https://develop.svn.wordpress.org/trunk@62824


git-svn-id: http://core.svn.wordpress.org/trunk@62104 1a063a9b-81f0-0310-95a4-ce76da25c4cd
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] Bug An existing feature does not function as intended

Projects

Development

Successfully merging this pull request may close these issues.

4 participants