|
| 1 | +## Design Language |
| 2 | + |
| 3 | +A hand-crafted CSS design system with two theme modes (dark consumer-facing, |
| 4 | +light admin/internal). Pure CSS custom properties — no Tailwind, no preprocessor, |
| 5 | +no component library. Icons via Font Awesome 6. Modern CSS features assumed |
| 6 | +(`color-mix()`, `backdrop-filter`, `:has()`, `dvh` units, `env()` safe-area insets). |
| 7 | + |
| 8 | +### Color Primitives |
| 9 | + |
| 10 | +**Dark theme (consumer-facing)** |
| 11 | + |
| 12 | +| Role | Value | Notes | |
| 13 | +|-----------------|-----------|--------------------------------| |
| 14 | +| Background | `#121e2b` | Primary surfaces, cards | |
| 15 | +| Background deep | `#0f1a26` | Deepest layer, inset regions | |
| 16 | +| Input surface | `#374151` | Form controls, select fields | |
| 17 | +| Text primary | `#e5e7eb` | Body copy, headings | |
| 18 | +| Border | `#374151` | Dividers, card edges | |
| 19 | +| Accent | `#fba919` | CTAs, links, focus rings, stars| |
| 20 | +| Secondary CTA | `#7c3aed` | Alternate action (hover: `#6d28d9`) | |
| 21 | +| Destructive | `#ef4444` | Delete, favorite active, error | |
| 22 | +| Positive | `#4ade80` | User-contributed highlights | |
| 23 | +| Star empty | `#2d3a49` | Unfilled rating slots | |
| 24 | +| Placeholder | `#9ca3af` | Input placeholder text | |
| 25 | +| Overlay dark | `#0f172a` | Text-on-accent, dark contrast | |
| 26 | + |
| 27 | +**Light theme (admin/internal)** |
| 28 | + |
| 29 | +| Role | Value | Notes | |
| 30 | +|------------------|-----------|--------------------------------| |
| 31 | +| Background | `#ffffff` | Page & card surface | |
| 32 | +| Surface | `#f5f5f7` | Subtle backgrounds, table head | |
| 33 | +| Surface hover | `#e8e8ed` | Row hover, button hover | |
| 34 | +| Text primary | `#1d1d1f` | Headings, body | |
| 35 | +| Text secondary | `#6e6e73` | Descriptions, metadata | |
| 36 | +| Text muted | `#86868b` | Placeholders, disabled | |
| 37 | +| Border | `#d2d2d7` | Inputs, cards, dividers | |
| 38 | +| Action | `#0071e3` | Primary buttons, links | |
| 39 | +| Action hover | `#0077ed` | Hover state | |
| 40 | +| Row hover | `#fafafa` | Table row highlight | |
| 41 | + |
| 42 | +**Semantic status colors (both themes)** |
| 43 | + |
| 44 | +| Status | Fill | Background | Text | |
| 45 | +|---------|-------------|-------------|------------| |
| 46 | +| Success | `#34c759` | `#d1f4e0` | `#0a5e2a` | |
| 47 | +| Error | `#ff3b30` | `#ffe5e5` | `#c41e3a` | |
| 48 | +| Warning | — | `#fff3cd` | `#856404` | |
| 49 | +| Info | `#60a5fa` | `#dbeafe` | `#1e40af` | |
| 50 | + |
| 51 | +**Grade scale (A–F data visualization)** |
| 52 | + |
| 53 | +| Grade | Color | Text | |
| 54 | +|-------|-----------|-----------| |
| 55 | +| A | `#34c759` | white | |
| 56 | +| B | `#7dc734` | white | |
| 57 | +| C | `#facc15` | `#1d1d1f` | |
| 58 | +| D | `#f97316` | white | |
| 59 | +| F | `#b91c1c` | white | |
| 60 | + |
| 61 | +**OEM/featured gradient:** `linear-gradient(135deg, #047857, #10b981, #34d399)` |
| 62 | + |
| 63 | +### Typography |
| 64 | + |
| 65 | +``` |
| 66 | +Sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, |
| 67 | + Ubuntu, Cantarell, "Helvetica Neue", sans-serif |
| 68 | +Mono: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, |
| 69 | + 'Courier New', monospace |
| 70 | +``` |
| 71 | + |
| 72 | +| Scale | Size | Weight | Extras | |
| 73 | +|-----------------|--------|--------|---------------------------------------| |
| 74 | +| Page title | 32px | 600 | line-height 1.2 | |
| 75 | +| Stat value | 28px | 600 | line-height 1.2, tabular-nums | |
| 76 | +| Section title | 20px | 600–700| — | |
| 77 | +| Modal title | 18px | 700 | — | |
| 78 | +| Subhead | 16px | 600–700| — | |
| 79 | +| Field label | 15px | 500 | — | |
| 80 | +| Body | 14–15px| 400–500| line-height 1.4–1.6 | |
| 81 | +| Small / meta | 13px | 500–600| — | |
| 82 | +| Badge / caption | 12px | 600 | — | |
| 83 | +| Section label | 11px | 600–700| uppercase, letter-spacing 0.3–0.8px | |
| 84 | +| Micro badge | 9px | 700 | uppercase, letter-spacing 0.5px | |
| 85 | + |
| 86 | +Antialiasing: `-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale` |
| 87 | + |
| 88 | +### Spacing |
| 89 | + |
| 90 | +**Base increments used:** 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 32, 40, 60px |
| 91 | + |
| 92 | +| Context | Value | |
| 93 | +|----------------------------|-----------------| |
| 94 | +| Card padding (mobile) | 12–16px | |
| 95 | +| Card padding (desktop) | 20–24px | |
| 96 | +| Card header padding | 20px 24px | |
| 97 | +| Card gap (between items) | 12px | |
| 98 | +| Grid gap | 16–20px | |
| 99 | +| Section margin-bottom | 20–24px | |
| 100 | +| Form field vertical rhythm | 20px padding-block | |
| 101 | +| Modal padding | 16–24px | |
| 102 | +| Toast padding | 14px 20px | |
| 103 | +| Inline element gap | 6–8px | |
| 104 | +| Action button padding | 8–16px vert, 16–24px horiz | |
| 105 | +| CTA button padding | 10px 18px | |
| 106 | +| Compact cell padding | 8–12px | |
| 107 | + |
| 108 | +### Border Radius |
| 109 | + |
| 110 | +| Token | Value | Usage | |
| 111 | +|---------|----------|------------------------------------| |
| 112 | +| `xs` | `4px` | Tags, pills, small code blocks | |
| 113 | +| `sm` | `6px` | Badges, small buttons, review pills| |
| 114 | +| `md` | `8px` | Buttons, inputs, standard elements | |
| 115 | +| `lg` | `10px` | Filter sections, callouts, toasts | |
| 116 | +| `xl` | `12px` | Cards, modals, floating bars | |
| 117 | +| `2xl` | `16px` | Full modals, chips (pill-shaped) | |
| 118 | +| `pill` | `20px` | Filter chips, rounded pill shapes | |
| 119 | +| `toggle`| `34px` | iOS-style toggle tracks | |
| 120 | +| `full` | `9999px` | Range tracks, fully rounded | |
| 121 | +| `circle`| `50%` | Avatars, status icons, rank badges | |
| 122 | + |
| 123 | +### Shadows |
| 124 | + |
| 125 | +| Name | Value | Usage | |
| 126 | +|-------------------|----------------------------------------------|----------------------------| |
| 127 | +| Toggle dot | `0 1px 3px rgba(0,0,0,0.15)` | Toggle switch knob | |
| 128 | +| Subtle | `0 1px 3px rgba(0,0,0,0.2)` | Range thumb, small elevate | |
| 129 | +| Tooltip | `0 4px 12px rgba(0,0,0,0.4)` | Tooltip popover | |
| 130 | +| Dropdown | `0 8px 24px rgba(0,0,0,0.3)` | Image modal | |
| 131 | +| Floating | `0 8px 25px rgba(0,0,0,0.4)` | Sticky bottom bar | |
| 132 | +| Toast | `0 8px 30px rgba(0,0,0,0.4)` | Toast notifications | |
| 133 | +| Modal | `0 25px 60px rgba(0,0,0,0.5)` | Centered modals | |
| 134 | +| Drawer | `-10px 0 40px rgba(0,0,0,0.4)` | Side drawer panel | |
| 135 | +| Focus ring (light)| `0 0 0 3px rgba(0,113,227,0.1)` | Input focus (light theme) | |
| 136 | +| Focus ring (dark) | `0 0 0 2px [accent]` | Input focus (dark theme) | |
| 137 | +| Star glow | `drop-shadow(0 1px 3px color-mix(in srgb, [color] 35%, transparent))` | Filled rating stars | |
| 138 | + |
| 139 | +### Overlays & Backdrop |
| 140 | + |
| 141 | +| Context | Background | Backdrop filter | |
| 142 | +|-----------------|--------------------------------|-----------------| |
| 143 | +| Modal overlay | `rgba(0, 0, 0, 0.6)` | `blur(4px)` | |
| 144 | +| Drawer overlay | `rgba(0, 0, 0, 0.5)` | `blur(4px)` | |
| 145 | +| Image lightbox | `rgba(0, 0, 0, 0.75)` | `blur(4px)` | |
| 146 | +| Floating bar | card bg + border | `blur(8px)` | |
| 147 | +| Icon overlay | `rgba(15, 23, 42, 0.55)` | `blur(4px)` | |
| 148 | +| Icon overlay hover | `rgba(15, 23, 42, 0.75)` | — | |
| 149 | + |
| 150 | +### Breakpoints |
| 151 | + |
| 152 | +| Name | Query | Behavior | |
| 153 | +|--------------|-----------------------|------------------------------------| |
| 154 | +| Small mobile | `max-width: 480px` | Stack all, full-width buttons | |
| 155 | +| Mobile | `max-width: 600px` | Single column, stacked filters | |
| 156 | +| Mobile modal | `max-width: 640px` | Full-screen sheet, safe-area inset | |
| 157 | +| Tablet | `max-width: 782px` | Reduced padding, 2-col stats grid | |
| 158 | +| Desktop | `min-width: 1024px` | Full grid layouts (3-col cards) | |
| 159 | +| Wide | `max-width: 1200px` | Table cell adjustments | |
| 160 | + |
| 161 | +Grid columns: `repeat(auto-fill, minmax(300px, 1fr))` general, `repeat(auto-fit, minmax(180px, 1fr))` for stat cards. |
| 162 | + |
| 163 | +### Transitions & Animations |
| 164 | + |
| 165 | +**Duration scale:** |
| 166 | + |
| 167 | +| Token | Duration | Easing | Usage | |
| 168 | +|----------|----------|----------------------------------|------------------------------------| |
| 169 | +| `press` | 0.1s | `ease` | Active scale (`scale(0.93)`) | |
| 170 | +| `fast` | 0.15s | `ease` | Hover borders, color changes | |
| 171 | +| `base` | 0.2s | `ease` | Buttons, inputs, backgrounds | |
| 172 | +| `medium` | 0.25s | `ease` | Chevron rotation, collapse | |
| 173 | +| `slow` | 0.3s | `ease` | Drawers, modals, max-height toggle | |
| 174 | +| `sheet` | 0.3s | `cubic-bezier(0.32, 0.72, 0, 1)`| Mobile bottom sheet entry | |
| 175 | +| `bar` | 0.6s | `ease` | Data bar fill width | |
| 176 | + |
| 177 | +**Named animations:** |
| 178 | + |
| 179 | +| Animation | Spec | Usage | |
| 180 | +|------------|---------------------------------------|------------------------------| |
| 181 | +| `spin` | `0.8s linear infinite` | Loading spinner (rotate 360) | |
| 182 | +| `shimmer` | `1.5s ease-in-out infinite` | Skeleton loading placeholder | |
| 183 | +| `pulse` | `2s ease-in-out infinite` | Badge attention glow | |
| 184 | +| `chipIn` | `0.2s ease` scale(0.9→1) + fade | Filter chip entry | |
| 185 | +| `fadeIn` | opacity 0→1 | Generic reveal | |
| 186 | +| `slideUp` | translateY(20px)→0 + fade | Modal/toast entry | |
| 187 | +| `highlight`| `2s ease` box-shadow accent glow | Scroll-to target pulse | |
| 188 | + |
| 189 | +### Component Patterns |
| 190 | + |
| 191 | +**Card** — `border: 1px solid [border]`, `border-radius: 12px`, `overflow: hidden`. |
| 192 | +Header/body split with border-bottom divider. Hover: border lightens toward accent via `color-mix()`. |
| 193 | + |
| 194 | +**Button sizes** — Standard height 40–44px. Inline-flex, center-aligned, gap 6px. |
| 195 | +Primary: accent bg / dark text. Secondary: transparent + border. Danger: error bg / white. |
| 196 | +Press: `transform: scale(0.93)`. Disabled: `opacity: 0.5; cursor: not-allowed`. |
| 197 | + |
| 198 | +**Toggle (iOS-style)** — Two sizes: |
| 199 | +- Large (admin): 52×32px track, 28px knob, 2px inset |
| 200 | +- Small (consumer): 44×24px track, 18px knob, 3px inset |
| 201 | +- Off: muted bg. On: success green. Knob slides `translateX(20px)`. |
| 202 | + |
| 203 | +**Segmented control** — Flex container, `gap: 0`, deep bg, 4px padding, 10px radius. |
| 204 | +Child buttons share radius. Active: accent bg, dark text. |
| 205 | + |
| 206 | +**Chip / pill** — `border-radius: 20px`, accent-tinted bg via `color-mix(in srgb, [accent] 12%, [deep-bg])`. |
| 207 | +Dismiss button: 18px circle, border bg, hover red `#ee383a`. |
| 208 | + |
| 209 | +**Rating stars** — SVG layers (bg/fill/half). Empty: muted color at 0.35 opacity. |
| 210 | +Filled: accent + drop-shadow. Hover: `scale(1.15)`. Secondary color for user-contributed. |
| 211 | + |
| 212 | +**Modal** — Centered, max-width 520px, 16px border-radius, slide-up 20px entry. |
| 213 | +Mobile <640px: full-screen bottom sheet via `translateY(100%)`, 0px radius, safe-area padding, |
| 214 | +sticky header/footer, `cubic-bezier(0.32, 0.72, 0, 1)` entry. |
| 215 | + |
| 216 | +**Drawer** — Right-aligned, max-width 480px, `translateX(100%)` entry, border-left. |
| 217 | +Mobile: full width. |
| 218 | + |
| 219 | +**Toast** — Fixed top-right (24px inset), max-width 380px, slide-in from right, |
| 220 | +10px radius, elevated shadow. Status border-color indicates type. |
| 221 | + |
| 222 | +**Skeleton** — Shimmer gradient: |
| 223 | +`linear-gradient(90deg, [card-bg] 0%, [muted 8%] 40%, [muted 12%] 50%, [muted 8%] 60%, [card-bg] 100%)` |
| 224 | +background-size 200%, animated position sweep 1.5s. |
| 225 | + |
| 226 | +**Table** — Full-width collapse. Header: surface bg, uppercase 13px muted text. |
| 227 | +Cells: 12–16px padding, border-bottom divider. Row hover: `#fafafa`. |
| 228 | +Row actions: invisible by default, fade in on row hover (0.15s opacity). |
| 229 | +Compact variant: 10–12px padding, 12–13px font. |
| 230 | + |
| 231 | +**Tooltip** — 320px max-width, 8px radius, dark bg, elevated shadow. |
| 232 | +Hover/focus-within toggles visibility + opacity 0.2s. |
| 233 | + |
| 234 | +**Range slider** — Custom thumb: 20px white circle, 2px accent border. |
| 235 | +Track: 6px height, full-round, gradient fill. Hover: `scale(1.15)` + accent ring via `color-mix()`. |
| 236 | + |
| 237 | +**Empty state** — Centered, 60px padding, large muted icon (48–64px), |
| 238 | +20px bold heading, 14px description (max-width 500px, line-height 1.6). |
| 239 | + |
| 240 | +### Accessibility |
| 241 | + |
| 242 | +- Focus-visible: `outline: 2px solid [accent]; outline-offset: 2px` |
| 243 | +- `prefers-reduced-motion: reduce`: disable all animations, shimmer shows static tint |
| 244 | +- Screen-reader-only: standard clip-path pattern |
| 245 | +- Safe-area insets: `env(safe-area-inset-top)` and `env(safe-area-inset-bottom)` on mobile sheets |
| 246 | +- Print: hide interactive elements, switch to high-contrast paper colors |
| 247 | +- Minimum touch targets: 40–44px height |
| 248 | +- Checkbox accent-color matches theme action color |
| 249 | + |
| 250 | +### Conventions |
| 251 | + |
| 252 | +- `color-mix(in srgb, [color] N%, transparent)` for tinted backgrounds & borders |
| 253 | +- `color-mix(in srgb, [accent] N%, [deep-bg])` for subtle accent surfaces |
| 254 | +- Uppercase labels: 11px, weight 600–700, `letter-spacing: 0.3–0.8px` |
| 255 | +- Monospace values: use mono stack for numeric data cells |
| 256 | +- Icons: Font Awesome 6 solid (`fa-solid` prefix) |
| 257 | +- No framework dependencies — all components are vanilla CSS + semantic HTML |
| 258 | +- Build tooling: esbuild |
0 commit comments