React + Vite + TypeScript SPA covering the full ABS feature set (library browsing, item detail, metadata/cover editing, podcasts, player with session sync, admin: users/libraries/scanner/server settings). Dev uses a dynamic CORS proxy; production is served by server/index.mjs (static + reverse proxy to ABS_URL). Includes systemd unit and installer under deploy/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
89 lines
5.1 KiB
Markdown
89 lines
5.1 KiB
Markdown
# 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/<page>.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 `<html>`; 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).
|