Skip to content

Prototype: look Appearance font faces up from the family a block's text is drawn in - #83462

Open
Jiwoon-Kim wants to merge 5 commits into
WordPress:trunkfrom
Jiwoon-Kim:try/block-screen-inherited-font-capabilities
Open

Jiwoon-Kim wants to merge 5 commits into
WordPress:trunkfrom
Jiwoon-Kim:try/block-screen-inherited-font-capabilities

Conversation

@Jiwoon-Kim

@Jiwoon-Kim Jiwoon-Kim commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Prototype for #83459. Opened to ask a question rather than to merge, and happy to close it if the answer is no.

What?

In Styles → Blocks → , Appearance is built from the block's own font family. A block that draws its text in the root font has none, so the control falls back to the built-in list and offers weights the font does not provide.

This adds an optional capabilityFontFamily to TypographyPanel: the family the text is drawn in when neither the panel's value nor the value it inherits names one. The Blocks screen passes the root font family, and the control looks its faces up instead of falling back.

It also carries a fix found while testing that: the panel style now preserves a family written inline in style.typography.fontFamily, matching the existing attribute path. A block that records its family that way reached the panel with none at all, so the faces looked up were the inherited font's rather than its own.

The three places a family can come from, all of which the faces are now read through:

fontFamily attribute            the family a picker sets
style.typography.fontFamily     a family written into the style
inherited or root family        what capabilityFontFamily supplies

Why?

The question behind #83459 is which family should decide what Appearance offers:

  • A. The block's own fontFamily only, as today.
  • B. The family the block actually renders in, resolved through the cascade.
  • C. Neither: disable Appearance when no family is resolved.

This prototype implements B, because the text really is drawn in the inherited family and #49090 asked for the control to offer what the font has. If A is the intended model for this screen, the bug is instead that the built-in list stands in for "unknown", and this should be closed in favour of C or of saying so in the UI.

Worth noting that the three surfaces disagree today. The block inspector resolves the inherited family already, through resolveStyle() and its root layer; the Blocks screen reads getStyle( gs, path, blockName ), which stops at styles.blocks.<name>. The same block gets different Appearance options depending on where it is edited.

How?

TypographyPanel takes capabilityFontFamily and uses it for one thing:

const familyForFaces = fontFamily ?? decodeValue( capabilityFontFamily );

That value feeds getMergedFontFamiliesAndFontFamilyFaces() and nothing else. It does not become a value, is not written on change, does not affect what the Font control displays, and does not mark anything as inherited: fontFamily, inheritedFontFamily and isFontFamilyPlaceholder are untouched, so the Font control still reads Default and the inheritance treatment is unchanged.

screen-block.tsx reads the root family separately, rather than folding it into inheritedStyle, which 22 controls in the panel read for their own inheritance display.

The inline family is a separate commit. attributesToStyle() read the fontFamily attribute and fell to undefined without one, while the three values beside it — font size, text shadow and text colour — each fall back to the style. Measured in the editor: a paragraph whose own family is an icon font declaring fontWeight: "100 700" was offered weights to 1000, the range of the site's font; with the family preserved the same control stops at 700.

Deliberately not included, because they are the broader change #83459 mentions: making the Blocks screen resolve inheritance generally, and widening the inheritance indicators to match.

Testing Instructions

  1. In the active theme's theme.json, register a family whose face declares "fontWeight": "100 700" and set it as the root font, as in Typography: the Blocks screen does not resolve a block's inherited font family, so Appearance offers weights the font lacks #83459.
  2. Open Styles → Blocks → Paragraph with its own font left at Default, and open Appearance.
  3. On trunk the list runs Thin to Extra Black. With this change it stops at Bold, and the Font control still reads Default.
  4. Set a font on the block itself. Appearance follows that family, as before.

Testing Instructions for Keyboard

No change to the controls themselves; only the options listed for a block that inherits its font change.

Not included

No browser test for what Appearance offers. The inline family fix has unit coverage, binding the three paths above to one expectation, but the options a block is offered are behind the panel's menu and the existing coverage for that control is browser-based. That test would have to be rewritten if the answer to the question above is A or C, so I would rather add it once the model is settled.

One known limitation: the root family is read without the screen's prefix, so a block style variation screen reads the root rather than the variation's typography. Worth fixing if this direction is taken.

Use of AI Tools

AI tools (Claude Code, Claude Opus 5) assisted with implementation and with the measurements in #83459. I reviewed the changes and the results.

🤖 Generated with Claude Code

In Styles > Blocks > <block>, the Appearance options come from the block's own
font family. A block that draws its text in the root font has none of its own,
so the control falls back to the built-in weight list and offers weights the
font does not provide.

Give TypographyPanel an optional `capabilityFontFamily`: the family the text is
drawn in when neither the panel's value nor the value it inherits names one. It
feeds the face lookup and nothing else, so the Font control still reads
`Default`, no value is written, and the inheritance treatment the other controls
show is unchanged.

The Blocks screen reads the root family separately rather than folding it into
`inheritedStyle`, which 22 controls in the panel read for their own display.

Prototype for WordPress#83459, which asks whether this screen should resolve the family
the block renders in at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the [Package] Block editor /packages/block-editor label Sep 23, 2026
@github-actions

Copy link
Copy Markdown

🤖 PR meta 🤖

🏷️ Labels

This pull request needs exactly one label indicating its type, and has 0.

  • Required: any label starting with [Type].
  • Found: none.

Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Jiwoon-Kim and others added 3 commits September 27, 2026 01:43
A block records its font family in one of two places: the attribute a preset
sets, or `style.typography.fontFamily` when the value was written inline.
The style the Typography panel is given read only the attribute, so a block
of the second kind arrived without a family at all, and everything that asks
what font it is drawn in answered with the inherited one instead.

The three values beside it already fall back this way. Font size, text
shadow and text colour each read their attribute and, failing that, the
style; the family alone dropped to `undefined`.

What that cost is not cosmetic. The faces a family declares decide which
styles, weights and widths the panel can offer, so reading the wrong family
offers the wrong ones. Measured in the editor, a paragraph whose own family
is an icon font declaring `fontWeight: "100 700"` was offered weights up to
1000, which is the range of the site's font rather than its own. With the
family preserved the same control stops at 700.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Jiwoon-Kim
Jiwoon-Kim marked this pull request as ready for review September 27, 2026 09:07
@github-actions

github-actions Bot commented Sep 27, 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: Jiwoon-Kim <kimjiwoon@git.wordpress.org>
Co-authored-by: jahirulwp <jahirulwp@git.wordpress.org>

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

@jahirulwp

jahirulwp commented Sep 27, 2026 •

Copy link
Copy Markdown

Thanks for picking this up! I tested this PR, and I have one suggestion.

Testing

  • Checked out a215bb9f (Node 24.21). The new typography-attributes-to-style tests pass. The related suites also pass: the block-editor global-styles tests (including the Typography panel browser tests) and all of global-styles-ui. That's 21 files and 2409 tests.
  • I also wrote a browser test that renders ScreenBlock with a config modeled on Twenty Twenty-Four: body text in Inter (300 900), and headings in Cardo (400, 400 italic, 700) set on styles.elements.heading. That's how TT4 ships its fonts.
Block trunk this PR
Paragraph built-in list (Thin … Extra Black) Inter's weights (Light … Black) ✅
Heading built-in list (Thin … Extra Black) Inter's weights ❌. The heading is drawn in Cardo, so this should be Regular, Bold, Regular Italic, Bold Italic

So Paragraph is fixed, but Heading still gets the wrong list. screen-block.tsx only reads the root typography.fontFamily, while a Heading's text is painted by elements.heading. Based on getElementLayers() in inherited-value-context.js, the same problem should affect Button (elements.button), the other heading-like blocks (Site Title, Post Title, Query Title, …), and the whole-link blocks (elements.link).

Suggestion

The block inspector already resolves this cascade with resolveStyle() plus getElementLayers(). Using both on the Blocks screen would keep the two in sync:

const capabilityFontFamily = useMemo(
	() =>
		resolveStyle( mergedConfig, {
			blockName: name,
			variationName: variation ?? null,
			elements: getElementLayers( name ),
		} ).value?.typography?.fontFamily,
	[ mergedConfig, name, variation ]
);

This needs getElementLayers to be exported from inherited-value-context.js and added to the block-editor private APIs (through components/global-styles/index.js). With that change, both cases in my test pass, all related suites still pass (22 files and 2411 tests, including the new one), and npm run lint:js is clean on the changed files. Passing variationName should also fix the block style variation limitation you mention in the description, but I haven't tested that case. Since you left the browser test out until the model is settled, I can share the patch and the test, or open a PR against your branch, whichever you prefer.

I think this also answers the A/B/C question: go with B (the family the text is actually drawn in), resolved the same way the inspector does it. That way the Blocks screen and the block inspector offer the same weights for the same block.

A smaller note (this bug already exists; this PR didn't cause it): styleToAttributes() in hooks/typography.jsx always removes typography.fontFamily from style and only keeps a preset slug. After the attributesToStyle() change, the panel now shows an inline, non-preset family. From reading the code, I think changing another typography control (for example, font size) still drops that family. Might be worth a follow-up.

Not tested yet: a manual check in the editor.

@Jiwoon-Kim

Copy link
Copy Markdown
Contributor Author

Thank you — this is the case I could not find, and the Twenty Twenty-Four setup is what makes it obvious. It also answers the A/B/C question, in favour of B: the family the text is actually drawn in, resolved the same way the inspector resolves it, so that the Blocks screen and the block inspector offer the same weights for the same block.

Could you open a pull request against this branch with the resolveStyle() and getElementLayers() change and the browser test? I would rather review yours than rewrite what you have already tested. I will run the manual editor checks alongside it.

One thing worth a case, since your test models Twenty Twenty-Four rather than running it: the shipped themes do not agree on how a family is written into styles. Twenty Twenty-Five uses var:preset|font-family|manrope, while Twenty Twenty-Four and Twenty Twenty-Three use var(--wp--preset--font-family--heading). It would be worth having both forms in the test rather than whichever one the modeled config happens to use.

That the element screen already handles the second form is a point for resolveStyle(): on a real Twenty Twenty-Four, Styles > Typography > Headings shows Cardo and offers its Regular and Bold and the italic it has a face for.

One note for whoever reads the diff after us: passing only the block name to getElementLayers() is deliberate rather than an omission. Its second parameter is a resolved heading level, and leaving it out returns [ 'heading' ], which is what the Blocks screen wants, since that screen is about a block type rather than a particular heading in a document.

On the styleToAttributes() note: I agree, and I would keep it out of this pull request. It predates this branch and it is a different write path, so it deserves its own issue rather than being folded into a change about what the panel reads.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Block editor /packages/block-editor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants