Skip to content

feat(text): optical-center baseline mode, default for mixed-case UI#31

Merged
chiefcll merged 1 commit into
mainfrom
feat/text-optical-baseline
May 22, 2026
Merged

feat(text): optical-center baseline mode, default for mixed-case UI#31
chiefcll merged 1 commit into
mainfrom
feat/text-optical-baseline

Conversation

@chiefcll
Copy link
Copy Markdown
Contributor

Summary

Pure cap-height centering (PR #27's default) reads slightly low for lowercase-heavy mixed-case strings like "Button" — the visual mass sits in the x-height band, below cap-center. Pure x-height centering goes the other way: capitals appear high.

This PR adds a new textBaselineMode: 'optical' that places the mean of cap-height and x-height at the line-box center:

firstBaselineY = (lineHeightPx + (capHeight + xHeight) * 0.5) * 0.5

This is the sweet spot for typical TV UI text — the same heuristic macOS/iOS controls use. 'optical' becomes the new default. 'cap', 'x', and 'linebox' remain available for content where one of those is specifically preferred.

Why

For Ubuntu at common UI sizes, optical-mode shifts text up vs. cap-mode by:

fontSize shift up
32 (button label) ~1.7 px
44 (heading) ~2.3 px
50 (large heading) ~2.75 px

That's the "text reads a little low" nudge designers manually apply in CSS — now built into the engine.

When to override

  • All-caps or numeric content (timers, badges, all-uppercase headings) → set textBaselineMode: 'cap' globally. Optical mode pushes caps slightly above center for these.
  • Body text where lowercase dominates → 'x' if you want maximum lowercase centering.
  • Legacy line-box layouts'linebox' matches the pre-PR-feat(text): cap-height centering as the per-line vertical anchor #27 behavior.

Files

Test plan

  • pnpm build clean.
  • vitest run — 182/182 pass.
  • Full VRT in Docker CI mode — 166/166 pass against the new certified set.
  • Visually spot-checked text-vertical-align snapshots — baselines now sit slightly above the green cap-mode reference lines, exactly the expected ~2.75 px optical shift at fontSize: 50.

Notes for reviewers

  • This is a default-changing PR. Apps that depend on the exact pre-PR baseline positions (pixel-perfect manual layouts pinned to cap-anchored baselines) will see a small upward shift on every text node. The fix is one line: textBaselineMode: 'cap' in renderer settings.
  • The trimmed-height formula from feat(text): expose trimmedHeight on text nodes #30 is unchanged — it's still measured from cap-top to descender bottom regardless of baseline mode.

🤖 Generated with Claude Code

Pure cap-height centering reads slightly low for lowercase-heavy
mixed-case strings like "Button" because the visual mass sits in the
x-height band, below cap-center. Pure x-height centering goes the
other way — caps appear high.

Add a new `textBaselineMode: 'optical'` that places the mean of
cap-height and x-height at the line-box center:

    firstBaselineY = (lineHeightPx + (capHeight + xHeight) * 0.5) * 0.5

This is the sweet spot for typical TV UI text — same heuristic
macOS/iOS controls use. For Ubuntu at fontSize 32, text shifts up by
about 1.7 px vs. cap-mode; at fontSize 50, about 2.75 px.

Set `'optical'` as the new default. `'cap'` remains available for
content that's mostly uppercase or numeric (timers, badges), and `'x'`
for body text where lowercase dominates.

All 142 text-bearing VRT snapshots recertified to reflect the new
default. SDF handler comment updated since xHeight is now also
consumed by `'optical'`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chiefcll chiefcll merged commit 81a320b into main May 22, 2026
1 check passed
@chiefcll chiefcll deleted the feat/text-optical-baseline branch May 22, 2026 23:13
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.

1 participant