Source of truth for visual decisions. The design kit lives at
.context/design-handoff/herocast-design-system/ — this document is the
codebase-local distillation engineers actually read.
Power user tools for decentralized social. Warm near-monochrome product, reserved color, keyboard-first throughout.
- Brand name is lowercase:
herocast. NeverHerocast, neverHeroCast. - Tokens are the source of truth. Never inline
#hex,rgb(...),hsl(...), or raw Tailwind palette utilities (bg-red-500,text-blue-400) in product chrome. Resolve throughhsl(var(--token))or a semantic Tailwind class (bg-primary,text-muted-foreground,text-mention,text-channel,text-destructive,text-success,text-warning,text-info). Thepnpm checkaggregator includes adesign-tokensadapter that flags leaks — keep that file at zero warnings if you touched it. - Brand-blue (
--mention) is reserved. It carries@mentions, links, the primary chart series, and inbox unread notifications (via--info, same swatch). Nothing else. - Channels are violet (
--channel), never blue./channelreferences, the in-feed channel chip, and the channel highlight in linkified casts all usetext-channel/bg-channel/10. This separation lets users tell people from venues at a glance. - Type pairing. Inter for body and UI (
var(--font-sans), applied athtml). Satoshi (var(--font-display)) is display only — the wordmark and rare marketing takeover headlines. JetBrains Mono (var(--font-mono)) for addresses, hashes, FIDs, and kbd glyphs. Don't swap them. - Lucide is the sole icon library. Stroke 1.5 px (set globally on
.lucide). No emoji or unicode glyphs as icons in product chrome — emoji inside a cast body is user content and stays. When you import an icon, double-check the Lucide name doesn't shadownext/image,next/link, or a SupabaseUsertype (alias toXxxIconif it does). - Buttons carry the herocast signature.
h-[42px]default,rounded-lg(10 px),font-semibold. Filled variants useshadow-button-press(themed for light/dark), outline usesshadow-button-inset, and all shadow-carrying variants presstranslateY(1px)on:active. Don't inlineboxShadow— extend--shadow-button-*instead. - Focus ring is a 2 px outline with 2 px offset. Defined once in
src/globals.cssvia:where(...):focus-visible { outline: 2px solid hsl(var(--ring)/0.7); outline-offset: 2px; }. Don't add per-componentfocus-visible:ring-*chains — they suppress the global rule. - Motion is fast and quiet.
--duration-instant: 80ms(row hover, kbd press),--duration-fast: 150ms(button press, accordion),--duration-base: 220ms(dialog, sheet, popover),--duration-slow: 320ms(full-screen takeover). Easing is--ease-out: cubic-bezier(0.16, 1, 0.3, 1)for surfaces and--ease-in-outfor chrome. No bounces. - Z-index uses tokens.
--z-dropdown: 30 < --z-popover: 40 < --z-modal: 50 < --z-toast: 60 < --z-tooltip: 70 < --z-command: 80. No magic numbers.
All tokens live in src/globals.css (:root / .dark) and are exposed to
Tailwind via tailwind.config.js. See those two files for the canonical list.
Categories:
- Surfaces & roles:
--background,--foreground,--card,--popover,--primary,--secondary,--muted,--accent(each with-foreground). - Status:
--destructive,--success,--warning,--info,--pending. - Information accents:
--mention,--channel,--hash. - Lines & focus:
--border,--input,--ring. - Sidebar family:
--sidebar-*(warm cream variants — don't substitute the regular surface tokens on sidebar chrome). - Marketing:
--brand-purple,--brand-purple-deep(gradient endpoints for paid-plan CTAs and marketing surfaces only). - Charts:
--chart-1..5(categorical: blue, near-black, violet, green, amber). - Type scale:
--text-{xs..6xl},--weight-{regular,medium,semibold,bold}. - Radius:
--radius-{xs(4), sm(6), md(8), lg(10), xl(12), 2xl(16), full}. Buttons uselg(10 px). Cards usexl(12 px). Modals use2xl(16 px). - Density (opt-in via
<html data-density="compact|comfortable">):--density-row,--density-body,--density-avatar,--density-pad-{x,y}. - Alpha overlays:
--alpha-hover(row/button hover wash),--alpha-mention-fill(0.08),--alpha-channel-fill(0.10),--alpha-highlight(button inset highlight strength, themed). - Shadows:
--shadow-{xs,sm,(default),md,lg}, plus button-specific--shadow-button-insetand--shadow-button-press.
First-class citizens, not optional polish:
- Command palette (
⌘K) — Radix Dialog over backdrop, max-w-640, max-h-70vh, sits at--z-command. Per-row shortcut hint via the canonicalKbd/KbdGroup. - Kbd / KbdGroup (
src/components/ui/kbd.tsx) — the single keyboard hint renderer. Chord rendering by default (⌘ K); passsequenceforg › fordered shortcuts. - Density modes — three states via
[data-density]. Tokens are wired; a settings toggle is a planned follow-up.
- Direct. Confident, slightly contrarian. Reader is already a power-user peer.
- Sentence case for buttons, nav, labels (
New draft,Scheduled,Manage). Title Case is rare and reserved for marketing display headlines. - In-product copy is terse. One- or two-word labels (
Post,Feeds,Channels,Search). Empty states are matter-of-fact, no exclamations, no hype words.
- Check whether an existing semantic token fits. Most "I need a red here"
moments are
text-destructive; "I need a green check" istext-success. - If a new semantic token is needed, add it to both
:rootand.darkinsrc/globals.css, then expose it undertheme.extend.colorsintailwind.config.js. Don't ship a token that only exists in one theme. - Never reach for a raw Tailwind palette utility (
bg-red-500) as a quick fix. Thedesign-tokensadapter flags it on the nextpnpm check. - The
EXEMPT_FILESset inscripts/check/adapters/design-tokens.tsis for files where raw color literals are genuinely intentional (PWA manifest, external brand SVGs, dev-only panels). Add to it sparingly and document the reason on the same line.
See .context/design-system-migration-plan.md for the original ordered
checklist and .context/design-handoff/herocast-design-system/project/MIGRATION.md
for the kit's breaking-change list. The migration that landed in
hellno/design-system-migration covered tokens, fonts, button signature,
channel-chip violet flip, heroicons→Lucide sweep, focus-ring unification,
sidebar token wiring, CommandPalette tokenization, Kbd consolidation, and
the design-tokens harness. Remaining deliberately deferred:
- Web3 typed-data primitives (
Address,Hash,Fid,Ens) undersrc/components/web3/— pure rendering primitives, not blocking the upgrade. - Density toggle UI in settings — tokens live, surface is planned.
?hotkey overlay — explicitly scoped out.- Button
lgsize — kept for ~12 hero CTAs that need extra weight; spec technically says onlysm+ default.