docs/16-visual-references.md

16 — Visual References: Geist, Linear, Vercel (beyond t3code)

Why look past t3code: it sets the rendering language (directive 6) but not the component discipline. Vercel's Geist is a published token system (values included); Linear is the reference for the attention loop's interaction patterns (inbox, keyboard-first). Both constrain what "Linear-grade" means for docs/14 §7 without breaking the GNOME-citizen rule: translate patterns, never paste hex — Adwaita variables stay the source of truth so light/dark/accent/high-contrast keep following the desktop.

Sources (primary)

  • Geist foundations + components: https://vercel.com/geist/{colors,theme, button,badge,keyboard-input,command-menu,empty-state,…} and the shipped CSS custom properties (light + dark pairs) fetched 2026-09-29.
  • Linear product docs: https://linear.app/docs/{inbox,notifications}.md (llms.txt index), https://linear.app/llms.txt.
  • Typeface evidence: InterVariable.woff2 preloaded on linear.app; --font-geist-sans / --font-geist-mono stacks in Vercel CSS.

Geist tokens (exact values, light / dark)

Neutral ramp is pure gray, steps shared by both themes:

steplightdarkused for
background-100#fff#000canvas
background-200#fafafa#000sunken
gray-100#f2f2f2#1a1a1ahover (light) / wash
gray-200#ebebeb#1f1f1fhover (dark)
gray-400#eaeaea#2e2e2eborders
gray-700#a8a8a8#8f8f8fsecondary text / disabled fg
gray-1000#171717#edededprimary text

Accents are per-theme pairs (700 step): blue #0071f6 / #0070f7, red #fc0035 / #f13242, amber #ffb200, green #28a948 / #00ab3e, purple #9f00f4 / #9440d5, teal #00a794 / #00a694.

Elevation is layered, never a lone drop: --ds-shadow-border-small = 0 0 0 1px border-base + 0px 2px 2px …0a + 0px 8px 8px -8px …0a + background-border; dark hairlines go inset (#ffffff1a) so splits can't clip them — the same reason our pane rings are inset shadows.

Focus is a triple ring, instant (transition explicitly off on focus): 0 0 0 1px border, 0 0 0 2px background, 0 0 0 4px focus-color.

Components: buttons 32px tall, 6px radius, 14px medium, transitions 150ms ease-in-out over border/background/color/transform/box-shadow only; press = scale (no layout shift); kbd chips 20px tall, text-xs, rounded; headings tracked tight (letter-spacing: -0.32px at 16px); disabled = gray-700 on gray-100; empty states = icon + title + description + one action (exactly our AdwStatusPage shape).

Linear patterns (interaction, not hex — styles are CSS-in-JS)

  • Priority vs Other inbox tabs: the triage split our "Needs you" section already mirrors — validates the design, keep it.
  • Keyboard-first lists: J/K move, U toggles read, H snoozes, Backspace clears, G I jumps to inbox, Cmd/Ctrl F filters the list in place. Our sidebar has none of this: selection is pointer-only and Ctrl+Shift+J jumps globally without list context.
  • No browser-push novelty: Linear routes real-time alerts to the desktop app and digests the rest by urgency — our SIGNALTTY_NOTIFY kill-switch + 8s timeout + suppress-when-focused already follows this; Focus/Mark-read actions (§007) match their "act from the alert" loop.
  • Inter everywhere, tabular numerals in counts, 13px dense UI with 6–8px radii and 1px dividers over a near-black canvas.

Gap analysis vs gui/data/style.css

What already matches (do not touch): inset hairline rings, short pointer-only press feedback, status-vocabulary colors, AdwStatusPage empty states, hover-reveal row controls, .numeric counts.

#DeltaCostNote
1Visible keyboard focus, instant and inset on decision buttons, sidebar rows and header controlsComplete in spec009Native render regression verifies that inactive-pane controls appear under keyboard focus.
2Sidebar list keyboard nav (J/K move, U mark read on the selected row, type-to-filter?)GUI-onlyLinear parity for the attention loop; reuses pane.mark_seen + selection. Needs a display to verify.
3Command palette (Ctrl+K: jump to workspace/pane, run win.* actions)GUI-only, mediumSubsumes Ctrl+Shift+J; Geist command-menu + Linear ⌘K agree on the shape. Natural home for future workspace diff display too.
4Permission-orange audit: orange-3 hardcoded with one dark override vs Geist per-theme accent pairsCSS-onlyEither tune the pair or fold approval into the warning scale; eyeball light + dark.
5kbd chip style for shortcut hints in menus/tooltipsCSS-only, tinyGeist kbd pattern; cheap consistency win.
6Snooze (H)Model + IPC + GUIDeferred: needs a snoozed_until attention dimmer server-side (new state, new spec).
7Elevation layering (border + drop + bg-border)CSS-only, subtleOur single 0 1px 3px is flatter than both references; low priority.

Out of scope on purpose: Inter/Geist typefaces (system stack is the GNOME-citizen rule), pasted hex values (Adwaita owns color), per-client seen-states and turn-scoped diffs (ADR-0011 stands).

Recommendation

Spec009 completes focus and native presentation refinement, with light, dark, narrow and high-contrast evidence in UI verification. List-specific shortcuts and a command palette remain separate feature work; this reference does not authorize those interactions.

Icon and placement refinement, spec053

Apple toolbar guidance and GNOME header bars support contextual actions, restrained groups and spacing. GNOME symbolic icons support recognizable monochrome artwork on a 16px grid. Signaltty retains its original 1.5px family rather than replacing it with another icon library. Creation follows workspace context; Board and Changes form a view group; pane Close has a gap from split controls. Worktree window actions move to the header and checkout actions remain local. Native captures and explicit proof limits are recorded in spec053 verification.

Top bar refinement, spec057

GNOME header bars recommend start/title/end grouping, few contextual controls, spacing between functional groups and blank drag space. Apple toolbars prioritize frequent actions and clear separation of mixed symbols and labels. The native HeaderBar title lane gives identity the remaining space; Sidebar and New Tab lead, attention/views/menu trail. A concealed counter's Revealer reserved 35px in the baseline, so the counter now hides directly. Compact toolbar mode follows allocated content width rather than total window width, protecting the title when Changes is docked. New Tab, Board and Changes stay labeled in the compact sidebar. Native measurement, light/dark/RTL/enlarged-text/high-contrast proof and compositor limits are recorded in spec057 verification.

Sidebar app identity, spec058

GNOME app icons recommend a unique domain metaphor, limited detail at small sizes and a matching symbolic variant. Apple app icons recommend a simple core concept and consistent identity across contexts. Signaltty's original prompt with two parallel rails combines terminal input and parallel work. The static sidebar lockup uses a 20px monochrome mark and 600-weight system wordmark; launcher artwork uses a quiet solid tile and one brand hue. No extra tagline, hover/click treatment or branding animation is added. Native minimum-rail/enlarged/RTL/high-contrast and size-matrix proof is recorded in spec058 verification.