Typography: Add font-stretch and font-variation-settings support to Theme JSON styles. - #13789
Jiwoon-Kim wants to merge 3 commits into
Conversation
Two typography style properties had no way to reach CSS. `font-stretch` is the one axis with a CSS property that a browser will not synthesize, there being no `font-synthesis-width`, so a width a variable font declares could not be used at all. `font-variation-settings` is what the axes with no property of their own have, and nothing could store a value for it. Adds `styles.typography.fontStretch` and `styles.typography.fontVariationSettings`, the latter an object keyed by axis tag so that the theme.json origins merge per axis, where a string would replace the whole list. Adds the settings that go with them, `axes` on a font face for the axes a file has and their ranges, `font-variation-settings` to the properties `safecss_filter_attr()` allows, and the two block supports. The rule for what may be written to `font-variation-settings` lives in `WP_Style_Engine`: a tag of four letters or digits, a numeric value, and not one of the four axes a CSS property owns, because the property is applied after those and would take the axis from them. `opsz` is allowed, since `font-optical-sizing` only switches the browser's own tracking. `WP_Theme_JSON` reads that rule through the style engine when it sanitizes rather than keeping a second copy of it. The two lists of variables in the block support are regenerated rather than patched, because the longest name sets the column the coding standard aligns the assignments to. No line in them changes meaning. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Six tests were written before the ticket existed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`src/wp-includes/theme.json` is built rather than written: `copy:gutenberg-theme-json` takes it from the Gutenberg plugin and rewrites only its `$schema`. Turning the width setting on here was undone by the build and caught by the guard that follows it, so the default arrives with the Gutenberg pull request instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
Tested PR #13789 / Trac #66198 on PHP 8.3 + macOS. Test Results
Minor ObservationIn return is_string( $tag )
&& 1 === preg_match( '/^[A-Za-z0-9]{4}$/', $tag )Because PHP automatically casts numeric string array keys like Overall, the implementation and tests are very solid and ready for Core. AI DisclosureAssisted by generative AI tools to run automated checks (PHPUnit and PHPCS), execute edge-case scripts, and format the testing report. |
Why
Two typography style properties have no way to reach CSS today, and a variable font needs both.
font-stretchis the one axis with a CSS property that a browser will not synthesize — there is nofont-synthesis-width— so a width a font declares cannot be used at all without it.font-variation-settingsis what the axes with no property of their own have, and there is no style property for it.This is the Core half of editor work happening in Gutenberg: WordPress/gutenberg#83159 is the editor side, and WordPress/gutenberg#83148 is the design discussion behind it. This carries the contract only: what may be stored, what is written, and what is refused.
What
styles.typographyfontStretchfont-stretchfontVariationSettings, an object keyed by axis tagfont-variation-settingsAlso:
settings.typography.fontStretchbeside the existingfontStyleandfontWeight;settings.typography.fontVariations, a boolean for whether a site offers custom-axis editing;axeson a font face, for the axes a file has and their ranges;font-variation-settingsadded to whatsafecss_filter_attr()allows,font-stretchbeing there already; and the two block supports.How
WP_Style_Engineholds the rule for what may be written tofont-variation-settings: a tag of four letters or digits, a numeric value, and not one of the four axes a CSS property owns (wght,wdth,slnt,ital), because the property is applied after those and would take the axis from them.opszis allowed, sincefont-optical-sizingonly switches the browser's tracking rather than taking a coordinate.WP_Theme_JSONreads that rule through the style engine in both places it needs it — serializing a stylesheet, and sanitizing styles for a user withoutunfiltered_html— rather than holding a second copy. A second copy is what this whole line of work started from: two helpers reading the same font faces by different rules offered a static family nine widths it had no files for.The block support's two lists of variables are regenerated rather than patched, because the longest name sets the column the coding standard aligns the assignments to. No line in them changes meaning.
Testing Instructions
npm run test:php -- --filter 'Typography|Theme_JSON|Style_Engine|StyleEngine|ThemeJson|font_stretch'. 516 tests pass here.theme.json, setstyles.typography.fontStretchto"75%"andstyles.typography.fontVariationSettingsto{ "GRAD": 50, "wght": 700 }. The stylesheet carriesfont-stretch: 75%andfont-variation-settings: "GRAD" 50; the weight is left tofont-weight, so a<strong>inside still renders bolder.unfiltered_html, save Global Styles withfontVariationSettingsset to{ "GRAD": 20, "wght": 700, "XTRA": "500; color: red" }. OnlyGRADis stored.Not included
The editor controls and the reading of a family's faces that decides what they offer, which stay in Gutenberg; which blocks opt in, since the supports here are off everywhere; and reading
axesfrom a font file'sfvartable when it is uploaded.Nor
wp-includes/theme.json, which turns the width setting on by default. The build copies that file from the plugin, so the default arrives with the Gutenberg pull request rather than from here; editing it in this branch was reverted by the build and caught bygit diff --exit-code.Trac ticket: https://core.trac.wordpress.org/ticket/66198
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Porting the implementation and tests from the Gutenberg prototype, and running the test suites. I reviewed the changes and the results, and one wrong assumption the port made was found by the tests rather than by reading: Core keys the font families setting by origin, as the plugin does.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.