veza/apps/web/docs/DESIGN_SYSTEM_REFERENCE.md
senke f64a85414c refactor: Phase 1 — SUMI token foundation
- Rewrite index.css with complete SUMI token system (dark + light themes)
- All --sumi-* variables: backgrounds, surfaces, borders, text, pigments,
  spacing, radius, shadows, glass, scrollbar, motion, z-index, layout
- shadcn/Radix semantic mapping (--background, --foreground, etc.)
- Tailwind @theme mapping with new fonts (Inter, Space Grotesk, JetBrains Mono)
- SUMI keyframe animations (sumi-fade-in, sumi-slide-up, sumi-scale-in, etc.)
- Delete 11 redundant CSS files (design-system.css, design-tokens.css,
  button.css, card.css, input.css, badge-avatar.css, header.css,
  fix-input-focus.css, fix-login-form.css, visual-enhancements.css,
  premium-utilities.css)
- Update main.tsx: single CSS import (index.css only)
- Update ThemeProvider: data-theme attribute instead of .dark class toggle
- Update index.html FOUC script: data-theme attribute

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:48:01 +01:00

1958 lines
83 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SUMI Design System — Source de Vérité
**Version**: 2.0 — Merged (Visual truth: Web version)
**Updated**: 2026-02-12
**Status**: Active — Single Source of Truth for ALL UI decisions
**System Name**: SUMI (墨) — "L'encre et la lumiere"
---
## 1. Philosophy & Identity
### 1.1 Name: SUMI (墨)
"Sumi" is the Japanese word for ink — the fundamental medium of sumi-e painting (ink wash). This design system takes its visual language from the encounter between **ink and paper**: depth without darkness, light without glare, expression without excess.
The name replaces all previous system names (KODO, Spectre Astral, Neon Refined, Botanical). There is now ONE system.
### 1.2 Core Concept: "L'encre et la lumière"
The visual identity of Veza/Talas is built on a single metaphor: **ink diluted in water, applied on washi paper**. This is not a decorative theme — it's a design grammar that unifies ALL user experiences.
> *Encre sur papier. Chaque surface est une feuille, chaque accent un coup de pinceau délibéré. L'espace (間 ma) est sacré.*
### 1.3 Les 6 Règles Inviolables
1. **Encre, pas néon** — Les couleurs sont des pigments, pas des lumières. Quand tout brille, rien ne se distingue. Le glow est UNIQUEMENT pour les focus rings.
2. **Espace sacré (Ma 間)** — Spacing généreux, densité maîtrisée. Ce qui n'est pas là est aussi important que ce qui y est. Le vide n'est pas vide — il respire.
3. **Universalité** — Lisible pour tous (8 ans80 ans, auditeur casuallabel pro). Pas de dark pattern, pas de complexité gratuite.
4. **Wabi-sabi numérique** — Authenticité > perfection stérile. Personnalité dans les détails, pas dans les effets. Les textures sont subtiles, les formes légèrement organiques.
5. **Révélation progressive** — Simple pour l'auditeur, profond pour le label. Les fonctions avancées (stats, gestion multi-artistes, analytics) se révèlent au fur et à mesure. Jamais tout d'un coup.
6. **Thèmes comme épices** — Gaming (XP, achievements), cyber (monospace, terminal), graffiti (chips, tags) = 5% de l'UI, 100% de la personnalité. Ce sont des accents, pas le plat principal.
### 1.4 Thèmes Absorbés
| Thème | Comment il se manifeste | Où dans l'UI |
|---|---|---|
| **Fusain / Lavis japonais** | Couleurs chaudes, tons d'encre, espace | **Partout** — c'est le langage visuel de base |
| **Nature / Botanical** | Sauge comme couleur success, tons terreux | Badges, états online/success |
| **Cybersec / Linux** | JetBrains Mono, terminal blocks, stats mono | Stats, analytics, terminal, durées |
| **Jeux vidéo 2D** | XP bar, achievements, animation pop | Gamification, récompenses |
| **Graffiti / Tag** | Chips genre, tags, énergie gestuelle | Tags musicaux, filtres, catégories |
| **Musique indé** | Authenticité, imperfection, warmth | Ton général, pas de clinquant |
### 1.5 Mnemonic S.U.M.I.
As a quick memory device, the system name also encodes its principles:
- **S — Subtle**: 90% neutral ink tones. Accent ONLY where action or status demands it.
- **U — Universal**: Grandmother and label manager see the same natural interface.
- **M — Measured**: 4px grid, defined elevations, purposeful transitions. No ornament without function.
- **I — Intentional**: Every visual decision answers "what does this help the user DO?"
### 1.6 Reference Products (Quality Benchmarks)
The SUMI system targets the visual quality and polish of:
- **Spotify**: Surface hierarchy, typography-driven hierarchy, minimal accent usage
- **Discord**: Information density without clutter, dark mode excellence, community warmth
- **Linear**: Precision, speed feel, glass effects done right
- **Arc Browser**: Personality without sacrificing usability
### 1.7 What SUMI is NOT
- NOT a neon/cyberpunk interface (no #00fff7 everywhere)
- NOT a flat botanical/nature app (nature is a sub-theme, not the identity)
- NOT a gaming UI (gaming elements appear only in gamification features)
- NOT music-focused visually (music is the CONTENT; the UI is content-agnostic)
- NOT anxious or overstimulating (calm > cool)
---
## 2. Color System — "Les 4 Pigments"
The SUMI palette repose sur des neutres chauds (90% of the UI) and 4 intentional accent pigments. Pas 50 variables. Pas de neon. Think: charcoal on rice paper, not LED screens.
### 2.1 The 90/10 Rule
**90% of every screen is ink tones** (warm neutrals). **10% is accent color** — reserved exclusively for primary actions, active states, status indicators, and navigation markers. This ratio is non-negotiable.
### 2.2 Backgrounds — Couches d'encre
Like ink wash (墨絵): darkest = most ink, lightest = diluted wash. These are WARM darks — notice the slight warm undertone vs cold slate.
**Dark mode** (default):
| Token | Hex | Role |
|---|---|---|
| `--sumi-bg-void` | `#0c0c0f` | Deepest — app background behind everything |
| `--sumi-bg-base` | `#121215` | Primary background — main content area |
| `--sumi-bg-raised` | `#1a1a1f` | Cards, sidebar — one layer up |
| `--sumi-bg-overlay` | `#222228` | Dropdowns, popovers, modals |
| `--sumi-bg-hover` | `#2a2a31` | Hover state on surfaces |
| `--sumi-bg-active` | `#32323a` | Active/pressed state |
| `--sumi-bg-wash` | `#18181d` | Subtle tinted bg (like diluted ink wash) |
**Light mode** ("Washi Paper" 和紙 — warm off-whites, like handmade Japanese paper. NOT cold clinical white):
| Token | Hex | Role |
|---|---|---|
| `--sumi-bg-void` | `#f0ece4` | Warmest base — like aged washi |
| `--sumi-bg-base` | `#f6f3ed` | Main content — fresh rice paper |
| `--sumi-bg-raised` | `#ffffff` | Cards — pure white stands out on cream |
| `--sumi-bg-overlay` | `#ffffff` | Modals, dropdowns |
| `--sumi-bg-hover` | `#ede9e1` | Hover on light surfaces |
| `--sumi-bg-active` | `#e4e0d8` | Pressed on light surfaces |
| `--sumi-bg-wash` | `#f8f6f1` | Subtle tinted bg |
### 2.3 Surfaces
For components that sit ON backgrounds (inputs, wells, insets):
| Token | Dark | Light | Usage |
|---|---|---|---|
| `--sumi-surface-inset` | `#101013` | `#ebe7df` | Sunken — search bars, input fields |
| `--sumi-surface-subtle` | `#1e1e24` | `#f2eee6` | Slightly raised — table rows alternate |
| `--sumi-surface-card` | `#1a1a1f` | `#ffffff` | Standard card |
| `--sumi-surface-elevated` | `#242430` | `#ffffff` | Floating — FAB, sticky headers |
### 2.4 Borders — Ink lines
| Token | Dark | Light | Usage |
|---|---|---|---|
| `--sumi-border-faint` | `rgba(255,255,255, 0.06)` | `rgba(0,0,0, 0.05)` | Subtle separation |
| `--sumi-border-default` | `rgba(255,255,255, 0.10)` | `rgba(0,0,0, 0.10)` | Standard borders |
| `--sumi-border-strong` | `rgba(255,255,255, 0.16)` | `rgba(0,0,0, 0.18)` | Emphasized borders |
| `--sumi-border-focus` | `rgba(139,170,220, 0.50)` | `rgba(80,110,170, 0.45)` | Focus rings |
| `--sumi-border-accent` | `rgba(139,170,220, 0.30)` | `rgba(80,110,170, 0.25)` | Accent-colored borders |
**Border width**: Always 1px. The only exception is the active sidebar indicator (3px left border) and focus rings (2px outline).
### 2.5 Text — Densités d'encre
| Token | Dark | Light | Usage |
|---|---|---|---|
| `--sumi-text-primary` | `#f0ede8` (warm white) | `#1a1816` (deep charcoal) | Titles, main content |
| `--sumi-text-secondary` | `#a8a4a0` | `#5c5854` | Descriptions, metadata |
| `--sumi-text-tertiary` | `#706c68` | `#8a8580` | Timestamps, captions |
| `--sumi-text-disabled` | `#4a4844` | `#b5b0aa` | Disabled state |
| `--sumi-text-inverse` | `#121215` | `#f0ede8` | On accent backgrounds |
| `--sumi-text-link` | `#8baade` | `#4a6fa5` | Links — soft blue |
**Text hierarchy rule**: Info principale = `text-primary` (titles, names), info secondaire = `text-secondary` (artist under title, descriptions), info tertiaire = `text-tertiary` (timestamps, durations, captions). Données chiffrées = `font-mono` toujours (12,847 streams, 3:42, +23.4%).
### 2.6 Les 4 Pigments d'Accent
| Pigment | Kanji | Variable | Hex (dark) | Hex (light) | Usage |
|---|---|---|---|---|---|
| **Indigo Ink** | 藍墨 | `--sumi-accent` | `#7c9dd6` | `#4a6fa5` | Primaire — boutons, active, focus, navigation |
| **Vermillion Seal** | 朱印 | `--sumi-vermillion` | `#d4634a` | `#b84a35` | Danger — erreurs, destructif, notifications, live |
| **Sage** | 草墨 | `--sumi-sage` | `#7a9e6c` | `#5a7e4e` | Success — online, validé, nature |
| **Gold Leaf** | 金墨 | `--sumi-gold` | `#c9a84c` | `#9a7d2e` | Reward — XP, achievements, premium, warnings |
Each pigment has 3 variants:
| Variant | Indigo | Vermillion | Sage | Gold |
|---|---|---|---|---|
| `base` | `#7c9dd6` | `#d4634a` | `#7a9e6c` | `#c9a84c` |
| `hover` | `#93afe0` | `#de7a64` | `#8eb280` | `#d6b860` |
| `subtle` | `rgba(124,157,214, 0.12)` | `rgba(212,99,74, 0.12)` | `rgba(122,158,108, 0.12)` | `rgba(201,168,76, 0.12)` |
Additional accent variants (Indigo only): `--sumi-accent-active: #6b8dc6`, `--sumi-accent-muted: rgba(124,157,214, 0.20)`, `--sumi-accent-emphasis: #5a7fba`.
**Usage rules for primary (Indigo):**
- Primary buttons: `--sumi-accent` background, `--sumi-text-inverse` text
- Links: `--sumi-text-link`
- Active nav item: `--sumi-accent-subtle` background tint + `--sumi-accent` text
- Focus ring: `--sumi-shadow-glow`
- Progress bars, sliders: `--sumi-accent`
- NEVER as background for cards, sections, or large areas
### 2.7 Semantic Mapping
```css
--sumi-success: var(--sumi-sage);
--sumi-warning: var(--sumi-gold);
--sumi-error: var(--sumi-vermillion);
--sumi-info: var(--sumi-accent);
--sumi-live: #e05a5a; /* Brighter red for live indicators */
--sumi-online: var(--sumi-sage);
```
Each semantic color has a `*-subtle` variant for tinted backgrounds (e.g. `--sumi-success-subtle: var(--sumi-sage-subtle)`).
### 2.8 Contextual Accents — Feature-Specific Colors
These colors appear ONLY in their specific feature contexts. They are NOT part of the general UI palette.
| Token | Hex | Feature Context |
|---|---|---|
| `--graffiti-magenta` | `#c840a0` | Creative expression: artist profiles, cover art badges |
| `--gaming-gold` | `#d4b040` | XP, achievements, leaderboards, level badges |
| `--terminal-green` | `#3eaa5e` | Admin panel, dev tools, API status |
| `--sakura` | `#e0a0b8` | Delicate moments: welcome, onboarding, empty states |
**Strict usage rule**: If you cannot justify which FEATURE requires the contextual color, use `--sumi-accent` or `--sumi-vermillion` instead.
### 2.9 Color Architecture Summary
```
┌───────────────────────────────────────────────────────┐
│ SUMI COLOR MODEL │
├───────────────────────────────────────────────────────┤
│ │
│ ████████████████████████████████████████ 90% │
│ Sumi Neutrals (--sumi-bg-*, --sumi-text-*) │
│ Backgrounds, surfaces, text, borders │
│ │
│ ██████ 7% │
│ Indigo Ink 藍墨 (--sumi-accent) │
│ Buttons, links, active states, focus │
│ │
│ ███ 2% │
│ Vermillion 朱印 + Sage 草墨 + Gold 金墨 │
│ Errors, success, warnings, achievements │
│ │
│ █ 1% │
│ Contextual (magenta, gold, green, sakura) │
│ Feature-specific moments only │
│ │
└───────────────────────────────────────────────────────┘
```
---
## 3. Typography — "The Brush & The Grid"
### 3.1 Font Stack
3 fonts + 1 decorative. That's it. No Orbitron. No display fonts that scream.
| Role | Font | Variable | Fallbacks | Weight Range | Why |
|---|---|---|---|---|---|
| **Body** | Inter | `--sumi-font-body` | -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif | 300700 | Universal readability. Google/Apple-tier. |
| **Headings** | Space Grotesk | `--sumi-font-heading` | 'Inter', sans-serif | 400700 | Geometric but warm. Has personality without being aggressive. Nods to tech/cyber subtly. |
| **Mono** | JetBrains Mono | `--sumi-font-mono` | 'SF Mono', 'Consolas', monospace | 400600 | Code, terminal UI, stats. The cybersec/linux accent. |
| **Decorative** | Noto Serif JP | `--sumi-font-serif` | Georgia, serif | 400, 600 | Easter eggs, citations japonaises (rare). |
**Fonts removed** (and why):
- Orbitron: Too sci-fi/gaming. Makes the app look like a cyberpunk game, alienates non-tech users.
- Rajdhani: Too niche/specific. Doesn't add readability.
- Barlow / DM Sans: Good but Inter is the industry standard body font with better feature support.
- Source Serif 4 / Instrument Serif: Serif fonts don't belong in a streaming app primary stack.
- Bebas Neue: Display-only font, Space Grotesk at bold weights covers this.
**Loading strategy:**
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Noto+Serif+JP:wght@400;600&display=swap" rel="stylesheet">
```
**Font feature settings** (Inter): `font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';` — stylistic alternates for cleaner glyphs.
### 3.2 Type Scale (Major Third — 1.250)
| Class | Token | Size | Font | Weight | Line Height | Spacing | Usage |
|---|---|---|---|---|---|---|---|
| `.sumi-display` | `--sumi-text-4xl` | 2.25rem (36px) | Heading | 700 | `--sumi-leading-tight` (1.25) | `--sumi-tracking-tighter` (-0.03em) | Hero, landing |
| `.sumi-h1` | `--sumi-text-3xl` | 1.875rem (30px) | Heading | 600 | `--sumi-leading-tight` | `--sumi-tracking-tight` (-0.015em) | Titre de page |
| `.sumi-h2` | `--sumi-text-2xl` | 1.5rem (24px) | Heading | 600 | `--sumi-leading-snug` (1.375) | `--sumi-tracking-tight` | Section |
| `.sumi-h3` | `--sumi-text-xl` | 1.25rem (20px) | Heading | 500 | `--sumi-leading-snug` | Normal | Sous-section |
| `.sumi-h4` | `--sumi-text-lg` | 1.125rem (18px) | Heading | 500 | `--sumi-leading-snug` | Normal | Sous-titre |
| `.sumi-body-lg` | `--sumi-text-md` | 1rem (16px) | Body | 400 | `--sumi-leading-relaxed` (1.625) | Normal | Texte important |
| `.sumi-body` | `--sumi-text-base` | 0.875rem (14px) | Body | 400 | `--sumi-leading-normal` (1.5) | Normal | **Texte standard** |
| `.sumi-body-sm` | `--sumi-text-sm` | 0.8125rem (13px) | Body | 400 | `--sumi-leading-normal` | Normal | Texte compact |
| `.sumi-caption` | `--sumi-text-xs` | 0.75rem (12px) | Body | 400 | `--sumi-leading-normal` | Normal | Timestamps, captions |
| `.sumi-label` | `--sumi-text-xs` | 0.75rem (12px) | Body | 500 | `--sumi-leading-normal` | `--sumi-tracking-wider` (0.05em) | Labels uppercase |
| `.sumi-mono` | `--sumi-text-sm` | 0.8125rem (13px) | Mono | 400 | — | — | Stats, durées |
### 3.3 Typography Tokens
**Line Heights:**
| Token | Value |
|---|---|
| `--sumi-leading-none` | 1 |
| `--sumi-leading-tight` | 1.25 |
| `--sumi-leading-snug` | 1.375 |
| `--sumi-leading-normal` | 1.5 |
| `--sumi-leading-relaxed` | 1.625 |
| `--sumi-leading-loose` | 1.75 |
**Letter Spacing:**
| Token | Value |
|---|---|
| `--sumi-tracking-tighter` | -0.03em |
| `--sumi-tracking-tight` | -0.015em |
| `--sumi-tracking-normal` | 0 |
| `--sumi-tracking-wide` | 0.025em |
| `--sumi-tracking-wider` | 0.05em |
| `--sumi-tracking-widest` | 0.1em |
**Font Weights:**
| Token | Value |
|---|---|
| `--sumi-weight-light` | 300 |
| `--sumi-weight-regular` | 400 |
| `--sumi-weight-medium` | 500 |
| `--sumi-weight-semibold` | 600 |
| `--sumi-weight-bold` | 700 |
### 3.4 Text Color Hierarchy
On dark surfaces:
| Priority | Token | Hex | Usage |
|---|---|---|---|
| **Primary** | `--sumi-text-primary` | `#f0ede8` | Headings, important text, interactive labels |
| **Secondary** | `--sumi-text-secondary` | `#a8a4a0` | Body text, descriptions |
| **Tertiary** | `--sumi-text-tertiary` | `#706c68` | Metadata, timestamps, helper text |
| **Disabled** | `--sumi-text-disabled` | `#4a4844` | Disabled labels, placeholder |
On light surfaces:
| Priority | Token | Hex | Usage |
|---|---|---|---|
| **Primary** | `--sumi-text-primary` | `#1a1816` | Headings, important text |
| **Secondary** | `--sumi-text-secondary` | `#5c5854` | Body text |
| **Tertiary** | `--sumi-text-tertiary` | `#8a8580` | Metadata |
| **Disabled** | `--sumi-text-disabled` | `#b5b0aa` | Disabled |
### 3.5 Typography Rules
1. **Never use more than 3 font weights on a single screen**: pick from 400, 500, 600, 700.
2. **Headings are always `font-heading` (Space Grotesk)**, never mono or body.
3. **Monospace is ONLY for**: BPM numbers, musical keys, code blocks, terminal output, timestamps in admin, file sizes, stream counts, durations.
4. **Maximum 4 type sizes per screen** to maintain hierarchy clarity.
5. **Line length**: Body text should not exceed `65ch` (~520px at 14px). Use `max-w-prose` or equivalent.
6. **Font feature settings**: Always enable Inter's stylistic alternates with `'cv02', 'cv03', 'cv04', 'cv11'`.
---
## 4. Spacing, Radius & Elevation
### 4.1 Spacing — 4px Base Grid
All spacing uses the standard Tailwind scale built on a 4px base unit. NO arbitrary spacing values.
| Token | Tailwind | Value | Typical Use |
|---|---|---|---|
| `1` | `p-1` / `gap-1` | 4px | Icon-to-text micro gap |
| `1.5` | `p-1.5` / `gap-1.5` | 6px | Tight chip/badge padding |
| `2` | `p-2` / `gap-2` | 8px | Internal component gap, icon button padding |
| `3` | `p-3` / `gap-3` | 12px | Compact card padding, input horizontal padding |
| `4` | `p-4` / `gap-4` | 16px | Standard card padding, list item spacing |
| `5` | `p-5` / `gap-5` | 20px | Form group spacing |
| `6` | `p-6` / `gap-6` | 24px | Generous card padding, section internal spacing |
| `8` | `p-8` / `gap-8` | 32px | Section-to-section spacing |
| `10` | `p-10` / `gap-10` | 40px | Large section spacing |
| `12` | `p-12` / `gap-12` | 48px | Page-level section gaps |
| `16` | `p-16` / `gap-16` | 64px | Hero sections top/bottom |
| `20` | `p-20` / `gap-20` | 80px | Maximum content spacing |
**Spacing rules:**
1. Card padding: `p-4` (compact) or `p-6` (standard). NOT `p-8` for cards (too much wasted space).
2. Section spacing: `gap-6` (tight) or `gap-8` (standard) between related sections.
3. Page-level spacing: `gap-10` or `gap-12` between major page sections.
4. Never use `space-y-*` when `gap-*` on a flex/grid parent achieves the same result.
### 4.2 Border Radius
Inspired by ink brush curves — soft but not bubbly. The dominant shape language is "soft rectangle".
| Token | Tailwind | Value | Usage |
|---|---|---|---|
| `none` | `rounded-none` | 0px | Table cells, inline code |
| `sm` | `rounded-sm` | 4px | Chips inside inputs, micro elements |
| `md` | `rounded-md` | 6px | Badges, small buttons, tags |
| `DEFAULT` | `rounded` | 8px | Buttons, inputs, small cards |
| `lg` | `rounded-lg` | 12px | Standard cards, dropdowns, popovers |
| `xl` | `rounded-xl` | 16px | Modals, large feature cards |
| `2xl` | `rounded-2xl` | 20px | Hero cards, full-page modals |
| `full` | `rounded-full` | 9999px | Avatars, pills, circular buttons, sliders |
**Radius rules:**
1. Default interactive elements (buttons, inputs): `rounded` (8px)
2. Cards: `rounded-lg` (12px)
3. Modals: `rounded-xl` (16px)
4. Avatars: Always `rounded-full`
5. NEVER mix different radii on the same component — children should use smaller or equal radius.
### 4.3 Elevation & Shadow System
Shadows in SUMI are "ink pooling" — soft, warm-tinted, not harsh black.
**Dark mode shadows:**
| Token | CSS Value | Usage |
|---|---|---|
| `--sumi-shadow-xs` | `0 1px 2px rgba(0,0,0,0.30)` | Inputs, badges |
| `--sumi-shadow-sm` | `0 2px 4px rgba(0,0,0,0.25), 0 1px 2px rgba(0,0,0,0.20)` | Cards at rest |
| `--sumi-shadow-md` | `0 4px 12px rgba(0,0,0,0.30), 0 2px 4px rgba(0,0,0,0.15)` | Cards hover, tooltips |
| `--sumi-shadow-lg` | `0 8px 24px rgba(0,0,0,0.35), 0 4px 8px rgba(0,0,0,0.20)` | Dropdowns, popovers |
| `--sumi-shadow-xl` | `0 16px 48px rgba(0,0,0,0.40), 0 8px 16px rgba(0,0,0,0.20)` | Modals, drawers |
| `--sumi-shadow-2xl` | `0 24px 64px rgba(0,0,0,0.50)` | Full-screen overlays |
**Light mode shadows** (softer):
| Token | CSS Value |
|---|---|
| `--sumi-shadow-xs` | `0 1px 2px rgba(0,0,0,0.05)` |
| `--sumi-shadow-sm` | `0 2px 4px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)` |
| `--sumi-shadow-md` | `0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04)` |
| `--sumi-shadow-lg` | `0 8px 24px rgba(0,0,0,0.10), 0 4px 8px rgba(0,0,0,0.05)` |
| `--sumi-shadow-xl` | `0 16px 48px rgba(0,0,0,0.12), 0 8px 16px rgba(0,0,0,0.06)` |
| `--sumi-shadow-2xl` | `0 24px 64px rgba(0,0,0,0.15)` |
**Glow** — used ONLY for focus states and primary CTA. Never decorative:
| Token | CSS Value | When to use |
|---|---|---|
| `--sumi-shadow-glow` | `0 0 0 3px rgba(124,157,214,0.25)` | Focus rings on interactive elements |
| `--sumi-shadow-glow-lg` | `0 0 20px rgba(124,157,214,0.15)` | "Now playing", active live stream |
**Glow rules:**
1. Glows are ANIMATED, never static (they pulse or appear then fade)
2. Maximum 1 glowing element visible at a time on any screen
3. Respect `prefers-reduced-motion`: no glow animations
### 4.4 Glass Panel
Frosted ink paper — for player bar, header, floating panels. NOT for regular cards.
```css
.sumi-glass {
background: var(--sumi-glass-bg);
backdrop-filter: blur(var(--sumi-glass-blur));
-webkit-backdrop-filter: blur(var(--sumi-glass-blur));
border: 1px solid var(--sumi-glass-border);
}
```
| Token | Dark | Light |
|---|---|---|
| `--sumi-glass-bg` | `rgba(18,18,21, 0.80)` | `rgba(255,255,255, 0.85)` |
| `--sumi-glass-border` | `rgba(255,255,255, 0.08)` | `rgba(0,0,0, 0.06)` |
| `--sumi-glass-blur` | `12px` | `12px` |
### 4.5 Scrollbar
| Token | Dark | Light |
|---|---|---|
| `--sumi-scrollbar-track` | `transparent` | `transparent` |
| `--sumi-scrollbar-thumb` | `rgba(255,255,255, 0.10)` | `rgba(0,0,0, 0.12)` |
| `--sumi-scrollbar-hover` | `rgba(255,255,255, 0.18)` | `rgba(0,0,0, 0.22)` |
Width: 6px. Border-radius: `--sumi-radius-full`.
### 4.6 Z-Index Scale
| Token | Value | Usage |
|---|---|---|
| `--sumi-z-base` | 0 | Default |
| `--sumi-z-raised` | 10 | Slightly elevated |
| `--sumi-z-dropdown` | 100 | Dropdown menus |
| `--sumi-z-sticky` | 200 | Sidebar, header, player |
| `--sumi-z-overlay` | 300 | Overlays |
| `--sumi-z-modal` | 400 | Modals |
| `--sumi-z-popover` | 500 | Popovers |
| `--sumi-z-toast` | 600 | Toast notifications |
| `--sumi-z-tooltip` | 700 | Tooltips |
| `--sumi-z-max` | 999 | Emergency override |
### 4.7 Layout Tokens
| Token | Value | Usage |
|---|---|---|
| `--sumi-max-width` | 1400px | Overall page max |
| `--sumi-max-width-content` | 1200px | Content area |
| `--sumi-max-width-narrow` | 800px | Narrow layouts (settings, forms) |
| `--sumi-max-width-prose` | 65ch | Body text maximum line length |
| `--sumi-sidebar-width` | 240px | Sidebar expanded |
| `--sumi-sidebar-collapsed` | 64px | Sidebar collapsed |
| `--sumi-header-height` | 56px | Header bar |
| `--sumi-player-height` | 80px | Player bar |
### 4.8 Surface Hierarchy
The surface system creates depth through background color, not shadows. This is the Spotify/Discord approach.
```
┌─ Layer 0: Canvas ──────────────────────────────────────┐
│ --sumi-bg-void (dark) / washi (light) │
│ The page background. Nothing sits below this. │
│ │
│ ┌─ Layer 1: Surface ──────────────────────────────┐ │
│ │ --sumi-bg-raised / --sumi-surface-card │ │
│ │ Cards, panels, sidebar content area │ │
│ │ │ │
│ │ ┌─ Layer 2: Elevated ──────────────────────┐ │ │
│ │ │ --sumi-surface-elevated / glass panels │ │ │
│ │ │ Dropdowns, popovers, floating player │ │ │
│ │ │ │ │ │
│ │ │ ┌─ Layer 3: Overlay ────────────────┐ │ │ │
│ │ │ │ --sumi-bg-overlay │ │ │ │
│ │ │ │ Modals, dialogs, command palette │ │ │ │
│ │ │ └───────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
**Rules:**
1. Cards do NOT have shadows in dark mode — they rely on background color difference.
2. Cards in light mode get `shadow-sm` for subtle lift.
3. Floating elements (player, dropdowns) get `shadow-lg` in light mode, `shadow-md` in dark mode.
4. Modals always have `shadow-xl` + backdrop overlay (`rgba(0,0,0,0.60)` + `backdrop-filter: blur(4px)`).
5. The sidebar is Layer 1 (raised) — it uses `--sumi-bg-raised`.
---
## 5. Motion & Texture
### 5.1 Animation Principles
Motion in SUMI follows the movement of ink on paper: **fluid arrival, calm rest, gentle exit**. Never mechanical. Never bouncy unless celebrating (achievement unlocked).
**Rules:**
1. Every animation must have a purpose: feedback, orientation, or celebration.
2. If you can't explain what the animation TELLS the user — remove it.
3. Hover effects are limited to: background change, opacity change, border change. NO scale transforms on cards.
4. Respect `prefers-reduced-motion`: disable decorative motion, keep functional feedback (opacity changes OK, position changes not).
### 5.2 Duration Scale
| Token | Value | Usage |
|---|---|---|
| `--sumi-duration-instant` | 75ms | Button press feedback, keyboard response |
| `--sumi-duration-fast` | 150ms | Hover in/out, focus ring appear, tooltip show |
| `--sumi-duration-normal` | 200ms | Panel open/close, sidebar toggle, dropdown appear |
| `--sumi-duration-slow` | 300ms | Modal entrance, sidebar transition |
| `--sumi-duration-slower` | 500ms | Achievement animation only |
### 5.3 Easing Curves
| Token | Value | Usage |
|---|---|---|
| `--sumi-ease-default` | `cubic-bezier(0.25, 0.1, 0.25, 1)` | Standard for everything |
| `--sumi-ease-out` | `cubic-bezier(0.33, 1, 0.68, 1)` | Decelerate — entries, elements appearing |
| `--sumi-ease-in` | `cubic-bezier(0.32, 0, 0.67, 0)` | Accelerate — exits, elements disappearing |
| `--sumi-ease-in-out` | `cubic-bezier(0.65, 0, 0.35, 1)` | Symmetric — toggles, smooth both ways |
| `--sumi-ease-bounce` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | Playful — achievements ONLY, never elsewhere |
| `--sumi-ease-spring` | `cubic-bezier(0.175, 0.885, 0.32, 1.1)` | Natural spring — toast notifications |
### 5.4 Transition Presets (CSS Variables)
Composable transition presets for consistency:
```css
--sumi-transition-colors: color var(--sumi-duration-fast) var(--sumi-ease-default),
background-color var(--sumi-duration-fast) var(--sumi-ease-default),
border-color var(--sumi-duration-fast) var(--sumi-ease-default);
--sumi-transition-opacity: opacity var(--sumi-duration-fast) var(--sumi-ease-default);
--sumi-transition-transform: transform var(--sumi-duration-normal) var(--sumi-ease-out);
--sumi-transition-shadow: box-shadow var(--sumi-duration-fast) var(--sumi-ease-default);
--sumi-transition-all: all var(--sumi-duration-normal) var(--sumi-ease-default);
```
### 5.5 Hover States — The Complete List
What hovering DOES in SUMI:
| Element | Hover Effect | Transition |
|---|---|---|
| **Button (primary)** | Lighten background 10% | `background 150ms ease-out` |
| **Button (ghost)** | Show `sumi-800` background | `background 150ms ease-out` |
| **Card** | Show `white/5%` overlay OR lighten surface | `background 150ms ease-out` |
| **Link** | Underline + slightly dim | `color 150ms ease-out` |
| **Nav item** | Background tint + text brighten | `background 150ms ease-out, color 150ms ease-out` |
| **Table row** | Lighten background | `background 100ms ease-out` |
| **Icon button** | Background tint circle | `background 150ms ease-out` |
What hovering does NOT do:
- NO `scale()` transforms on any element
- NO shadow changes on hover (shadows are static by elevation)
- NO border-color changes (except on inputs going to focus)
- NO glow effects on hover (glows are for status, not interaction)
### 5.6 Keyframe Animations
```css
/* Essential animations — only these are allowed */
@keyframes sumi-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes sumi-fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes sumi-slide-up {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes sumi-slide-down {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes sumi-scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes sumi-scale-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.95); }
}
/* Achievement pop — the ONE playful animation (gaming touch) */
@keyframes sumi-pop {
0% { opacity: 0; transform: scale(0.8); }
60% { opacity: 1; transform: scale(1.05); }
100% { transform: scale(1); }
}
/* Subtle pulse for live indicators */
@keyframes sumi-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Ink brush stroke reveal — for page transitions (signature SUMI touch) */
@keyframes sumi-brush-reveal {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0 0 0 0); }
}
/* Skeleton shimmer (loading) */
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* EQ bars — for "now playing" indicator */
@keyframes eq-bar {
0%, 100% { transform: scaleY(0.3); }
50% { transform: scaleY(1); }
}
```
### 5.7 Texture — The Sumi Layer
The visual signature that makes SUMI unique. These textures are SUBTLE — they create atmosphere, not noise.
#### 5.7.1 Paper Grain (Global)
A barely perceptible noise texture on the body background. Gives the feeling of looking at washi paper on a screen.
```css
body::after {
content: '';
position: fixed;
inset: 0;
background: url("data:image/svg+xml,...noise-svg...");
opacity: 0.012; /* Almost invisible — you feel it, don't see it */
pointer-events: none;
z-index: 9999;
mix-blend-mode: overlay;
}
```
Opacity: 1.2% in dark mode, 2% in light mode. If users set `prefers-reduced-motion`, disable it entirely.
#### 5.7.2 Ink Wash Gradients (Hero/Feature Sections)
For hero sections, featured content, or onboarding screens — a soft, asymmetric gradient that feels like diluted ink spreading on wet paper.
```css
.sumi-wash-texture {
position: relative;
}
.sumi-wash-texture::after {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 20% 50%, var(--sumi-accent-subtle) 0%, transparent 60%),
radial-gradient(ellipse at 80% 20%, rgba(201,168,76,0.04) 0%, transparent 50%);
pointer-events: none;
}
```
Key characteristics:
- Asymmetric (never centered or symmetrical)
- Very low opacity (4-8%)
- Uses primary and warm colors
- Organic ellipse shapes, not circles
#### 5.7.3 Brush Stroke Accents (Special Moments)
For empty states, achievement cards, artist profile headers — SVG brush strokes as decorative elements. These are NOT used in general UI.
Usage contexts:
- Empty state illustrations: A single brush stroke as background
- Achievement card borders: Ink splash frame
- Artist profile hero: Calligraphic flourish behind artist name
- 404 page: Sumi-e landscape sketch
**Rules:**
1. Maximum 1 brush stroke element per screen
2. Always `opacity: 0.1` to `0.3` — they're atmosphere, not content
3. Never compete with text for attention
4. Color: `sumi-400` or `primary-400` only
---
## 6. Component Specifications
Every component below uses `--sumi-*` CSS custom properties. The CSS class definitions below are the definitive implementations.
### 6.1 Card
```css
.sumi-card {
background: var(--sumi-surface-card);
border: 1px solid var(--sumi-border-faint);
border-radius: var(--sumi-radius-lg);
padding: var(--sumi-space-4);
transition: var(--sumi-transition-shadow), var(--sumi-transition-colors);
}
.sumi-card:hover {
border-color: var(--sumi-border-default);
box-shadow: var(--sumi-shadow-sm);
}
.sumi-card--interactive { cursor: pointer; }
.sumi-card--interactive:hover {
background: var(--sumi-bg-hover);
box-shadow: var(--sumi-shadow-md);
}
.sumi-card--elevated {
background: var(--sumi-surface-elevated);
box-shadow: var(--sumi-shadow-md);
}
```
**Card variants** (prose specs):
| Variant | Details |
|---|---|
| **Media Card** (Track, Album, Playlist) | Cover `aspect-square`, `rounded-lg`. Title `text-sm font-medium`, truncated. Subtitle `text-xs text-secondary`, truncated. Play overlay on hover. |
| **Stat Card** | Icon container 40px `rounded-lg`, value `sumi-h2`, label `sumi-label`, trend indicator mono ±%. |
| **Profile Card** | Avatar `rounded-full`, name `sumi-h4`, role badge pill. |
### 6.2 Buttons
```css
.sumi-btn {
display: inline-flex; align-items: center; justify-content: center;
gap: var(--sumi-space-2);
font-family: var(--sumi-font-body);
font-size: var(--sumi-text-sm);
font-weight: var(--sumi-weight-medium);
line-height: 1;
padding: var(--sumi-space-2) var(--sumi-space-4);
border-radius: var(--sumi-radius-md);
border: 1px solid transparent;
cursor: pointer;
transition: var(--sumi-transition-colors), var(--sumi-transition-shadow);
user-select: none; white-space: nowrap;
}
.sumi-btn:focus-visible { box-shadow: var(--sumi-shadow-glow); }
.sumi-btn:disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
/* Primary — THE action */
.sumi-btn--primary {
background: var(--sumi-accent); color: var(--sumi-text-inverse); border-color: var(--sumi-accent);
}
.sumi-btn--primary:hover { background: var(--sumi-accent-hover); border-color: var(--sumi-accent-hover); }
.sumi-btn--primary:active { background: var(--sumi-accent-active); }
/* Secondary — bordered, subtle */
.sumi-btn--secondary {
background: transparent; color: var(--sumi-text-primary); border-color: var(--sumi-border-strong);
}
.sumi-btn--secondary:hover { background: var(--sumi-bg-hover); border-color: var(--sumi-accent); color: var(--sumi-accent); }
/* Ghost — no border, minimal */
.sumi-btn--ghost { background: transparent; color: var(--sumi-text-secondary); border-color: transparent; }
.sumi-btn--ghost:hover { background: var(--sumi-bg-hover); color: var(--sumi-text-primary); }
/* Danger */
.sumi-btn--danger { background: var(--sumi-vermillion); color: white; border-color: var(--sumi-vermillion); }
.sumi-btn--danger:hover { background: var(--sumi-vermillion-hover); }
/* Sizes */
.sumi-btn--xs { padding: var(--sumi-space-1) var(--sumi-space-2); font-size: var(--sumi-text-xs); }
.sumi-btn--sm { padding: var(--sumi-space-1-5) var(--sumi-space-3); font-size: var(--sumi-text-sm); }
.sumi-btn--lg { padding: var(--sumi-space-3) var(--sumi-space-6); font-size: var(--sumi-text-md); }
.sumi-btn--icon { padding: var(--sumi-space-2); width: 36px; height: 36px; }
.sumi-btn--icon-sm { padding: var(--sumi-space-1-5); width: 28px; height: 28px; }
```
### 6.3 Input
```css
.sumi-input {
display: block; width: 100%;
font-family: var(--sumi-font-body);
font-size: var(--sumi-text-base);
color: var(--sumi-text-primary);
background: var(--sumi-surface-inset);
border: 1px solid var(--sumi-border-default);
border-radius: var(--sumi-radius-md);
padding: var(--sumi-space-2) var(--sumi-space-3);
transition: var(--sumi-transition-colors), var(--sumi-transition-shadow);
}
.sumi-input::placeholder { color: var(--sumi-text-tertiary); }
.sumi-input:hover { border-color: var(--sumi-border-strong); }
.sumi-input:focus { outline: none; border-color: var(--sumi-accent); box-shadow: var(--sumi-shadow-glow); }
```
Additional form elements (prose specs):
- **Checkbox/Radio**: 18px, 2px border, checked = `--sumi-accent` fill + white check.
- **Toggle**: See `.sumi-toggle` below.
### 6.4 Badge / Tag
```css
.sumi-badge {
display: inline-flex; align-items: center; gap: var(--sumi-space-1);
font-family: var(--sumi-font-body);
font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-medium);
padding: var(--sumi-space-0-5) var(--sumi-space-2);
border-radius: var(--sumi-radius-full);
line-height: var(--sumi-leading-normal);
}
.sumi-badge--default { background: var(--sumi-bg-hover); color: var(--sumi-text-secondary); }
.sumi-badge--accent { background: var(--sumi-accent-subtle); color: var(--sumi-accent); }
.sumi-badge--success { background: var(--sumi-success-subtle); color: var(--sumi-success); }
.sumi-badge--warning { background: var(--sumi-warning-subtle); color: var(--sumi-warning); }
.sumi-badge--error { background: var(--sumi-error-subtle); color: var(--sumi-error); }
.sumi-badge--live { background: rgba(224,90,90,0.15); color: var(--sumi-live); }
```
### 6.5 Chip (Graffiti touch)
```css
.sumi-chip {
display: inline-flex; align-items: center; gap: var(--sumi-space-1);
font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-semibold);
padding: var(--sumi-space-1) var(--sumi-space-2-5);
border-radius: var(--sumi-radius-sm);
background: var(--sumi-bg-hover); color: var(--sumi-text-secondary);
border: 1px solid var(--sumi-border-faint);
transition: var(--sumi-transition-colors); cursor: pointer;
}
.sumi-chip:hover { background: var(--sumi-accent-subtle); color: var(--sumi-accent); border-color: var(--sumi-border-accent); }
.sumi-chip--selected { background: var(--sumi-accent-subtle); color: var(--sumi-accent); border-color: var(--sumi-accent); }
```
### 6.6 Avatar
```css
.sumi-avatar {
display: inline-flex; align-items: center; justify-content: center;
border-radius: var(--sumi-radius-full); overflow: hidden;
background: var(--sumi-bg-hover); color: var(--sumi-text-secondary);
font-weight: var(--sumi-weight-medium); flex-shrink: 0;
}
.sumi-avatar--xs { width: 24px; height: 24px; font-size: 10px; }
.sumi-avatar--sm { width: 32px; height: 32px; font-size: var(--sumi-text-xs); }
.sumi-avatar--md { width: 40px; height: 40px; font-size: var(--sumi-text-sm); }
.sumi-avatar--lg { width: 48px; height: 48px; font-size: var(--sumi-text-base); }
.sumi-avatar--xl { width: 64px; height: 64px; font-size: var(--sumi-text-lg); }
.sumi-avatar--2xl { width: 96px; height: 96px; font-size: var(--sumi-text-2xl); }
.sumi-avatar img { width: 100%; height: 100%; object-fit: cover; }
/* Online indicator (position: relative on parent required) */
.sumi-avatar__status {
position: absolute; bottom: 0; right: 0;
width: 10px; height: 10px; border-radius: var(--sumi-radius-full);
border: 2px solid var(--sumi-bg-raised);
}
.sumi-avatar__status--online { background: var(--sumi-online); }
.sumi-avatar__status--live { background: var(--sumi-live); animation: sumi-pulse 2s ease-in-out infinite; }
```
### 6.7 Dropdown / Menu
```css
.sumi-menu {
background: var(--sumi-bg-overlay);
border: 1px solid var(--sumi-border-default);
border-radius: var(--sumi-radius-lg);
box-shadow: var(--sumi-shadow-xl);
padding: var(--sumi-space-1);
z-index: var(--sumi-z-dropdown);
min-width: 180px;
animation: sumi-scale-in var(--sumi-duration-fast) var(--sumi-ease-out);
}
.sumi-menu__item {
display: flex; align-items: center; gap: var(--sumi-space-2);
padding: var(--sumi-space-2) var(--sumi-space-3);
border-radius: var(--sumi-radius-sm);
font-size: var(--sumi-text-sm); color: var(--sumi-text-secondary);
cursor: pointer; transition: var(--sumi-transition-colors);
}
.sumi-menu__item:hover { background: var(--sumi-bg-hover); color: var(--sumi-text-primary); }
.sumi-menu__item--active { background: var(--sumi-accent-subtle); color: var(--sumi-accent); }
.sumi-menu__item--danger { color: var(--sumi-vermillion); }
.sumi-menu__item--danger:hover { background: var(--sumi-vermillion-subtle); }
.sumi-menu__separator { height: 1px; background: var(--sumi-border-faint); margin: var(--sumi-space-1) 0; }
```
### 6.8 Modal
```css
.sumi-modal-backdrop {
position: fixed; inset: 0;
background: rgba(0,0,0,0.60); backdrop-filter: blur(4px);
z-index: var(--sumi-z-modal);
animation: sumi-fade-in var(--sumi-duration-fast) var(--sumi-ease-out);
}
.sumi-modal {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: var(--sumi-bg-overlay);
border: 1px solid var(--sumi-border-default);
border-radius: var(--sumi-radius-xl);
box-shadow: var(--sumi-shadow-2xl);
z-index: calc(var(--sumi-z-modal) + 1);
max-width: 560px; width: 90vw; max-height: 85vh; overflow-y: auto;
animation: sumi-scale-in var(--sumi-duration-normal) var(--sumi-ease-out);
}
.sumi-modal__header { padding: var(--sumi-space-5) var(--sumi-space-6) var(--sumi-space-3); }
.sumi-modal__body { padding: var(--sumi-space-3) var(--sumi-space-6); }
.sumi-modal__footer { padding: var(--sumi-space-3) var(--sumi-space-6) var(--sumi-space-5); display: flex; justify-content: flex-end; gap: var(--sumi-space-2); }
```
### 6.9 Toast
```css
.sumi-toast {
display: flex; align-items: flex-start; gap: var(--sumi-space-3);
background: var(--sumi-surface-elevated);
border: 1px solid var(--sumi-border-default);
border-radius: var(--sumi-radius-lg);
padding: var(--sumi-space-3) var(--sumi-space-4);
box-shadow: var(--sumi-shadow-xl);
min-width: 300px; max-width: 420px;
animation: sumi-slide-up var(--sumi-duration-slow) var(--sumi-ease-spring);
}
```
### 6.10 Tooltip
```css
.sumi-tooltip {
background: var(--sumi-bg-overlay);
color: var(--sumi-text-primary);
font-size: var(--sumi-text-xs);
padding: var(--sumi-space-1-5) var(--sumi-space-2-5);
border-radius: var(--sumi-radius-md);
box-shadow: var(--sumi-shadow-lg);
border: 1px solid var(--sumi-border-default);
max-width: 240px; z-index: var(--sumi-z-tooltip);
}
```
### 6.11 Tabs
```css
.sumi-tabs {
display: flex; gap: var(--sumi-space-1);
border-bottom: 1px solid var(--sumi-border-faint);
}
.sumi-tab {
padding: var(--sumi-space-2) var(--sumi-space-4);
font-size: var(--sumi-text-sm); font-weight: var(--sumi-weight-medium);
color: var(--sumi-text-tertiary); cursor: pointer;
border-bottom: 2px solid transparent;
transition: var(--sumi-transition-colors);
}
.sumi-tab:hover { color: var(--sumi-text-primary); }
.sumi-tab--active { color: var(--sumi-text-primary); border-bottom-color: var(--sumi-accent); }
```
### 6.12 Toggle
```css
.sumi-toggle {
position: relative; width: 40px; height: 22px;
background: var(--sumi-bg-active); border-radius: var(--sumi-radius-full);
cursor: pointer; transition: var(--sumi-transition-colors);
}
.sumi-toggle--active { background: var(--sumi-accent); }
.sumi-toggle__knob {
position: absolute; top: 2px; left: 2px;
width: 18px; height: 18px; border-radius: var(--sumi-radius-full);
background: white; box-shadow: var(--sumi-shadow-xs);
transition: transform var(--sumi-duration-fast) var(--sumi-ease-out);
}
.sumi-toggle--active .sumi-toggle__knob { transform: translateX(18px); }
```
### 6.13 Divider
```css
.sumi-divider { height: 1px; background: var(--sumi-border-faint); border: none; margin: var(--sumi-space-4) 0; }
```
### 6.14 Sidebar
```css
.sumi-sidebar {
position: fixed; top: 0; left: 0; bottom: 0;
width: var(--sumi-sidebar-width);
background: var(--sumi-bg-raised);
border-right: 1px solid var(--sumi-border-faint);
z-index: var(--sumi-z-sticky);
display: flex; flex-direction: column;
transition: width var(--sumi-duration-slow) var(--sumi-ease-out);
overflow: hidden;
}
.sumi-sidebar--collapsed { width: var(--sumi-sidebar-collapsed); }
.sumi-sidebar__nav-item {
display: flex; align-items: center; gap: var(--sumi-space-3);
padding: var(--sumi-space-2) var(--sumi-space-3);
border-radius: var(--sumi-radius-md);
color: var(--sumi-text-secondary); font-size: var(--sumi-text-sm);
font-weight: var(--sumi-weight-medium); cursor: pointer;
transition: var(--sumi-transition-colors); text-decoration: none;
}
.sumi-sidebar__nav-item:hover { background: var(--sumi-bg-hover); color: var(--sumi-text-primary); }
.sumi-sidebar__nav-item--active { background: var(--sumi-accent-subtle); color: var(--sumi-accent); }
/* Active indicator — 3px left accent bar */
.sumi-sidebar__nav-item--active::before {
content: ''; position: absolute; left: 0; top: 25%; bottom: 25%;
width: 3px; background: var(--sumi-accent);
border-radius: 0 var(--sumi-radius-full) var(--sumi-radius-full) 0;
}
.sumi-sidebar__section-label {
font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-medium);
letter-spacing: var(--sumi-tracking-wider); text-transform: uppercase;
color: var(--sumi-text-tertiary);
padding: var(--sumi-space-4) var(--sumi-space-3) var(--sumi-space-1);
}
```
### 6.15 Header
```css
.sumi-header {
position: fixed; top: 0; right: 0;
height: var(--sumi-header-height);
background: var(--sumi-glass-bg);
backdrop-filter: blur(var(--sumi-glass-blur));
-webkit-backdrop-filter: blur(var(--sumi-glass-blur));
border-bottom: 1px solid var(--sumi-border-faint);
z-index: var(--sumi-z-sticky);
display: flex; align-items: center;
padding: 0 var(--sumi-space-6); gap: var(--sumi-space-4);
}
```
### 6.16 Player Bar
```css
.sumi-player {
position: fixed; bottom: 0; left: 0; right: 0;
height: var(--sumi-player-height);
background: var(--sumi-glass-bg);
backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
border-top: 1px solid var(--sumi-border-faint);
z-index: var(--sumi-z-sticky);
display: flex; align-items: center;
padding: 0 var(--sumi-space-6);
}
```
**Player variants** (prose specs — not in Web version CSS):
| Component | Details |
|---|---|
| **Mini Player** | 3 columns: track info (left, 48px cover `rounded-md`), controls (center), volume/queue (right). Progress bar: 4px → 6px on hover, `--sumi-accent` fill. |
| **Full Player** | Background: gradient from dominant album color at 15% opacity. Cover: 280px max, `rounded-xl`, `shadow-xl`. Time: `font-mono`. |
| **Queue Drawer** | Slides up, max-height 60vh. Current track: `--sumi-accent-subtle` bg + left border. Drag handle: pill at top center. |
### 6.17 Data Display
```css
/* Table */
.sumi-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--sumi-text-sm); }
.sumi-table th {
text-align: left; padding: var(--sumi-space-2) var(--sumi-space-3);
font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-medium);
letter-spacing: var(--sumi-tracking-wide); text-transform: uppercase;
color: var(--sumi-text-tertiary); border-bottom: 1px solid var(--sumi-border-default);
}
.sumi-table td { padding: var(--sumi-space-3); border-bottom: 1px solid var(--sumi-border-faint); color: var(--sumi-text-secondary); }
.sumi-table tr:hover td { background: var(--sumi-bg-hover); }
.sumi-table .sumi-col-number { font-family: var(--sumi-font-mono); font-size: var(--sumi-text-xs); letter-spacing: var(--sumi-tracking-wide); }
/* Track List Item */
.sumi-track {
display: flex; align-items: center; gap: var(--sumi-space-3);
padding: var(--sumi-space-2) var(--sumi-space-3); border-radius: var(--sumi-radius-md);
transition: var(--sumi-transition-colors); cursor: pointer;
}
.sumi-track:hover { background: var(--sumi-bg-hover); }
.sumi-track--playing { background: var(--sumi-accent-subtle); }
.sumi-track__number { font-family: var(--sumi-font-mono); font-size: var(--sumi-text-xs); color: var(--sumi-text-tertiary); width: 24px; text-align: right; }
.sumi-track__title { font-weight: var(--sumi-weight-medium); color: var(--sumi-text-primary); }
.sumi-track__artist { font-size: var(--sumi-text-sm); color: var(--sumi-text-secondary); }
.sumi-track__duration { font-family: var(--sumi-font-mono); font-size: var(--sumi-text-xs); color: var(--sumi-text-tertiary); margin-left: auto; }
/* Stat Card */
.sumi-stat { display: flex; flex-direction: column; gap: var(--sumi-space-1); padding: var(--sumi-space-4); }
.sumi-stat__label { font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-medium); letter-spacing: var(--sumi-tracking-wide); text-transform: uppercase; color: var(--sumi-text-tertiary); }
.sumi-stat__value { font-family: var(--sumi-font-heading); font-size: var(--sumi-text-2xl); font-weight: var(--sumi-weight-bold); letter-spacing: var(--sumi-tracking-tight); color: var(--sumi-text-primary); }
.sumi-stat__value--mono { font-family: var(--sumi-font-mono); }
.sumi-stat__trend { font-family: var(--sumi-font-mono); font-size: var(--sumi-text-xs); }
.sumi-stat__trend--up { color: var(--sumi-sage); }
.sumi-stat__trend--down { color: var(--sumi-vermillion); }
```
### 6.18 Specialty Components (Thematic Personality)
```css
/* XP / Achievement Bar (Gaming touch 🎮) */
.sumi-xp-bar { display: flex; align-items: center; gap: var(--sumi-space-3); }
.sumi-xp-bar__track { flex: 1; height: 6px; background: var(--sumi-bg-hover); border-radius: var(--sumi-radius-full); overflow: hidden; }
.sumi-xp-bar__fill { height: 100%; background: var(--sumi-gold); border-radius: var(--sumi-radius-full); transition: width var(--sumi-duration-slower) var(--sumi-ease-out); }
.sumi-xp-bar__label { font-family: var(--sumi-font-mono); font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-medium); color: var(--sumi-gold); }
/* Achievement Toast (Gaming 🎮) */
.sumi-achievement {
display: flex; align-items: center; gap: var(--sumi-space-3);
background: var(--sumi-surface-elevated);
border: 1px solid var(--sumi-gold-subtle); border-left: 3px solid var(--sumi-gold);
border-radius: var(--sumi-radius-lg); padding: var(--sumi-space-3) var(--sumi-space-4);
animation: sumi-pop var(--sumi-duration-slower) var(--sumi-ease-bounce);
}
.sumi-achievement__icon {
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
background: var(--sumi-gold-subtle); border-radius: var(--sumi-radius-md); font-size: var(--sumi-text-lg);
}
/* Terminal Block (Cybersec/Linux 💻) */
.sumi-terminal {
background: var(--sumi-bg-void); border: 1px solid var(--sumi-border-default);
border-radius: var(--sumi-radius-lg); font-family: var(--sumi-font-mono);
font-size: var(--sumi-text-sm); overflow: hidden;
}
.sumi-terminal__header {
display: flex; align-items: center; gap: var(--sumi-space-2);
padding: var(--sumi-space-2) var(--sumi-space-3);
background: var(--sumi-bg-raised); border-bottom: 1px solid var(--sumi-border-faint);
}
.sumi-terminal__dot { width: 8px; height: 8px; border-radius: var(--sumi-radius-full); }
.sumi-terminal__dot--red { background: var(--sumi-vermillion); }
.sumi-terminal__dot--yellow { background: var(--sumi-gold); }
.sumi-terminal__dot--green { background: var(--sumi-sage); }
.sumi-terminal__body { padding: var(--sumi-space-4); color: var(--sumi-text-secondary); line-height: var(--sumi-leading-relaxed); }
.sumi-terminal__prompt { color: var(--sumi-accent); }
.sumi-terminal__output { color: var(--sumi-text-tertiary); }
/* Live Indicator (📡 Stream) */
.sumi-live-dot {
display: inline-flex; align-items: center; gap: var(--sumi-space-1-5);
font-size: var(--sumi-text-xs); font-weight: var(--sumi-weight-semibold);
text-transform: uppercase; letter-spacing: var(--sumi-tracking-wider);
color: var(--sumi-live);
}
.sumi-live-dot::before {
content: ''; width: 6px; height: 6px;
border-radius: var(--sumi-radius-full); background: var(--sumi-live);
animation: sumi-pulse 2s ease-in-out infinite;
}
/* Cover Art (🎵 Music) */
.sumi-cover {
border-radius: var(--sumi-radius-md); overflow: hidden;
background: var(--sumi-bg-hover); aspect-ratio: 1; position: relative;
}
.sumi-cover img { width: 100%; height: 100%; object-fit: cover; }
.sumi-cover--sm { width: 40px; height: 40px; border-radius: var(--sumi-radius-sm); }
.sumi-cover--md { width: 56px; height: 56px; }
.sumi-cover--lg { width: 160px; height: 160px; border-radius: var(--sumi-radius-lg); }
.sumi-cover--xl { width: 240px; height: 240px; border-radius: var(--sumi-radius-xl); }
.sumi-cover--hero { width: 100%; max-width: 300px; box-shadow: var(--sumi-shadow-xl); }
/* Progress Slider (🎧 Player) */
.sumi-slider {
position: relative; width: 100%; height: 4px;
background: var(--sumi-bg-hover); border-radius: var(--sumi-radius-full); cursor: pointer;
}
.sumi-slider:hover { height: 6px; }
.sumi-slider__fill { height: 100%; background: var(--sumi-accent); border-radius: var(--sumi-radius-full); position: relative; }
.sumi-slider__thumb {
position: absolute; right: -6px; top: 50%; transform: translateY(-50%);
width: 12px; height: 12px; border-radius: var(--sumi-radius-full);
background: white; box-shadow: var(--sumi-shadow-sm);
opacity: 0; transition: opacity var(--sumi-duration-fast);
}
.sumi-slider:hover .sumi-slider__thumb { opacity: 1; }
```
### 6.19 Chat (Prose Spec)
| Element | Spec |
|---|---|
| **Message Bubble** | Max-width 70%. Own: `--sumi-accent-subtle` bg, right-aligned. Others: `--sumi-bg-hover` bg, left-aligned. `rounded-xl` with squared corner on sender's side. |
| **Timestamp** | `sumi-caption`, `text-tertiary`, below message. |
| **Reactions** | Small pills, `--sumi-bg-active` bg, emoji + count. |
| **Room List** | 48px items, 36px avatar, room name `sumi-body font-medium`, last message `sumi-caption text-secondary`, unread badge `--sumi-accent` pill with white text. |
### 6.20 Skeleton / Loading
| Aspect | Spec |
|---|---|
| **Shape** | Matches the content it replaces (text line, avatar, card) |
| **Background** | `--sumi-bg-hover` (dark) / `--sumi-bg-active` (light) |
| **Animation** | `shimmer` sweep, 1.8s, ease-in-out, infinite |
| **Shimmer highlight** | `rgba(255,255,255, 0.06)` (dark) / `rgba(255,255,255, 0.40)` (light) |
| **Radius** | Matches the component being loaded |
### 6.21 Empty States
Empty states use the sumi-e aesthetic most directly. They are the moments where the app's personality shines.
- Container: centered, `max-w-sm`, `py-16`
- Illustration: Simple brush stroke SVG, `--sumi-text-tertiary` color, 120px max
- Title: `sumi-h4`, `text-primary`
- Description: `sumi-body`, `text-secondary`, 2-3 lines max
- CTA: `.sumi-btn--primary` below
- Background: Optional `.sumi-wash-texture`
---
## 7. Anti-Patterns — INTERDIT
| Ne JAMAIS faire | Faire a la place |
|---|---|
| Couleurs Tailwind par defaut (`slate`, `zinc`, `gray`) | Tokens `--sumi-*` exclusively |
| Glow/neon decoratif | Glow UNIQUEMENT pour focus rings (`--sumi-shadow-glow`) |
| Orbitron ou font gaming | Space Grotesk pour headings |
| Clip-path manga/hex | `border-radius` standard (`--sumi-radius-*`) |
| Plus de 4 couleurs d'accent | Indigo, Vermillon, Sauge, Or — c'est tout |
| Gradient sur composants | Gradient uniquement hero/cover (`.sumi-wash-texture`) |
| Box-shadow decoratif | Shadow = elevation fonctionnelle uniquement |
| Animations > 300ms | Max 300ms sauf achievements (500ms) |
| Valeurs CSS arbitraires (`w-[347px]`) | Tokens et echelle Tailwind/SUMI |
| `!important` sauf utilitaires | Specificite CSS normale |
| Neon flicker, matrix rain, terminal green text | Subtilite, discretion |
---
## 8. Component Checklist — Quality Gate
Avant de merger un composant, verifier :
- [ ] Utilise uniquement des tokens `--sumi-*`
- [ ] Fonctionne en dark ET light theme
- [ ] Responsive (teste a 320px, 768px, 1024px, 1280px)
- [ ] Focus visible avec `--sumi-shadow-glow`
- [ ] Pas plus de 2 fonts utilisees (body + heading, ou body + mono)
- [ ] Pas d'animation > 300ms (sauf achievement)
- [ ] Contrast ratio >= 4.5:1 pour le texte (WCAG AA)
- [ ] Pas de valeur CSS arbitraire
- [ ] States couverts : default, hover, active, focus, disabled
- [ ] Donnees chiffrees en `font-mono` (`--sumi-font-mono`)
---
## 9. Theme Contexts & Accessibility
### 9.1 How Sub-Themes Surface
Instead of applying ALL your interests equally across the UI (which creates visual chaos), each theme surfaces in a SPECIFIC context. The base SUMI aesthetic is always the foundation.
#### Theme Map
| Theme | Where It Appears | How It Manifests | Never Appears In |
|---|---|---|---|
| **Sumi-e / Lavis** | Everywhere (base) | Ink neutrals, paper textures, soft shadows, ink wash gradients, brush stroke decorative elements | — |
| **Nature** | Empty states, onboarding, community features, settings backgrounds | Organic shapes in illustrations, leaf/branch motifs in empty state art, earthy warm accents, organic radius on feature cards | Navigation, data tables, admin |
| **Graffiti / Tag** | Artist profiles, creative tools, release announcements, achievement badges | Bold uppercase type moments (display font at large size), manga clip-paths on special cards, splash/drip micro-texture on album art hover, high-contrast headlines | General cards, forms, settings |
| **Gaming** | XP/levels, achievements, leaderboards, challenges, gamification UI | `--sumi-gold` accent, `.sumi-xp-bar`, `.sumi-achievement`, level numbers in bold | Chat, marketplace, analytics |
| **Terminal / Linux** | Admin panel, developer API page, system status, file manager | `.sumi-terminal`, monospace font for data, `--terminal-green` for success status, `--sumi-bg-void` even in light mode | Player, social, onboarding |
| **Music** | Player, waveforms, streaming page, track cards | Waveform visualizations, EQ bar animations, BPM/key in mono font — but these are FUNCTIONAL, not decorative. The player itself uses the base SUMI palette | Everywhere else looks the same whether it's a music app or not |
#### Implementation Examples
**Graffiti moment — Artist profile hero:**
```tsx
<div className="relative overflow-hidden rounded-2xl">
<div className="absolute inset-0 opacity-10 bg-graffiti-magenta" />
<div className="relative p-8">
<h1 className="sumi-display uppercase tracking-tight">{artist.name}</h1>
{/* Bold, high-contrast — the graffiti energy comes from typography, not color */}
</div>
</div>
```
**Gaming moment — Achievement toast:**
```tsx
<div className="sumi-achievement">
<div className="sumi-achievement__icon">🏆</div>
<div>
<div className="sumi-h4">First Upload!</div>
<div className="sumi-caption" style={{ color: 'var(--sumi-text-secondary)' }}>+100 XP</div>
</div>
</div>
```
**Terminal moment — Admin status:**
```tsx
<div className="sumi-terminal">
<div className="sumi-terminal__header">
<span className="sumi-terminal__dot sumi-terminal__dot--red" />
<span className="sumi-terminal__dot sumi-terminal__dot--yellow" />
<span className="sumi-terminal__dot sumi-terminal__dot--green" />
</div>
<div className="sumi-terminal__body">
<span className="sumi-terminal__prompt">$</span> status api-gateway
<div className="sumi-terminal__output"> online uptime: 99.97%</div>
</div>
</div>
```
### 9.2 Dark Mode vs Light Mode
Dark mode is the DEFAULT (like Spotify, Discord). Light mode is the secondary option.
#### Dark Mode (default)
- Canvas: `--sumi-bg-void` (#0c0c0f)
- Surfaces: `--sumi-surface-card` (#1a1a1f)
- Text: `--sumi-text-primary` (#f0ede8), `--sumi-text-secondary` (#a8a4a0)
- Borders: `--sumi-border-default` (rgba 10%)
- Accent: `--sumi-accent` (#7c9dd6)
- Character: Calm, focused, immersive — like a recording studio at night
#### Light Mode
- Canvas: `--sumi-bg-void` (#f0ece4) — warm washi paper, NOT pure white
- Surfaces: `--sumi-surface-card` (#ffffff)
- Text: `--sumi-text-primary` (#1a1816), `--sumi-text-secondary` (#5c5854)
- Borders: `--sumi-border-default` (rgba 10%)
- Accent: `--sumi-accent` (#4a6fa5) — darker for contrast on light
- Character: Airy, bright, like a sunlit studio with paper walls
#### Switching Behavior
- Follows system preference by default (`prefers-color-scheme`)
- User can override in settings (dark / light / auto)
- Data attribute: `[data-theme="dark"]` / `[data-theme="light"]`
- Transition between modes: `--sumi-duration-normal` `--sumi-ease-in-out` on background, color, border-color
### 9.3 Accessibility Requirements
SUMI targets **WCAG 2.1 AA** compliance (AAA where feasible).
#### Contrast Ratios (validated)
| Combination | Contrast Ratio | Rating |
|---|---|---|
| `--sumi-text-primary` on `--sumi-bg-base` (heading on dark) | ~14:1 | AAA |
| `--sumi-text-secondary` on `--sumi-bg-base` (body on dark) | ~7.5:1 | AAA |
| `--sumi-text-tertiary` on `--sumi-bg-base` (muted on dark) | ~4.5:1 | AA |
| `--sumi-accent` on `--sumi-bg-base` (accent on dark) | ~5.5:1 | AA |
| `--sumi-text-inverse` on `--sumi-accent` (text on button) | ~5.5:1 | AA |
| `--sumi-text-primary` on `--sumi-bg-base` (text on light) | ~14:1 | AAA |
| `--sumi-text-secondary` on `--sumi-bg-base` (muted on light) | ~5.5:1 | AA |
#### Focus Management
- All interactive elements MUST have visible focus states
- Focus ring: `box-shadow: var(--sumi-shadow-glow)` — 3px Indigo ring at 25%
- Focus is never hidden or removed via CSS
- Tab order follows visual reading order
- Skip-to-main-content link at the top of the page
#### Motion Accessibility
```css
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
/* Keep opacity transitions for functional feedback */
.interactive { transition: opacity 100ms ease-out; }
}
```
#### Screen Reader Requirements
- All images have `alt` text
- All icons-only buttons have `aria-label`
- Form fields have associated labels
- Status changes use `aria-live` regions
- Modal traps focus correctly
- Player controls are fully keyboard-accessible
- Current track info is announced via `aria-live="polite"`
#### Color Independence
- Never use color ALONE to convey information
- Status indicators: color + icon (checkmark for success, X for error, etc.)
- Form errors: red border + error icon + error message text
- Charts: use patterns/shapes in addition to color
### 9.4 Responsive Breakpoints
| Breakpoint | Width | Behavior |
|---|---|---|
| **Mobile** | < 640px (sm) | Single column, hamburger nav, full-width cards, bottom nav possible |
| **Tablet** | 6401023px (md) | 2-column grid, sidebar as overlay, condensed player |
| **Desktop** | 10241279px (lg) | Full sidebar + content, 3-column layouts |
| **Large** | >= 1280px (xl) | Max content width 1600px (`max-w-layout-content`), centered |
#### Mobile-Specific Rules
1. Sidebar becomes a bottom sheet or hamburger overlay
2. Player mini-bar stays at bottom (above bottom nav if present)
3. Cards go full-width with no horizontal padding reduction
4. Touch targets minimum 44px x 44px (Apple HIG)
5. No hover effects — focus/active states only
---
## 10. Implementation Guide — Tailwind + CSS Variables
### 10.1 CSS Variable Architecture
The system uses **two layers**: CSS custom properties for the raw values, and Tailwind `@theme` for mapping them to utility classes. This is already the pattern in `index.css` — we're simplifying and replacing the content.
```
index.css (source of truth)
├── :root, [data-theme="dark"] → Dark mode tokens (default)
├── [data-theme="light"] → Light mode tokens
├── @theme inline → Tailwind mapping
├── @layer base → Base styles, typography
└── @layer utilities → Custom utility classes
design-system.css → DELETED (merged into index.css)
design-tokens.css → DELETED (merged into index.css)
```
### 10.2 CSS Variables — Complete Map
```css
:root, [data-theme="dark"] {
/* ═══ BACKGROUNDS ═══ */
--sumi-bg-void: #0c0c0f;
--sumi-bg-base: #121215;
--sumi-bg-raised: #1a1a1f;
--sumi-bg-overlay: #222228;
--sumi-bg-hover: #2a2a31;
--sumi-bg-active: #32323a;
--sumi-bg-wash: #18181d;
/* ═══ SURFACES ═══ */
--sumi-surface-inset: #101013;
--sumi-surface-subtle: #1e1e24;
--sumi-surface-card: #1a1a1f;
--sumi-surface-elevated: #242430;
/* ═══ BORDERS ═══ */
--sumi-border-faint: rgba(255,255,255, 0.06);
--sumi-border-default: rgba(255,255,255, 0.10);
--sumi-border-strong: rgba(255,255,255, 0.16);
--sumi-border-focus: rgba(139,170,220, 0.50);
--sumi-border-accent: rgba(139,170,220, 0.30);
/* ═══ TEXT ═══ */
--sumi-text-primary: #f0ede8;
--sumi-text-secondary: #a8a4a0;
--sumi-text-tertiary: #706c68;
--sumi-text-disabled: #4a4844;
--sumi-text-inverse: #121215;
--sumi-text-link: #8baade;
/* ═══ PIGMENTS ═══ */
--sumi-accent: #7c9dd6;
--sumi-accent-hover: #93afe0;
--sumi-accent-active: #6b8dc6;
--sumi-accent-muted: rgba(124,157,214, 0.20);
--sumi-accent-subtle: rgba(124,157,214, 0.12);
--sumi-accent-emphasis: #5a7fba;
--sumi-vermillion: #d4634a;
--sumi-vermillion-hover: #de7a64;
--sumi-vermillion-subtle: rgba(212,99,74, 0.12);
--sumi-sage: #7a9e6c;
--sumi-sage-hover: #8eb280;
--sumi-sage-subtle: rgba(122,158,108, 0.12);
--sumi-gold: #c9a84c;
--sumi-gold-hover: #d6b860;
--sumi-gold-subtle: rgba(201,168,76, 0.12);
/* ═══ SEMANTIC ═══ */
--sumi-success: var(--sumi-sage);
--sumi-success-subtle: var(--sumi-sage-subtle);
--sumi-warning: var(--sumi-gold);
--sumi-warning-subtle: var(--sumi-gold-subtle);
--sumi-error: var(--sumi-vermillion);
--sumi-error-subtle: var(--sumi-vermillion-subtle);
--sumi-info: var(--sumi-accent);
--sumi-live: #e05a5a;
--sumi-online: var(--sumi-sage);
/* ═══ TYPOGRAPHY ═══ */
--sumi-font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--sumi-font-heading: 'Space Grotesk', 'Inter', sans-serif;
--sumi-font-mono: 'JetBrains Mono', 'SF Mono', 'Consolas', monospace;
--sumi-font-serif: 'Noto Serif JP', Georgia, serif;
--sumi-text-4xl: 2.25rem;
--sumi-text-3xl: 1.875rem;
--sumi-text-2xl: 1.5rem;
--sumi-text-xl: 1.25rem;
--sumi-text-lg: 1.125rem;
--sumi-text-md: 1rem;
--sumi-text-base: 0.875rem;
--sumi-text-sm: 0.8125rem;
--sumi-text-xs: 0.75rem;
--sumi-leading-none: 1;
--sumi-leading-tight: 1.25;
--sumi-leading-snug: 1.375;
--sumi-leading-normal: 1.5;
--sumi-leading-relaxed: 1.625;
--sumi-leading-loose: 1.75;
--sumi-tracking-tighter: -0.03em;
--sumi-tracking-tight: -0.015em;
--sumi-tracking-normal: 0;
--sumi-tracking-wide: 0.025em;
--sumi-tracking-wider: 0.05em;
--sumi-tracking-widest: 0.1em;
--sumi-weight-light: 300;
--sumi-weight-regular: 400;
--sumi-weight-medium: 500;
--sumi-weight-semibold: 600;
--sumi-weight-bold: 700;
/* ═══ SPACING ═══ */
--sumi-space-0-5: 2px;
--sumi-space-1: 4px;
--sumi-space-1-5: 6px;
--sumi-space-2: 8px;
--sumi-space-2-5: 10px;
--sumi-space-3: 12px;
--sumi-space-4: 16px;
--sumi-space-5: 20px;
--sumi-space-6: 24px;
--sumi-space-8: 32px;
--sumi-space-10: 40px;
--sumi-space-12: 48px;
--sumi-space-16: 64px;
--sumi-space-20: 80px;
/* ═══ RADIUS ═══ */
--sumi-radius-xs: 2px;
--sumi-radius-sm: 4px;
--sumi-radius-md: 6px;
--sumi-radius-lg: 12px;
--sumi-radius-xl: 16px;
--sumi-radius-2xl: 20px;
--sumi-radius-full: 9999px;
/* ═══ SHADOWS ═══ */
--sumi-shadow-xs: 0 1px 2px rgba(0,0,0,0.30);
--sumi-shadow-sm: 0 2px 4px rgba(0,0,0,0.25), 0 1px 2px rgba(0,0,0,0.20);
--sumi-shadow-md: 0 4px 12px rgba(0,0,0,0.30), 0 2px 4px rgba(0,0,0,0.15);
--sumi-shadow-lg: 0 8px 24px rgba(0,0,0,0.35), 0 4px 8px rgba(0,0,0,0.20);
--sumi-shadow-xl: 0 16px 48px rgba(0,0,0,0.40), 0 8px 16px rgba(0,0,0,0.20);
--sumi-shadow-2xl: 0 24px 64px rgba(0,0,0,0.50);
--sumi-shadow-glow: 0 0 0 3px rgba(124,157,214,0.25);
--sumi-shadow-glow-lg: 0 0 20px rgba(124,157,214,0.15);
/* ═══ GLASS ═══ */
--sumi-glass-bg: rgba(18,18,21, 0.80);
--sumi-glass-border: rgba(255,255,255, 0.08);
--sumi-glass-blur: 12px;
/* ═══ SCROLLBAR ═══ */
--sumi-scrollbar-track: transparent;
--sumi-scrollbar-thumb: rgba(255,255,255, 0.10);
--sumi-scrollbar-hover: rgba(255,255,255, 0.18);
/* ═══ MOTION ═══ */
--sumi-duration-instant: 75ms;
--sumi-duration-fast: 150ms;
--sumi-duration-normal: 200ms;
--sumi-duration-slow: 300ms;
--sumi-duration-slower: 500ms;
--sumi-ease-default: cubic-bezier(0.25, 0.1, 0.25, 1);
--sumi-ease-out: cubic-bezier(0.33, 1, 0.68, 1);
--sumi-ease-in: cubic-bezier(0.32, 0, 0.67, 0);
--sumi-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--sumi-ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
--sumi-ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.1);
--sumi-transition-colors: color var(--sumi-duration-fast) var(--sumi-ease-default),
background-color var(--sumi-duration-fast) var(--sumi-ease-default),
border-color var(--sumi-duration-fast) var(--sumi-ease-default);
--sumi-transition-opacity: opacity var(--sumi-duration-fast) var(--sumi-ease-default);
--sumi-transition-transform: transform var(--sumi-duration-normal) var(--sumi-ease-out);
--sumi-transition-shadow: box-shadow var(--sumi-duration-fast) var(--sumi-ease-default);
/* ═══ Z-INDEX ═══ */
--sumi-z-base: 0;
--sumi-z-raised: 10;
--sumi-z-dropdown: 100;
--sumi-z-sticky: 200;
--sumi-z-overlay: 300;
--sumi-z-modal: 400;
--sumi-z-popover: 500;
--sumi-z-toast: 600;
--sumi-z-tooltip: 700;
--sumi-z-max: 999;
/* ═══ LAYOUT ═══ */
--sumi-max-width: 1400px;
--sumi-max-width-content: 1200px;
--sumi-max-width-narrow: 800px;
--sumi-max-width-prose: 65ch;
--sumi-sidebar-width: 240px;
--sumi-sidebar-collapsed: 64px;
--sumi-header-height: 56px;
--sumi-player-height: 80px;
}
/* ═══ LIGHT THEME ═══ */
[data-theme="light"] {
--sumi-bg-void: #f0ece4;
--sumi-bg-base: #f6f3ed;
--sumi-bg-raised: #ffffff;
--sumi-bg-overlay: #ffffff;
--sumi-bg-hover: #ede9e1;
--sumi-bg-active: #e4e0d8;
--sumi-bg-wash: #f8f6f1;
--sumi-surface-inset: #ebe7df;
--sumi-surface-subtle: #f2eee6;
--sumi-surface-card: #ffffff;
--sumi-surface-elevated: #ffffff;
--sumi-border-faint: rgba(0,0,0, 0.05);
--sumi-border-default: rgba(0,0,0, 0.10);
--sumi-border-strong: rgba(0,0,0, 0.18);
--sumi-border-focus: rgba(80,110,170, 0.45);
--sumi-border-accent: rgba(80,110,170, 0.25);
--sumi-text-primary: #1a1816;
--sumi-text-secondary: #5c5854;
--sumi-text-tertiary: #8a8580;
--sumi-text-disabled: #b5b0aa;
--sumi-text-inverse: #f0ede8;
--sumi-text-link: #4a6fa5;
--sumi-accent: #4a6fa5;
--sumi-accent-hover: #3a5f95;
--sumi-accent-active: #5a7fb5;
--sumi-vermillion: #b84a35;
--sumi-vermillion-hover: #a03e2e;
--sumi-sage: #5a7e4e;
--sumi-sage-hover: #4d6e42;
--sumi-gold: #9a7d2e;
--sumi-gold-hover: #8a6d20;
--sumi-shadow-xs: 0 1px 2px rgba(0,0,0,0.05);
--sumi-shadow-sm: 0 2px 4px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
--sumi-shadow-md: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04);
--sumi-shadow-lg: 0 8px 24px rgba(0,0,0,0.10), 0 4px 8px rgba(0,0,0,0.05);
--sumi-shadow-xl: 0 16px 48px rgba(0,0,0,0.12), 0 8px 16px rgba(0,0,0,0.06);
--sumi-shadow-2xl: 0 24px 64px rgba(0,0,0,0.15);
--sumi-glass-bg: rgba(255,255,255, 0.85);
--sumi-glass-border: rgba(0,0,0, 0.06);
--sumi-scrollbar-thumb: rgba(0,0,0, 0.12);
--sumi-scrollbar-hover: rgba(0,0,0, 0.22);
}
```
### 10.3 shadcn/Radix Semantic Mapping
For shadcn/ui components, map `--sumi-*` tokens to the expected semantic variables:
```css
:root, [data-theme="dark"] {
--background: var(--sumi-bg-base);
--foreground: var(--sumi-text-primary);
--card: var(--sumi-surface-card);
--card-foreground: var(--sumi-text-primary);
--popover: var(--sumi-bg-overlay);
--popover-foreground: var(--sumi-text-primary);
--primary: var(--sumi-accent);
--primary-foreground: var(--sumi-text-inverse);
--secondary: var(--sumi-bg-hover);
--secondary-foreground: var(--sumi-text-primary);
--muted: var(--sumi-bg-hover);
--muted-foreground: var(--sumi-text-secondary);
--accent: var(--sumi-bg-hover);
--accent-foreground: var(--sumi-text-primary);
--destructive: var(--sumi-vermillion);
--destructive-foreground: #ffffff;
--border: var(--sumi-border-default);
--input: var(--sumi-border-default);
--ring: var(--sumi-border-focus);
--radius: var(--sumi-radius-md);
/* Sidebar mapping */
--sidebar: var(--sumi-bg-raised);
--sidebar-foreground: var(--sumi-text-secondary);
--sidebar-primary: var(--sumi-accent);
--sidebar-primary-foreground: var(--sumi-text-primary);
--sidebar-accent: var(--sumi-accent-subtle);
--sidebar-accent-foreground: var(--sumi-text-primary);
--sidebar-border: var(--sumi-border-faint);
}
```
### 10.4 Tailwind Theme Mapping
```css
@theme inline {
/* Typography */
--font-sans: var(--sumi-font-body);
--font-heading: var(--sumi-font-heading);
--font-mono: var(--sumi-font-mono);
--font-serif: var(--sumi-font-serif);
/* Semantic color mapping to Tailwind utilities */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
/* Extended palette */
--color-success: var(--sumi-success);
--color-warning: var(--sumi-warning);
--color-error: var(--sumi-error);
--color-sumi-accent: var(--sumi-accent);
--color-sumi-vermillion: var(--sumi-vermillion);
--color-sumi-sage: var(--sumi-sage);
--color-sumi-gold: var(--sumi-gold);
--color-gaming-gold: var(--sumi-gold);
--color-terminal-green: #3eaa5e;
--color-graffiti-magenta: #c840a0;
--color-sakura: #e0a0b8;
/* Radius */
--radius-sm: var(--sumi-radius-sm);
--radius-md: var(--sumi-radius-md);
--radius-lg: var(--sumi-radius-lg);
--radius-xl: var(--sumi-radius-xl);
--radius-2xl: var(--sumi-radius-2xl);
--radius-full: var(--sumi-radius-full);
/* Sidebar */
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
}
```
### 10.5 Migration Mapping (Old -> New)
| Ancien (Kodo/Fusion) | Nouveau (Sumi) |
|---|---|
| `--kodo-void` / `--veza-void` | `--sumi-bg-void` |
| `--kodo-ink` / `--veza-ink` | `--sumi-bg-base` |
| `--kodo-graphite` | `--sumi-bg-raised` |
| `--kodo-cyan` / `--veza-cyan` | `--sumi-accent` |
| `--kodo-magenta` / `--veza-magenta` | **Supprime** — utiliser `--sumi-vermillion` si danger |
| `--kodo-lime` / `--veza-lime` | **Supprime** — utiliser `--sumi-sage` si success |
| `--kodo-gold` / `--veza-xp-gold` | `--sumi-gold` |
| `--kodo-red` / `--veza-error` | `--sumi-vermillion` |
| `--glass-hud-bg` | `--sumi-glass-bg` |
| `--glow-cyan`, `--glow-magenta` etc. | **Supprime** — un seul `--sumi-shadow-glow` pour focus |
| `.clip-manga`, `.clip-hex` | **Supprime** |
| `.animate-neon-flicker` | **Supprime** |
| `.backdrop-blur-cyber` | `.sumi-glass` |
| `font-display` (Orbitron) | `font-heading` (Space Grotesk) |
| `font-body` (Barlow/DM Sans) | `font-sans` (Inter) |
### 10.6 Files to Update During Refonte
| File | Action | What Changes |
|---|---|---|
| `apps/web/src/index.css` | **Rewrite** | Replace ALL current `:root`, `.dark`, `@theme`, `@layer` content with SUMI tokens above. Keep layout utility classes. |
| `apps/web/src/styles/design-system.css` | **Delete** | All tokens merged into `index.css`. |
| `apps/web/src/styles/design-tokens.css` | **Delete** | All tokens merged into `index.css`. |
| `apps/web/docs/DESIGN_DIRECTION.md` | **Archive** | Rename to `DESIGN_DIRECTION_LEGACY.md`. Replaced by this document. |
| `apps/web/docs/DESIGN_TOKENS.md` | **Rewrite** | Point to this document as source of truth. |
| `apps/web/docs/APP_SHELL.md` | **Keep** | Still valid for shell layout. Update color references to `--sumi-*`. |
| Google Fonts link in `index.html` | **Update** | Load Inter + Space Grotesk + JetBrains Mono + Noto Serif JP. Remove Orbitron, Barlow, Bebas Neue, DM Sans. |
### 10.7 Migration Checklist
When refactoring each component/page from current system to SUMI:
```
□ Replace font references
- font-display/Orbitron → font-heading (Space Grotesk)
- font-body/Barlow/DM Sans → font-sans (Inter)
- Keep font-mono as-is (JetBrains Mono)
□ Replace color classes
- bg-kodo-void → bg-background (--sumi-bg-base)
- bg-kodo-ink → bg-card (--sumi-surface-card)
- bg-kodo-graphite → bg-muted (--sumi-bg-hover)
- text-kodo-text-main → text-foreground (--sumi-text-primary)
- text-kodo-text-dim → text-muted-foreground (--sumi-text-secondary)
- border-kodo-steel → border-border (--sumi-border-default)
- bg-kodo-cyan → bg-primary (--sumi-accent, use sparingly!)
- text-kodo-cyan → text-primary (--sumi-accent)
□ Remove excessive decoration
- Remove all hover:scale-* transforms
- Remove decorative shadows (keep only functional)
- Remove gradient backgrounds on cards
- Remove glow effects on general elements
- Remove neon border effects
□ Fix spacing
- Cards: p-4 or p-6 (not p-8)
- Sections: gap-6 or gap-8
- Page sections: gap-10 or gap-12
- Check 4px grid alignment
□ Verify accessibility
- Focus rings visible (--sumi-shadow-glow)
- Contrast ratios pass AA (4.5:1)
- Touch targets >= 44px
- Color not used alone for status
□ Test both modes
- Dark mode looks correct
- Light mode looks correct
- Transition between modes is smooth
```
### 10.8 Component Class Patterns
Standard patterns to follow when building/updating components:
```tsx
// ✅ CORRECT — SUMI style
<div className="sumi-card">
<h3 className="sumi-h4">{title}</h3>
<p className="sumi-body" style={{ color: 'var(--sumi-text-secondary)' }}>{description}</p>
</div>
// Also OK — using Tailwind + shadcn semantic mapping
<Card className="bg-card border border-border rounded-lg p-4 hover:bg-accent transition-colors">
<h3 className="text-sm font-medium font-heading text-foreground">{title}</h3>
<p className="text-xs text-muted-foreground mt-1">{description}</p>
</Card>
// ❌ WRONG — Old style
<Card className="bg-kodo-ink border-kodo-steel rounded-2xl p-8 hover:scale-[1.02] hover:shadow-neon-cyan/20">
<h3 className="text-kodo-text-main font-display">{title}</h3>
</Card>
```
```tsx
// ✅ CORRECT — Button
<button className="sumi-btn sumi-btn--primary">Upload Track</button>
// Also OK — Tailwind
<Button className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-md px-4 py-2 text-sm font-medium font-sans">
Upload Track
</Button>
// ❌ WRONG
<Button className="bg-gradient-to-r from-kodo-cyan to-kodo-magenta hover:scale-105 font-display uppercase">
UPLOAD
</Button>
```
### 10.9 Migration Order
1. Replace CSS tokens (search & replace global)
2. Delete old files (`design-system.css`, `design-tokens.css`)
3. Integrate new `--sumi-*` tokens into `index.css`
4. Migrate shell components first (sidebar, header, player)
5. Migrate primitives (buttons, inputs, cards)
6. Migrate pages
7. Validate with component checklist (Section 8)
---
## 11. Quick Reference Card
### Colors You'll Use 95% of the Time
| What | Dark Mode | Light Mode |
|---|---|---|
| Page background | `bg-background` (`--sumi-bg-base` #121215) | `bg-background` (`--sumi-bg-base` #f6f3ed) |
| Card background | `bg-card` (`--sumi-surface-card` #1a1a1f) | `bg-card` (`--sumi-surface-card` #ffffff) |
| Primary text | `text-foreground` (`--sumi-text-primary` #f0ede8) | `text-foreground` (`--sumi-text-primary` #1a1816) |
| Secondary text | `text-muted-foreground` (`--sumi-text-secondary` #a8a4a0) | `text-muted-foreground` (#5c5854) |
| Borders | `border-border` (`--sumi-border-default` rgba 10%) | same |
| Primary accent | `bg-primary` / `text-primary` (`--sumi-accent` #7c9dd6) | #4a6fa5 |
| Hover surface | `hover:bg-accent` (`--sumi-bg-hover` #2a2a31) | #ede9e1 |
### The Only Fonts
| Tailwind Class | Font | Token |
|---|---|---|
| `font-sans` | Inter | `--sumi-font-body` |
| `font-heading` | Space Grotesk | `--sumi-font-heading` |
| `font-mono` | JetBrains Mono | `--sumi-font-mono` |
| `font-serif` | Noto Serif JP | `--sumi-font-serif` (rare) |
### The Only Radii
| Token | Value | When |
|---|---|---|
| `--sumi-radius-md` (6px) | Buttons, inputs |
| `--sumi-radius-lg` (12px) | Cards |
| `--sumi-radius-xl` (16px) | Modals |
| `--sumi-radius-full` | Avatars, pills |
### The Only Shadows
| Token | When |
|---|---|
| none | Most elements (dark mode) |
| `--sumi-shadow-sm` | Cards (light mode) |
| `--sumi-shadow-lg` | Dropdowns, floating panels |
| `--sumi-shadow-xl` | Modals |
| `--sumi-shadow-glow` | Focus rings |
---
*SUMI Design System v2.0 — Veza / Talas — "L'encre et la lumiere"*
*This document is the single source of truth. All design decisions defer to it.*
*墨は心の鏡 — L'encre est le miroir du coeur*