Build log · The Terminal
Brief
A product primer for a fictional keyboard-first notebook called Sable. The aesthetic target is
rauno.me / Linear / Vercel — monospace leaning, muted neutrals, every interaction designed for
hands on the home row. The reviewer should open this chapter and immediately reach for ⌘K.
Patterns used
command-menu-kbar— Cmd+K command palette with chapter navigation + theme togglekeyboard-hint-kbd— reusable<Kbd>chip +useHotkeyshookinline-footnote-hovercard— Radix HoverCard footnotes with progressive fallbackspring-theme-toggle— three-state light / system / dark pill with Framer MotionlayoutIdclippath-page-transitions— circle reveal on entry from the landing page
Decisions
The command menu is the whole point
Sable's narrative is that nothing wants a mouse. The command menu lives above everything, opens
on ⌘K or Ctrl+K, and exposes eight navigation targets plus a theme toggle. cmdk does the
fuzzy search and focus trap; we just inject our own visual grammar (Kbd chips on the right, group
headings in tracking-wide mono). The only customization worth calling out is the
data-[selected=true]:bg-[var(--color-accent)]/15 row highlight — it uses the chapter accent
token so the menu automatically reskins when the user swaps themes.
<Kbd> is a primitive, not a one-off component
keyboard-hint-kbd exposes both the component and the useHotkeys hook. A single vocabulary
of "this is a keyboard input" prevents drift — the command menu, the feature list, and any
future interaction all render kbd chips identically. The mod pseudo-key bridges mac/win
(cmd vs ctrl) so hotkeys stay symmetric across platforms.
Footnotes degrade to anchored notes
We don't ship the anchored fallback yet (stub for Phase 5), but the hovercard is built to tolerate
it. The <sup> element keeps its semantic role; the number is the truth; the hovercard is the
enhancement. On touch, Radix falls back to tap-to-open. The data-cursor-hover attribute makes
the site-wide cursor scale up when a footnote is in range — tiny affordance, big effect.
The spring toggle is a shared-layout trick
Three buttons, one active-state motion.span with a shared layoutId="theme-toggle-bubble". As
the user clicks through the options, the bubble springs between them — one DOM node, no
crossfades. The stiffness/damping is deliberately firm (420/32) because a soft spring on a tiny
UI control reads as laggy, not delightful.
Theme state is per-chapter, not site-wide
Only the Terminal chapter reacts to theme — the other chapters hold their own palettes. The
toggle writes data-theme="dark" onto the chapter wrapper, CSS handles the rest via a second
[data-chapter="terminal"][data-theme="dark"] rule. The command menu stays in sync because its
surface reads from --color-surface, which the theme rule overrides.
Tradeoffs rejected
- A
ThemeProvidercontext. Unnecessary — one data attribute on the chapter root is all the shared state we need. - Persisting theme to localStorage. Out of scope. The chapter is a showcase, not an app; resetting to light on reload is correct.
- Making the command menu site-wide. Tempting, but each chapter has its own aesthetic and
palette. Site-wide
cmdkwould dilute the Terminal chapter's identity.
Performance notes
cmdk,framer-motion, and@radix-ui/react-hover-cardare code-split to this route only. First-load JS on/terminalis around ~80 KB gzipped for the chapter-specific bundle.- The theme toggle's motion layer renders one
<span>— no layout thrash. - Hotkey listeners are installed inside
useEffectand cleaned up on unmount.
What I'd change in production
- Wire a real keyboard-shortcut reference dialog (
?opens it). - Add chord support to the navigate group so
G H/G E/G Sjump between chapters. - Persist theme per-session with
useSyncExternalStoreso the setting survives navigation. - Add a visible, always-on hint bar at the bottom of the viewport (
⌘K … · T theme · ? help) for first-time visitors.