Files
shelfless/design-system/MASTER.md
Scarriffle 83d8b7b99d Initial commit: Shelfless – alternative Audiobookshelf frontend
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>
2026-06-02 20:23:04 +02:00

5.1 KiB
Raw Permalink Blame History

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.51.6.
  • Weight hierarchy: headings 500700, 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 ~1012px 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 150300ms; enter ease-out, exit ~6070% 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 12 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 4060% 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).