Skip to content

feat(theme): add typography role tokens - #572

Open
wemra3 wants to merge 13 commits into
mainfrom
feat/typography-roles
Open

wemra3 wants to merge 13 commits into
mainfrom
feat/typography-roles

Conversation

@wemra3

@wemra3 wemra3 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Motivation

AppShell defines a font family but no typography tokens. The styling guide gives a table of utility pairings, and real code matches it in only 15% to 40% of places (analysis in tailor-inc/platform-planning#1580). Without names for text styles, people and AI agents choose size, weight, and line height again for each element.

This PR adds the tokens only. No component uses them yet, so nothing changes visually.

Design Decision

Tokens first, in two layers like the colors

themes/default.css holds four variables per role: --app-shell-type-<role>-<size|line-height|weight|letter-spacing>. theme.bridge.css maps them to --text-<role> inside the existing @theme inline, with var() references only. @theme inline writes literal values into the utility, so values placed in the bridge could not be overridden on :root.

Ten roles with today's values

heading-lg, heading-md, heading-sm, body-md, body-sm, label-md, label-sm, code-sm, plus body-md-relaxed and body-sm-relaxed for long text. The values match the current utility pairings. Line height is a unitless ratio on a 4px grid. code-sm needs font-mono, because --text-* cannot set font-family.

cn() registers the role names

tailwind-merge reads an unknown text-* class as a text color. Without registration, cn("text-label-md", "text-muted-foreground") keeps only the color, so a role passed in className to an AppShell component would be lost. Core's cn() now registers the ten names in the font-size group. This change is limited to lib/utils.ts and its test.

Modifiers are a documented rule

A role may be combined with leading-none, tabular-nums, font-medium or font-semibold, and uppercase. This is written in the styling guide. There is no lint rule.

Summary

  • Add 10 typography role tokens and text-* utilities.
  • Register the role names in core's cn().
  • Add tests: token and bridge symmetry, fixed values, and a Tailwind compile test that checks the emitted CSS.
  • Rewrite the Typography section of the styling guide, with a live example.
  • Add the decision record decisions/typography-roles.md.
  • Fix the docs-browser @source path, which pointed to a folder that does not exist. This affects the docs app only.

Not in this PR

Applying roles to core components, a Text component, overline, moving 10px and 11px text to 12px, and tabular-nums as the default for numeric DataTable columns.

Question for reviewers

The cream and bloom palettes set line-height: 1.2 and letter-spacing: -0.03em on html :where(h1, h2, h3, h4, h5, h6) outside any @layer. This rule wins over a role utility on those elements. This PR keeps the rule and documents the effect. What does the rule protect, and should it stay after heading roles are applied?

🤖 Generated with Claude Code

Hiroki Uemura and others added 11 commits October 5, 2026 08:48
Add ten typography roles (heading-lg/md/sm, body-md/sm, label-md/sm,
code-sm, body-md-relaxed, body-sm-relaxed). Each role is four CSS
variables in themes/default.css, bridged to Tailwind text-* utilities in
theme.bridge.css. Register the role names in core's cn() so a role is
kept next to a text color class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The path resolved outside the repository and matched no file, so utility
classes used only in examples were not generated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrite the Typography section of the styling guide, add a live example,
add decisions/typography-roles.md, and add a changeset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add letter spacing to the role spec test, add a leading-none order test for cn(), and reword the default palette and template comments so they do not contradict themselves.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… data

Document that cream and bloom ignore leading-* and tracking-* on h1-h6, the astw: prefix limit of cn(), and that a role sets its own weight and letter spacing. Fix the ADR source table (four sources, usage counts) and record the cn() registration as a deviation from the first plan. Make the docs example self-contained.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The compile test only checked font-size for each role. A literal added to the bridge after the role entries could change line-height, weight or letter-spacing without failing the test. Assert all four values against the var() references.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ter spacing

The sentence after the typography example only named leading-none. Align it with the Change a role paragraph.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The relaxed roles change only line height, so a one-line sample does not
show the difference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Show the same paragraph in body-md and body-md-relaxed (and body-sm and
body-sm-relaxed) so the line height difference is visible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wemra3
wemra3 requested a review from a team as a code owner October 5, 2026 00:13
@wemra3
wemra3 requested review from IzumiSy and interacsean October 5, 2026 00:13
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Code Metrics Report

main (529b3ac) #572 (9d5f2ec) +/-
Coverage 87.6% 87.6% +0.0%
Test Execution Time 2m14s 1m27s -47s
Details
  |                     | main (529b3ac) | #572 (9d5f2ec) |  +/-  |
  |---------------------|----------------|----------------|-------|
+ | Coverage            |          87.6% |          87.6% | +0.0% |
  |   Files             |            206 |            206 |     0 |
  |   Lines             |           6090 |           6092 |    +2 |
+ |   Covered           |           5335 |           5337 |    +2 |
+ | Test Execution Time |          2m14s |          1m27s |  -47s |

Code coverage of files in pull request scope (100.0% → 100.0%, patch 100.0%)

Files Coverage +/- Patch Coverage Status
packages/core/src/lib/utils.ts 100.0% 0.0% 100.0% modified

Reported by octocov

@wemra3
wemra3 marked this pull request as draft October 5, 2026 00:16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wemra3
wemra3 marked this pull request as ready for review October 5, 2026 00:21
@IzumiSy

IzumiSy commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

@wemra3 this looks good to go, but can you resolve conflicts? I will approve this PR after that 🙏

Resolve conflicts with the semantic colour roles (#569): keep the typography
roles as a new section 8 block in default.css, merge the inherited-token notes
in theme.css and _template.css, keep both the 40-role and the typography
bridge tests, and regenerate docs-manifest.json with docs:sync.

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

Looks good!

Can I get eyes from @interacsean, too?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants