# Shelfless — Design System (MASTER) Single source of truth for visual + interaction design. Grounded in ui-ux-pro-max (Podcast Platform #27 + Music Streaming #45 + Book & Reading Tracker #135 → **Dark Mode (OLED) + Minimalism** with a literary-warm note) and the project brief. When building any page: read this file first. A page-specific override under `design-system/pages/.md` (if present) takes precedence over these rules. --- ## Principles - Dark theme by default. One consistent accent color, never competing accents. - Generous whitespace, clear type hierarchy, content (covers) first. - Subtle micro-animations only (cover hover, player slide-in). No motion overload. - NO AI-slop: no purple gradients, no glassmorphism overload, no card-in-card nesting. - NO online badge, user counter, activity feed, or "welcome back" banner. - Reference feel: Plex overview (but cleaner), Spotify player (but less cluttered). ## Typography Both verified as Google variable fonts. **No Inter / Roboto / Space Grotesk.** - Headings / titles / section headers: **Fraunces** (serif, literary, optical sizing). - Body / UI: **Hanken Grotesk** (humanist grotesque, highly readable). - Numbers in player timers, durations, data tables: `font-variant-numeric: tabular-nums`. - Type scale (px): 12 · 14 · 16 · 18 · 24 · 32 · 48. Body 16px, line-height 1.5–1.6. - Weight hierarchy: headings 500–700, body 400, labels/nav-active 500. - Loaded locally via `@fontsource-variable/fraunces` + `@fontsource-variable/hanken-grotesk` (no hard CDN dependency); `font-display: swap`. ## Color tokens (CSS custom properties on `:root`, dark default) All foreground/background pairs verified ≥4.5:1 (text) / ≥3:1 (large/UI). | Token | Value | Use | |------------------|-----------|--------------------------------------| | `--bg` | `#0C0A09` | app background (warm near-black) | | `--surface` | `#1C1917` | cards, sidebar, panels | | `--surface-2` | `#292524` | raised surfaces, player bar, inputs | | `--border` | `#3F3B38` | borders/dividers (visible in dark) | | `--text` | `#FAFAF9` | primary text | | `--text-muted` | `#A8A29E` | secondary text (≥4.5:1 on `--bg`) | | `--accent` | `#F59E0B` | accent (default Amber) | | `--on-accent` | `#0C0A09` | text/icon on accent fills | | `--destructive` | `#EF4444` | delete / danger | | `--success` | `#22C55E` | progress / completed | Accent is themeable via `data-accent` on ``; options in Settings: - `amber` #F59E0B (default) · `teal` #14B8A6 · `coral` #FB7185 · `green` #22C55E · `blue` #3B82F6. Tailwind maps `bg`, `surface`, `surface-2`, `border`, `text`, `text-muted`, `accent`, `on-accent`, `destructive`, `success` to these variables (`colors` in tailwind.config). ## Spacing, radius, elevation - 4/8px spacing rhythm. Section spacing tiers 16 / 24 / 32 / 48. - Radius ~10–12px on cards/buttons/inputs (`--radius: 0.625rem`). 0px never; no huge pills. - Elevation: one consistent shadow scale; modals/player use a slightly stronger shadow. Avoid random shadow values. ## Motion (respect `prefers-reduced-motion`) - Durations 150–300ms; enter ease-out, exit ~60–70% shorter. - Animate only `transform` / `opacity` (never width/height/top/left). - Cover hover: `scale(1.0 → 1.03)` + subtle lift/shadow. - Player bar: slide-up + fade on first appearance. - Skeleton shimmer while loading (>300ms). Visible 2px accent focus ring. - Max 1–2 animated elements per view. ## Component guidelines - **MediaCard**: cover (book 2:3 / podcast 1:1), rounded, hover-lift. Progress strip at bottom in `--accent`, shown only when progress > 0%. Subtle check overlay when finished. - **Sidebar**: `--surface` bg; active item = accent indicator bar + medium weight; always icon + label (never icon-only); collapsible to icon mode; on mobile → bottom nav (≤5). - **PlayerBar**: fixed bottom, `--surface-2`; tabular-nums times; draggable progress bar with accent only on the playhead/fill; touch targets ≥44px. - **Grid**: responsive `auto-fill minmax(...)`, `gap-6`. Virtualize only if a single page renders 50+ items (infinite scroll paginates, so usually fine). - **Modal**: scrim 40–60% black; animate from trigger (scale+fade); Esc/close affordance; focus trap + return focus; confirm before destructive/unsaved-dismiss. - **Table** (admin users): `aria-sort`, tabular-nums, row hover; destructive actions visually separated from normal ones. - **Forms**: visible labels (not placeholder-only), error below field, helper text, loading state on submit, password show/hide toggle, autofocus first invalid field. ## Icons Lucide React only. Consistent stroke width; one icon size scale (sm/md=24/lg). No emoji. ## Anti-patterns (do not do) Purple/indigo gradients · glassmorphism overload · card-in-card-in-card · emoji icons · online/user-count/activity-feed/welcome banners · icon-only nav · hover-only interactions · animating layout properties · gray-on-gray low contrast · raw hex in components (use tokens).