fix(desktop): restore timeline zoom via rem tokens + chat-as-base type scale#1052
Open
tellaho wants to merge 12 commits into
Open
fix(desktop): restore timeline zoom via rem tokens + chat-as-base type scale#1052tellaho wants to merge 12 commits into
tellaho wants to merge 12 commits into
Conversation
Cmd +/- zoom scales the root <html> font-size (rem-only by design), but PR #891 converted the message-timeline + thread render path from rem tokens to hardcoded px (text-[15px]/text-[13px], font-size: 15px), freezing that text against zoom. Preserve Kenny's 15px chat sizing intent but express it in rem so it scales: add rem-based `text-chat` (0.9375rem === 15px) and `text-code` (0.8125rem === 13px) Tailwind tokens, and swap the px classes over in MessageRow, markdown, mentionChip, and globals.css (.mention-highlight). Codify it: add `pnpm check:px-text` CI guard flagging new px text in the timeline/thread render path, wired into `pnpm check`, plus an AGENTS.md note steering future agents to rem tokens. Scoped to the regression footprint — no app-wide sweep. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
tho's reframe: chat text is the app's fundamental building block, so it should *be* the base type size, not a between-the-cracks 15px token wedged between text-sm (14px) and text-base (16px). Bump chat to 16px-as-base and re-anchor the timeline/thread type ramp: - Retire the custom `text-chat` (15px) and `text-code` (13px) rem tokens added in the zoom fix (#1051) — with chat at the stock base they're redundant artifacts of the 15px-wedge era. - Chat body + author → stock `text-base` (16px) in MessageRow, markdown, mentionChip. `.mention-highlight` follows chat to 1rem. - Code (inline + block) → stock `text-sm` (14px) — a deliberate, documented one-notch step down from the 16px base. Satellite elements (timestamps text-xs, system rows text-sm/xs, thread summary, reactions) are deliberately left on their stock tokens: against a 16px base the ratios tighten into the canonical base→sm→xs ramp (16/14/12) instead of the awkward 12/15 they sat at before. Bumping them would inflate the UI for no design gain. Everything stays rem so Cmd +/- zoom keeps scaling it; the check:px-text guard stays green (its guidance updated to point at the stock scale). Net: zero custom font tokens, timeline/thread render path only — no app-wide sweep. Composer chrome (.tiptap pre code) left untouched. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
tellaho
added a commit
that referenced
this pull request
Jun 15, 2026
DiffViewer renders in the message/timeline path via DiffMessage.tsx, so its 3 px font-size rules (.diff, .buzz-diff-gutter, .diff-decoration-content) stayed frozen against Cmd +/- zoom — the same bug class the rem sweep targets. These sat outside the text-[Npx] Tailwind-literal scope (plain CSS) and aren't caught by #1052's px-guard, which scopes CSS to globals.css only. Convert 1:1 with zero pixel shift: 12px -> 0.75rem, 11px -> 0.6875rem. Keeps the app-wide sweep honest. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
tellaho
added a commit
that referenced
this pull request
Jun 15, 2026
The composer's Tiptap .mention-highlight pill used a hardcoded font-size: 15px, freezing it against Cmd +/- zoom (which scales the root rem font-size). Convert to 1rem so it scales, and align to the timeline mention chip's text-base (1rem) sizing per #1052's chat-base type scale. Emoji-mart picker --font-size left as-is per scope. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Cmd +/- zoom scales the root html font-size (rem-only by design), but text-[Npx] font literals froze text in pixels and broke zoom outside the timeline. Convert the 63 non-overlapping UI surfaces to rem so text scales proportionally everywhere. Mapping: 12px -> stock text-xs token; sizes with no clean stock home (7-11px micro-text on avatars, badges, chips) -> 1:1 rem literals to preserve exact visual hierarchy with zero pixel shift. Also converts the 2 stray leading-[100px] line-height literals to rem. Deliberately defers the 3 render-path files (MessageRow.tsx, markdown.tsx, mentionChip.ts) to PR #1052, which already converts them and is awaiting merge — touching them here on a main base would manufacture a conflict. The check:px-text guard lands with #1052; once it merges, the guard covers this branch's 63 files plus #1052's 3. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
DiffViewer renders in the message/timeline path via DiffMessage.tsx, so its 3 px font-size rules (.diff, .buzz-diff-gutter, .diff-decoration-content) stayed frozen against Cmd +/- zoom — the same bug class the rem sweep targets. These sat outside the text-[Npx] Tailwind-literal scope (plain CSS) and aren't caught by #1052's px-guard, which scopes CSS to globals.css only. Convert 1:1 with zero pixel shift: 12px -> 0.75rem, 11px -> 0.6875rem. Keeps the app-wide sweep honest. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
The composer's Tiptap .mention-highlight pill used a hardcoded font-size: 15px, freezing it against Cmd +/- zoom (which scales the root rem font-size). Convert to 1rem so it scales, and align to the timeline mention chip's text-base (1rem) sizing per #1052's chat-base type scale. Emoji-mart picker --font-size left as-is per scope. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…pp-wide Arbitrary `text-[…rem]` literals had drifted across the desktop app pixel-by-pixel (11/10/9/8/7px), and the px-text guard only covered the message-timeline render path. This mints two named meta-text tokens and sweeps every arbitrary text literal onto the rem-based scale so Cmd +/- zoom keeps scaling and the sizes stay on one consolidated ramp. - Add `text-2xs` (0.6875rem/11px) and `text-3xs` (0.5rem/8px) to `tailwind.config.js` under `theme.extend.fontSize`. - Swap arbitrary literals to named tokens (round-up merges): 0.6875/0.625/0.5625rem -> `text-2xs`, 0.5/0.4375rem -> `text-3xs`. - Odd ducks: 0.8125rem (13px) -> stock `text-sm`, 10.5px -> `text-2xs`. - Leave the `text-[6rem]` avatar emoji glyph (decorative, allowlisted). - Widen the px-text guard: scan all of `desktop/src` and reject any arbitrary text-size literal (px **or** rem/em), not just px in the render path. Allowlist the avatar glyph by `path:line`. - Document the tokens + guard in AGENTS.md. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Update rich-text composer mention highlight styling in desktop/src/shared/styles/globals.css to inherit the editor font size instead of forcing text-base - Reduce chip padding to rem-based values so mention, agent mention, and channel highlights align with the composer line height - Remove the stale comment that tied composer chips to timeline chip sizing, preserving the larger rendered timeline chip style separately
- Remove the compact and tight props from the shared Markdown component API - Collapse MarkdownVariant handling into one default text rhythm with leading-6 body text - Update message, inbox, pulse, recent note, and agent transcript callers to stop passing compact/tight display flags - Simplify Markdown memo comparisons now that sizing variants are no longer part of the public surface
- Move mention chip sizing and hover treatment into shared CSS classes consumed by timeline markdown and composer decorations - Apply shared chip styling to channel links and message links so inline semantic chips use one visual treatment - Keep bot mentions optically balanced with agent-specific right padding and a slightly larger robot mask icon - Align composer text with the chat base type size and reduce mention/code radii by one token for a tighter inline appearance
- Add shared MessageHeaderRow and MessageAuthorText primitives for timeline header spacing and author/title typography - Use the shared header primitives in normal and system message rows so usernames, system titles, and timestamps align consistently - Reduce main timeline username size to match surrounding message surfaces and remove the extra body offset now that header line boxes align - Render system-event participants as profile names instead of mention chips and drop unused agent/persona props from system row plumbing
- Increase timeline message avatars to the 10 spacing token so one-line messages have a clearer visual anchor - Replace the previous negative text-column offset with an explicit gap between the header and body - Set header author and timestamp line-height to leading-4 so the header plus one body line aligns with the avatar height - Apply the same header/body gap treatment to system message rows for consistent vertical rhythm
Reconcile the px->rem / named-token type-scale sweep with main's DM/members modal polish (#1054). Resolved 14 conflicted files plus two silent auto-merge regressions caught by tsc/lint (not by conflict markers): - SystemMessageRow.tsx: auto-merge reverted to pre-main, dropping main's agent-mention feature (normalizePubkey/mentionChip, ProfileName highlight/isAgent, describeSystemEvent personaLookup+agentPubkeys, header restructure). Restored main's version, re-applied branch's text-2xs avatar token swaps, rewired agentPubkeys+personaLookup props. - markdown.tsx: auto-merge took branch side, dropping main's compact/tight MarkdownVariant + runtimeRef refactor (4 callers pass compact). Restored main's compact variant + runtimeRef, re-applied branch's migrations: code-block/inline-code text-[13px]->text-sm, rounded-md->rounded-sm, channel-link/message-link chips -> MENTION_CHIP_BASE/HOVER_CLASSES, compact-variant text-[15px]->text-sm. Combined both sides per the merge theme: kept branch's named-token migrations AND main's functional features. pnpm check + tsc + just ci green (desktop 729/729; mobile-test skipped locally, no dart toolchain and no mobile files touched). Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Category: fix
User Impact: Browser zoom (Cmd +/-) once again scales text everywhere it should — message timeline, thread UI, diff viewer, and the composer mention pill — and chat text now sits at a deliberate 16px base.
Problem: Zoom scales the root
<html>font-size (rem-only, by design from #573). PR #891 ("Tune chat text sizing") converted timeline + thread text from rem tokens to hardcoded px (text-[15px]/text-[13px]), which froze that text against zoom. The bug was the unit, not the size. Once the render path was fixed, a broader sweep found the same px-vs-rem regression scattered across the rest of the app (DiffViewer, composer mention highlight, and ~65 other text literals). While fixing the unit, it also became clear chat text shouldn't be a special between-the-cracks token wedged at 15px — chat is the base size, the fundamental building block of the app.Solution: One coherent fix across five commits — restore rem in the render path, re-anchor chat as the base type scale, then sweep the same fix app-wide so no surface is left frozen against zoom:
bfb892c9) — re-express timeline/thread text in rem so zoom scales it again, plus acheck:px-textguard so px text can't be reintroduced in the render path.485d10e0) — make chat the genuine stocktext-base(16px), retire both custom tokens (text-chat15px,text-code13px → zero custom font tokens), and re-anchor the surrounding ramp to the canonicalbase→sm→xs(16/14/12).c439255c) — convert px text literals to rem across ~65 files so every text surface scales with zoom, not just the timeline render path.8ca67e0a) — convertDiffViewer.cssfont-size px → rem so diff text zooms too.e42e70e2) — scale the composer mention pill with zoom and align it to the timeline mention chip's sizing (text-base/1rem) so the two surfaces match instead of diverging. Emoji-mart picker font deliberately left as-is per scope call.e5b10ce5) — mint namedtext-2xs(0.6875rem/11px) andtext-3xs(0.5rem/8px) tokens, sweep the remaining arbitrarytext-[…]literals onto named tokens, and widen thecheck:px-textguard to reject any arbitrary text-size literal — px and rem/em — across all ofdesktop/srcso the regression class can never return.All commits kept intact (no squash) to preserve the "rem floor → design re-anchor → app-wide sweep → permanent guard" trail.
File changes
desktop/tailwind.config.js
Net zero between-the-cracks font tokens —
text-chat/text-coderetired; chat uses stocktext-base, code uses stocktext-sm. Adds namedtext-2xs/text-3xsfor the genuinely sub-xs surfaces (badges, avatar initials, dense meta).desktop/src/features/messages/ui/MessageRow.tsx / desktop/src/shared/ui/markdown.tsx / desktop/src/shared/ui/mentionChip.ts
Render-path text moved to stock rem tokens: chat body + author →
text-base(16px), inline + block code →text-sm(14px).desktop/src/shared/styles/globals.css
.mention-highlightfont-size follows chat to1rem, matching the timeline mention chip so composer + timeline align.desktop/src/shared/styles/DiffViewer.css
Diff-viewer font-size px → rem so diff text scales with zoom.
~65 additional files (px text literals → rem / named tokens)
App-wide sweep converting hardcoded px text sizes to rem and arbitrary
text-[…]literals to named tokens so every text surface respects zoom. Includes the sub-xs round-ups:badge.tsx0.625rem(10px)→text-2xs(11px),AgentActivityCardtimestamps unified totext-2xs(fixes an 11px/10px same-role drift),PulseTabBar10.5px→text-2xs,UserAvatar0.5rem→text-3xs/0.5625rem→text-2xs. Only thetext-[6rem]avatar glyph remains, explicitly allowlisted bypath:line.desktop/scripts/check-px-text.mjs / scripts/check-px-text-core.mjs / desktop/package.json
check:px-textguard wired intopnpm check. Widened to flag any arbitrary text-size literal (px and rem/em) across all ofdesktop/src, with a guard message pointing at the stock scale.AGENTS.md
"Text sizing & zoom" section steering future agents to rem + named tokens + chat-as-base, and documenting the widened guard.
RESEARCH/TIMELINE_ZOOM_REM_REGRESSION.md / RESEARCH/CHAT_BASE_TYPE_SCALE.md
Findings + decision trees for both the regression fix and the type-scale pass.
Reproduction Steps
pnpm check— thecheck:px-textguard passes; reintroducing an arbitrary text-size literal (e.g.text-[0.9rem]ortext-[16px]) anywhere indesktop/srcfails the build with a fix message naming the stock scale.Reviewer notes
Scope: the timeline rem fix, chat-as-base type scale, the app-wide px→rem sweep, DiffViewer, composer-highlight, and the sub-xs token/guard work all land together in this single PR. The composer-highlight fold resolved cleanly — chat-as-base and the composer commit independently arrived at the same
1remvalue.Verification (Marge, complete): full Desktop Core CI gate on the combined diff at tip
e5b10ce5(67 files) — biome (lint + formatting),pnpm check(incl. widened guard), typecheck, 719/719 tests passing, build — all green. Zoom behavior + 16/14/12 ramp confirmed in the running app across timeline, thread, composer pill, and diff viewer. The widened guard was live-tested: a reintroducedtext-[0.9rem]failed the build as designed, then passed clean on restore. Sub-xs round-up surfaces (badges, avatar initials, agents view) spot-checked at 1x + zoom150 — legible, no layout breakage.Non-blocking flag (Marge), resolved: retiring
text-codemeans multi-line code blocks inherit stocktext-smleading (~20px). Marge confirmed "reads fine as-is" against a real code block — noleading-6add needed.Coordination: this work is coordinated in the sprout-message-timeline room — buzz://message?channel=36411e44-0e2d-4cfe-bd6e-567eb169db9f&thread=08cb51ea453785155907c74292366cf5d447b023940cc3dd2715b4e73aa3c782 — dispatched at buzz://message?channel=36411e44-0e2d-4cfe-bd6e-567eb169db9f&id=08cb51ea453785155907c74292366cf5d447b023940cc3dd2715b4e73aa3c782&thread=08cb51ea453785155907c74292366cf5d447b023940cc3dd2715b4e73aa3c782
15px→1rem(text-base); mention chip →text-base. Original zoom bug — px froze zoom, rem revives it.1rem; echoes timeline chip, scales with zoom.DiffViewer.css15px→1rem(gutter + code). Diff content scales at zoom.badge.tsx0.625rem(10px) →text-2xs(11px) round-up; count badges + avatar initials → named tokens (UserAvatar0.5rem→text-3xs,0.5625rem→text-2xs).AgentActivityCardtimestamps unified totext-2xs(fixes 11px/10px same-role drift);PulseTabBar10.5px→text-2xs, count badge0.625rem→text-2xs.