Skip to content

feat(ui): rem-based type scale, mobile chat sizing, accent contrast fixes - #21

Merged
martinezharo merged 2 commits into
mainfrom
claude/mobile-chat-font-accessibility-bqbyhl
Aug 9, 2026
Merged

feat(ui): rem-based type scale, mobile chat sizing, accent contrast fixes#21
martinezharo merged 2 commits into
mainfrom
claude/mobile-chat-font-accessibility-bqbyhl

Conversation

@martinezharo

@martinezharo martinezharo commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Started as "is the chat font on mobile big enough?". The answer was that it clears WCAG's formal bar and not much else — WCAG sets no minimum font size, and the viewport allows pinch-zoom, so nothing fails on size alone. But the audit turned up five real contrast failures next door, so this PR covers both.

Before / after

390px viewport, rendered against the compiled CSS of main and of this branch, so the two halves differ only by this change.

Light

Chat type scale, light theme, before and after

Dark — note the attachment panel, which used to darken the accent in both themes and now lightens here:

Chat type scale, dark theme, before and after

The type scale is now rem

Every size in the app was a hardcoded text-[Npx] — 141 of them across 12 components, in 23 distinct sizes. A px size is frozen at whatever was typed, so a reader who raised their browser's default font size gets nothing back. Pinch-zoom satisfies WCAG 1.4.4 on its own; it does not stand in for honouring the setting.

Replaced with eleven named steps defined in styles.css, all rem:

Token Size Role
text-meta 11px timestamps, badges, status pills
text-caption 12.5px secondary metadata
text-note 13.5px dense labels, list rows
text-body 14.5px default reading size
text-body-lg 15px document base
text-lead 16px mobile reading size, inputs
text-title-smtext-display-lg 17–26px headings

Sizes are named by role, not by measurement, so the scale can be retuned in one place. marketing.css loses its font-size: 16px root pin for the same reason — the rest of that sheet was already rem, so the pin was silently discarding the preference for the whole page.

The floor is 11px: Apple's HIG says never below 11pt and Material's labelSmall is 11sp, and the app shipped 9.5px and 10px labels under both.

The chat steps up on mobile

No breakpoint in Chat.tsx touched font size — max-md: only adjusted padding and widths, so a phone got the desktop density unchanged. Bubbles now go 14.5 → 16px on narrow screens, metadata 11 → 12.5px, file names 13.5 → 14.5px. For reference, WhatsApp, Telegram and iMessage all sit at 16–17px in the bubble.

Contrast fixes (WCAG 1.4.3 — small text needs 4.5:1)

Secondary copy on a sent bubble was on-accent at 70–75% alpha, and the "couldn't decrypt" line used opacity-75 for the same effect. The accent only clears full-strength text by ~5.4:1 to begin with, so any alpha under ~90% lands under the minimum:

Element Before (light / dark) After
Timestamp 3.51 / 3.65 4.74 / 5.03
Device name 3.78 / 4.01 4.74 / 5.03
File size + state 4.30 / 3.00 6.08 / 6.25
"Couldn't decrypt" 3.78 / 4.01 4.74 / 5.03

All four now use a solid --c-on-accent-muted token rather than an alpha, which is the same design intent at a legible contrast.

The fifth was structural: the attachment panel inside a sent bubble darkened the accent in both themes. The dark theme puts near-black text on a light accent, so darkening pushed the file name itself to 4.17:1 and its action icons to 2.58:1 (1.4.11 asks 3:1 for non-text). The panel now lightens in dark, restoring 6.9:1.

Two more papercuts

  • iOS auto-zoom. Safari zooms the viewport when a field under 16px takes focus, and never zooms back out — so opening the composer left the chat mis-scaled. Every text entry point (.field-input, the composer, the linking-code textarea) now steps up to 16px on mobile.
  • Retry target. The inline "Retry" on a failed message was a bare run of 10px text, ~13px tall. Now 24×24 minimum per WCAG 2.2 SC 2.5.8.

Verification

  • Contrast ratios computed from the actual token values, including the alpha compositing over the accent and over the attachment panel — every number in the tables above is measured, not estimated.
  • The captures above: both builds rendered side by side at 390px in both themes, with the real webfonts, to confirm the tokens resolve and the hierarchy still reads.
  • pnpm build, pnpm lint, pnpm format:check clean; 382 tests pass.

AGENTS.md gains a short Typography section so the scale doesn't rot back into arbitrary px.

…ixes

The chat shipped a desktop density unchanged to phones — 14.5px bubbles
and 10px timestamps, below both Apple's 11pt floor and Material's 11sp —
and every size in the app was a hardcoded `text-[Npx]`, which discards
the reader's browser font-size preference entirely.

Type scale
- Eleven named sizes in `styles.css`, all `rem`, replacing 141 arbitrary
  px utilities across 12 components. `rem` follows the user's font-size
  setting; the zoom gesture that satisfies WCAG 1.4.4 on its own cannot
  stand in for that. `marketing.css` loses its `font-size: 16px` root
  pin for the same reason.
- The floor is 11px, so the 9.5px and 10px labels move up to it.
- Chat steps up a rung on narrow screens: bubbles to 16px, metadata to
  12.5px, file names to 14.5px.

Contrast (WCAG 1.4.3, small text needs 4.5:1)
Secondary copy on a sent bubble was `on-accent` at 70-75% alpha, which
measured 3.51-4.01:1 in both themes; the "couldn't decrypt" line used
`opacity-75` for the same effect. All now use a solid
`--c-on-accent-muted`, verified at 4.74:1 light and 5.03:1 dark.

The attachment panel inside a sent bubble darkened the accent in both
themes, but the dark theme puts near-black text on a light accent — so
darkening dropped even the file name to 4.17:1 and its icons to 2.58:1
(1.4.11 wants 3:1). It now lightens in dark, restoring 6.9:1.

Also
- Every text field steps up to 16px on mobile: below that, iOS Safari
  zooms the viewport on focus and never zooms back out.
- The inline "Retry" was a ~13px-tall tap target; it now meets the
  24x24 minimum of WCAG 2.2 SC 2.5.8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtMsogZwimja6364oV8vBZ
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
file-sharer becce99 Aug 09 2026, 07:53 PM

Both themes at a 390px viewport, rendered against the compiled CSS of
`main` and of this branch so the two halves differ only by the change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtMsogZwimja6364oV8vBZ
@martinezharo
martinezharo merged commit 5c48175 into main Aug 9, 2026
2 checks passed
@martinezharo
martinezharo deleted the claude/mobile-chat-font-accessibility-bqbyhl branch August 9, 2026 19:56
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