Complete platform with unified design system and real API integration. Apps: Dashboard, Fitness, Budget, Inventory, Trips, Reader, Media, Settings Infrastructure: SvelteKit + Python gateway + Docker Compose
325 lines
14 KiB
Markdown
325 lines
14 KiB
Markdown
# Design System — Token Reference
|
||
|
||
> Source of truth: `src/app.css`
|
||
> Last updated: 2026-03-27
|
||
|
||
All UI values must come from these tokens unless listed under [Intentional Raw Values](#intentional-raw-values).
|
||
|
||
---
|
||
|
||
## Spacing
|
||
|
||
4px grid. Token name = multiplier (`--sp-3` = 3 × 4px = 12px).
|
||
|
||
| Token | Value | Use for |
|
||
|-------|-------|---------|
|
||
| `--sp-0` | 0px | Explicit zero |
|
||
| `--sp-px` | 1px | Borders, hairlines |
|
||
| `--sp-0.5` | 2px | Micro-nudge (margin-top on meta text) |
|
||
| `--sp-1` | 4px | Tight gap, field label gap, small padding |
|
||
| `--sp-1.5` | 6px | Badge gap, icon gap, footer gap |
|
||
| `--sp-2` | 8px | Compact gap, button group gap, inner padding |
|
||
| `--sp-3` | 12px | Standard gap, row padding, list item gap |
|
||
| `--sp-4` | 16px | Card padding (mobile), section margin, tab margin |
|
||
| `--sp-5` | 20px | Card padding (desktop), module gap, sidebar gap |
|
||
| `--sp-6` | 24px | Large padding, overlay padding, page container |
|
||
| `--sp-7` | 28px | Primary card padding, section group margin |
|
||
| `--sp-8` | 32px | Page top padding, empty state, desktop grid gap |
|
||
| `--sp-10` | 40px | Large elements (avatar width), empty list padding |
|
||
| `--sp-12` | 48px | Empty state padding, large spacing |
|
||
| `--sp-16` | 64px | Reserved |
|
||
| `--sp-20` | 80px | Page bottom padding (scroll clearance) |
|
||
|
||
### Semantic spacing aliases
|
||
|
||
| Token | Resolves to | Use for |
|
||
|-------|-------------|---------|
|
||
| `--section-gap` | `--sp-7` (28px) | Gap between major page sections |
|
||
| `--card-pad` | `--sp-5` (20px) | Default module/card padding |
|
||
| `--card-pad-primary` | `--sp-7` (28px) | Hero/primary module padding |
|
||
| `--card-pad-secondary` | `--sp-4` (16px) | Compact card padding |
|
||
| `--row-gap` | `--sp-3` (12px) | Gap between list rows |
|
||
| `--module-gap` | `--sp-5` (20px) | Gap between dashboard modules |
|
||
| `--row-pad-y` | 14px | Vertical padding inside data rows (off-grid, intentional) |
|
||
| `--row-pad-x` | `--sp-4` (16px) | Horizontal padding inside data rows |
|
||
| `--inner-gap` | `--sp-3` (12px) | Gap between items within a row |
|
||
|
||
### Mobile overrides (≤768px)
|
||
|
||
| Token | Desktop | Mobile |
|
||
|-------|---------|--------|
|
||
| `--card-pad` | 20px | 16px |
|
||
| `--card-pad-primary` | 28px | 20px |
|
||
| `--row-pad-y` | 14px | 16px |
|
||
| `--section-gap` | 28px | 20px |
|
||
|
||
---
|
||
|
||
## Radius
|
||
|
||
| Token | Value | Use for |
|
||
|-------|-------|---------|
|
||
| `--radius-xs` | 4px | Tiny pills, skeleton placeholders, kbd hints |
|
||
| `--radius-sm` | 6px | Badges, chips, nav links, danger buttons |
|
||
| `--radius-md` | 8px | Buttons, inputs, tabs, entry rows, icon containers |
|
||
| `--radius` | 12px | Cards, modals, panels, main containers |
|
||
| `--radius-lg` | 16px | Hero cards, action cards, pill chips, ImmichPicker modal |
|
||
| `--radius-full` | 9999px | Circles, toggles, avatars |
|
||
|
||
---
|
||
|
||
## Elevation (Shadows)
|
||
|
||
Light and dark mode have separate values. Dark mode uses higher opacity.
|
||
|
||
| Token | Light | Use for |
|
||
|-------|-------|---------|
|
||
| `--shadow-xs` | `0 1px 2px rgba(0,0,0,0.03)` | Row hover, active tabs, inner elements |
|
||
| `--shadow-sm` | `0 1px 3px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.04)` | Secondary cards, inputs, budget tables |
|
||
| `--shadow-md` | `0 2px 6px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06)` | Standard card elevation (default `.module`) |
|
||
| `--shadow-lg` | `0 4px 12px rgba(0,0,0,0.06), 0 16px 40px rgba(0,0,0,0.1)` | Hero cards, primary modules, dropdowns |
|
||
| `--shadow-xl` | `0 8px 24px rgba(0,0,0,0.08), 0 24px 60px rgba(0,0,0,0.15)` | Modals, overlay panels |
|
||
|
||
**Legacy aliases**: `--card-shadow` → `--shadow-md`, `--card-shadow-sm` → `--shadow-sm`
|
||
|
||
---
|
||
|
||
## Colors
|
||
|
||
### Surfaces (3-layer depth)
|
||
|
||
| Token | Light | Dark | Layer |
|
||
|-------|-------|------|-------|
|
||
| `--canvas` | `#F5F6F8` | `#09090b` | Page background — everything sits on this |
|
||
| `--surface` | `#FFFFFF` | `#0f0f12` | Sidebars, panels, slide-out sheets |
|
||
| `--surface-secondary` | `#FAFAFB` | `#111114` | Input backgrounds, secondary panels |
|
||
| `--card` | `#FFFFFF` | `#161619` | Content containers, elevated with shadow |
|
||
| `--card-secondary` | `#FAFAFB` | `#111114` | Secondary cards, button backgrounds |
|
||
| `--card-hover` | `#f0f0f3` | `#1c1c20` | Row hover, interactive feedback |
|
||
|
||
### Borders
|
||
|
||
| Token | Light | Dark |
|
||
|-------|-------|------|
|
||
| `--border` | `rgba(0,0,0,0.06)` | `rgba(255,255,255,0.06)` |
|
||
| `--border-strong` | `rgba(0,0,0,0.1)` | `rgba(255,255,255,0.1)` |
|
||
|
||
### Text hierarchy
|
||
|
||
| Token | Light | Dark | Use for |
|
||
|-------|-------|------|---------|
|
||
| `--text-1` | `#1a1a1f` | `#fafafa` | Headings, names, amounts — read first |
|
||
| `--text-2` | `#4a4a55` | `#a1a1aa` | Body text, descriptions — read second |
|
||
| `--text-3` | `#6b6b76` | `#71717a` | Labels, metadata, captions — supporting |
|
||
| `--text-4` | `#b4b4bd` | `#3f3f46` | Placeholders, disabled, timestamps — background |
|
||
|
||
### Accent (indigo / blue)
|
||
|
||
| Token | Light | Dark | Use for |
|
||
|-------|-------|------|---------|
|
||
| `--accent` | `#4F46E5` | `#3b82f6` | Primary actions, links, active states |
|
||
| `--accent-bg` | `#EEF2FF` | `rgba(59,130,246,0.1)` | Icon wells, strong highlight backgrounds |
|
||
| `--accent-dim` | `rgba(79,70,229,0.06)` | `rgba(59,130,246,0.08)` | Subtle hover, selection backgrounds, focus rings |
|
||
| `--accent-border` | `rgba(79,70,229,0.10)` | `rgba(59,130,246,0.12)` | Accent-tinted borders |
|
||
| `--accent-focus` | `rgba(79,70,229,0.12)` | `rgba(59,130,246,0.15)` | Active states, selection bars |
|
||
|
||
### Semantic colors
|
||
|
||
| Token | Light | Dark | Use for |
|
||
|-------|-------|------|---------|
|
||
| `--success` | `#16A34A` | `#22c55e` | Positive values, income, completed |
|
||
| `--success-bg` | `#F0FDF4` | `rgba(34,197,94,0.1)` | Icon wells |
|
||
| `--success-dim` | `rgba(34,197,94,0.08)` | `rgba(34,197,94,0.08)` | Badge backgrounds |
|
||
| `--error` | `#DC2626` | `#ef4444` | Errors, issues, delete actions |
|
||
| `--error-bg` | `#FEF2F2` | `rgba(239,68,68,0.1)` | Icon wells |
|
||
| `--error-dim` | `rgba(239,68,68,0.08)` | `rgba(239,68,68,0.08)` | Badge backgrounds |
|
||
| `--warning` | `#d97706` | `#f59e0b` | Warnings, pending states |
|
||
| `--warning-bg` | `rgba(245,158,11,0.08)` | `rgba(245,158,11,0.1)` | Badge backgrounds |
|
||
|
||
### Overlay
|
||
|
||
| Token | Light | Dark | Use for |
|
||
|-------|-------|------|---------|
|
||
| `--overlay` | `rgba(0,0,0,0.3)` | `rgba(0,0,0,0.6)` | Modal backdrop, reading pane overlay |
|
||
| `--overlay-strong` | `rgba(0,0,0,0.5)` | `rgba(0,0,0,0.75)` | Heavy overlays |
|
||
| `--nav-bg` | `rgba(255,255,255,0.9)` | `rgba(15,15,18,0.9)` | Navbar blur background |
|
||
|
||
---
|
||
|
||
## Typography
|
||
|
||
| Token | Desktop | Mobile (≤768px) | Use for |
|
||
|-------|---------|-----------------|---------|
|
||
| `--text-xs` | 11px | 12px | Badges, pills, tiny counters |
|
||
| `--text-sm` | 13px | 15px | Labels, meta, captions, button text |
|
||
| `--text-base` | 14px | 16px | Body text, list items, inputs |
|
||
| `--text-md` | 15px | 17px | Card titles, important rows (16px+ avoids iOS zoom) |
|
||
| `--text-lg` | 17px | 18px | Section headers, modal titles |
|
||
| `--text-xl` | 22px | 22px | Page titles |
|
||
| `--text-2xl` | 28px | 26px | Hero headings |
|
||
| `--text-3xl` | 36px | 32px | Large hero numbers |
|
||
|
||
### Line heights
|
||
|
||
| Token | Value | Use for |
|
||
|-------|-------|---------|
|
||
| `--leading-tight` | 1.2 | Headings, hero numbers |
|
||
| `--leading-snug` | 1.35 | Card titles, compact text |
|
||
| `--leading-normal` | 1.5 | Body text |
|
||
| `--leading-relaxed` | 1.65 | Article content |
|
||
| `--leading-loose` | 1.8 | Long-form reading |
|
||
|
||
### Fonts
|
||
|
||
| Token | Value |
|
||
|-------|-------|
|
||
| `--font` | `'Inter', -apple-system, system-ui, sans-serif` |
|
||
| `--mono` | `'JetBrains Mono', ui-monospace, monospace` |
|
||
|
||
---
|
||
|
||
## Global Component Classes
|
||
|
||
Defined in `app.css`, usable in any component without local `<style>` duplication.
|
||
|
||
| Class | Description |
|
||
|-------|-------------|
|
||
| `.module` | Card container (bg, border, shadow, padding) |
|
||
| `.module.primary` | Hero card (more padding + elevation) |
|
||
| `.module.flush` | No padding (for flush content) |
|
||
| `.module-header` | Flex header row (title left, action right) |
|
||
| `.module-title` | Uppercase label |
|
||
| `.module-action` | Accent link → "View all →" |
|
||
| `.data-row` | Standard list item with hover + zebra striping |
|
||
| `.badge` + `.error/.success/.warning/.accent/.muted` | Semantic status badges |
|
||
| `.tab-bar` + `.tab` + `.tab-badge` | Pill-style tab navigation |
|
||
| `.section-label` | Uppercase group header |
|
||
| `.btn-primary` / `.btn-primary.full` | Primary action buttons |
|
||
| `.btn-secondary` | Secondary action buttons |
|
||
| `.btn-icon` | Square icon button (36×36) |
|
||
| `.input` | Standard text input |
|
||
| `.skeleton` | Shimmer loading placeholder |
|
||
| `.page` / `.page-header` / `.page-title` / `.page-greeting` | Page-level layout |
|
||
| `.app-surface` | Centered max-width container |
|
||
|
||
---
|
||
|
||
## Intentional Raw Values
|
||
|
||
These values exist outside the token system by design. Do not convert them.
|
||
|
||
### Non-scale spacing
|
||
|
||
Values that don't land on the 4px grid. Used for optical tuning where grid steps are too coarse.
|
||
|
||
| Value | Where | Why |
|
||
|-------|-------|-----|
|
||
| `1px` | `margin-top` on meta text, feed separators | Sub-pixel nudge for vertical alignment |
|
||
| `3px` | Badge padding-y, kbd padding | Optical centering within small elements |
|
||
| `5px` | Pill padding-y, chip padding | Between sp-1 (4px) and sp-1.5 (6px), tuned per element |
|
||
| `7px` | View-btn padding-y, sidebar gap | Between sp-1.5 (6px) and sp-2 (8px) |
|
||
| `9px` | Nav item padding-y, suggestion row padding-y | Between sp-2 (8px) and 10px |
|
||
| `10px` | Input padding-y, dropdown padding, various gaps | Common "comfortable touch" size, between sp-2 and sp-3 |
|
||
| `11px` | Entry row padding-y, toggle btn padding-bottom | Asymmetric optical alignment |
|
||
| `13px` | FAB action padding-y, list row padding | Between sp-3 (12px) and 14px |
|
||
| `14px` | Button padding-x, row padding-x, modal gap, field row gap | Most common off-grid value. Used as standard horizontal rhythm for interactive elements. Also `--row-pad-y` for data rows. |
|
||
| `15px` | Transaction row padding-y | Between sp-3.5 and sp-4, tuned for readability |
|
||
| `18px` | Detail header margin-bottom, AI guide padding-top | Between sp-4 (16px) and sp-5 (20px) |
|
||
| `22px` | Trip stats padding-x, modal body padding | Between sp-5 (20px) and sp-6 (24px) |
|
||
|
||
### Non-scale border-radius
|
||
|
||
| Value | Where | Why |
|
||
|-------|-------|-----|
|
||
| `9px` | Status segment control inner radius | Between radius-md (8px) and radius (12px) |
|
||
| `10px` | Event cards, photo thumbnails, notes, modals, unscheduled items, food rows | Heavily used "soft card" radius. Between radius-md (8px) and radius (12px). |
|
||
| `14px` | CommandPalette box, toggle track | Large interactive controls |
|
||
| `20px` | Bottom sheet top corners | Extra-round for sheet feel |
|
||
| `50%` | Circular elements (cover nav dots, meal-number, image-delete) | True circle, differs from radius-full on non-square elements |
|
||
|
||
### Data visualization colors
|
||
|
||
These are visual category identifiers, not semantic UI colors. They must remain consistent within their visualization set, independent of theme.
|
||
|
||
**Fitness macros:**
|
||
| Color | Hex | Use |
|
||
|-------|-----|-----|
|
||
| Protein | `#8B5CF6` | Purple macro bar |
|
||
| Carbs | `#F59E0B` | Amber macro bar |
|
||
| Fat | `#3B82F6` | Blue macro bar |
|
||
|
||
**Fitness meal weights:**
|
||
| Color | Background | Text | Meaning |
|
||
|-------|------------|------|---------|
|
||
| Heavy | `rgba(239,68,68,0.1)` | `#DC2626` | High-calorie meal |
|
||
| Moderate | `rgba(245,158,11,0.1)` | `#B45309` | Medium-calorie meal |
|
||
| Light | `rgba(34,197,94,0.1)` | `#15803D` | Low-calorie meal |
|
||
|
||
**Trip categories:**
|
||
| Color | Background | Text | Category |
|
||
|-------|------------|------|----------|
|
||
| Hotel | `rgba(168,85,247,0.1)` | `#a855f7` | Lodging |
|
||
| Restaurant | `rgba(249,115,22,0.1)` | `#f97316` | Food & dining |
|
||
| Hike | `rgba(34,197,94,0.15)` | `#16a34a` | Outdoor activity |
|
||
| Logistics | `rgba(161,161,170,0.1)` | `--text-3` | Transport, other |
|
||
|
||
**Other:**
|
||
| Color | Hex | Use |
|
||
|-------|-----|-----|
|
||
| Favorite star | `#F59E0B` | Star icon fill (reader, fitness) |
|
||
| AI badge | `rgba(59,130,246,0.1)` / `#3B82F6` | AI-logged entry indicator |
|
||
| Transfer pill | `rgba(59,130,246,0.1)` / `#3b82f6` | Budget transfer indicator |
|
||
|
||
### Glass & overlay effects
|
||
|
||
Applied to elements layered over images. Not theme-switchable because they depend on photo content, not UI surface.
|
||
|
||
| Value | Where |
|
||
|-------|-------|
|
||
| `rgba(0,0,0,0.7)..0.1` | Cover image gradient (trips) |
|
||
| `rgba(255,255,255,0.15)` | Cover nav button background |
|
||
| `rgba(255,255,255,0.3)` | Cover nav button hover |
|
||
| `rgba(0,0,0,0.3)` / `rgba(0,0,0,0.5)` | Cover share button / hover |
|
||
| `rgba(0,0,0,0.35)` | Modal overlays (trips), detail overlay (inventory) |
|
||
| `rgba(0,0,0,0.5)` | Image delete button, search saving overlay |
|
||
|
||
### Directional panel shadows
|
||
|
||
Non-standard shadow directions for slide-in panels and bottom sheets. Cannot use elevation tokens.
|
||
|
||
| Value | Where |
|
||
|-------|-------|
|
||
| `0 -8px 32px rgba(0,0,0,0.12)` | FAB bottom sheet (fitness, trips) |
|
||
| `-12px 0 40px rgba(0,0,0,0.12)` | Detail slide-in sheet (inventory) |
|
||
| `-6px 0 28px rgba(0,0,0,0.08)` | Reading pane (reader) |
|
||
| `-8px 0 32px rgba(0,0,0,0.1)` | Edit sheet (trips) |
|
||
| `8px 0 24px rgba(0,0,0,0.08)` | Mobile sidebar (reader) |
|
||
|
||
### Accent-tinted FAB shadows
|
||
|
||
Colored shadows for floating action buttons. Not in the elevation scale because they use accent color, not black.
|
||
|
||
| Value | Where |
|
||
|-------|-------|
|
||
| `0 8px 24px rgba(79,70,229,0.3)` | FAB resting (fitness) |
|
||
| `0 12px 32px rgba(79,70,229,0.4)` | FAB hover (fitness) |
|
||
| `0 6px 20px rgba(79,70,229,0.3)` | FAB resting (trips) |
|
||
| `0 8px 28px rgba(79,70,229,0.4)` | FAB hover (trips) |
|
||
|
||
### Near-match shadows
|
||
|
||
Shadows that are close to tokens but intentionally differ in blur radius or opacity.
|
||
|
||
| Value | Nearest token | Diff |
|
||
|-------|--------------|------|
|
||
| `0 2px 6px rgba(0,0,0,0.05)` | `--shadow-xs` | Larger blur, higher opacity |
|
||
| `0 1px 2px rgba(0,0,0,0.04)` | `--shadow-xs` | Opacity 0.04 vs 0.03 |
|
||
| `0 1px 4px rgba(0,0,0,0.04)` | `--shadow-xs` | Larger blur |
|
||
| `0 1px 4px rgba(0,0,0,0.08)` | `--shadow-xs` | Larger blur + opacity |
|
||
| `0 16px 48px rgba(0,0,0,0.15)` | `--shadow-xl` | Single layer vs dual |
|
||
| `0 1px 3px rgba(0,0,0,0.06)` | `--shadow-xs` | Higher opacity |
|
||
| `0 8px 24px rgba(0,0,0,0.12)` | `--shadow-md` | Higher opacity |
|
||
| `0 20px 60px rgba(0,0,0,0.15)` | `--shadow-xl` | Single layer |
|
||
| `0 20px 60px rgba(0,0,0,0.2)` | `--shadow-xl` | Single layer, higher opacity |
|
||
| `0 1px 3px rgba(0,0,0,0.15)` | `--shadow-sm` | Toggle thumb, much higher opacity |
|