# SUMI v3 — Specification du Design System > Ce document est la specification technique complete de SUMI v3. > Il definit les regles, contraintes et tokens a implementer pour obtenir > une UI singuliere, vivante et econome en ressources. > Toute modification de l'UI doit etre conforme a ce document. > > Direction artistique : **lavis japonais interactif (sumi-e numerique)**. > Voir [[05_EXPERIENCE_UTILISATEUR/DIRECTION_ARTISTIQUE_TALAS]] pour la philosophie complete. --- ## 1. Ce qui change entre SUMI v2 et v3 | SUMI v2 (actuel) | SUMI v3 (cible) | |------------------|-----------------| | Palette indigo froide (#7c9dd6) | Palette cyan (#0098B5) — touche de couleur unique dans un lavis monochrome | | Surfaces plates (flat design) | Surfaces = couches d'encre diluee sur papier washi | | Themes statiques (dark/light) | Theme circadien (le lavis suit le soleil — papier chaud/froid/inverse) | | Interface figee | Interface qui se patine (le papier vieillit, l'encre s'oxyde) | | Animations generiques | Animations avec le poids de l'eau (goutte/trait/lavis/vague/maree) | | Icones standard | Icones = gestes calligraphiques dessines a la main | | Player = barre en bas | Player = suzuri (pierre a encre) — objet le plus dense de l'interface | | Performance non budgetee | Budget strict par metrique | | Bordures nettes (border CSS) | Bords diffus comme l'encre dans l'eau (box-shadow tres diffus) | **Ce qui est conserve :** - L'architecture tokens (CSS custom properties + TypeScript) - Le nommage `--sumi-*` pour toutes les variables - La grille de 4px - Les composants existants (refactores, pas remplaces) - Tailwind v4 CSS-first - Les modes accessibilite (high contrast, reduced motion, density) - Space Grotesk + Inter + JetBrains Mono (deja valides dans la direction artistique) --- ## 2. Palette — encre, papier et cyan ### 2.1 Accent principal : le cyan Le cyan (#0098B5) est la seule couleur d'accent. Dans un lavis traditionnel, une seule touche de couleur suffit a tout transformer. Le cyan est cette touche. Il evoque l'eau — l'element qui transporte l'encre, qui donne vie au lavis. Il contraste avec le noir/blanc ambiant comme un eclat de ciel dans un paysage monochrome. ```css /* SUMI v3 — Pigment primaire (cyan) */ --sumi-accent: #0098B5; --sumi-accent-hover: #00B4D8; --sumi-accent-active: #007A94; --sumi-accent-muted: rgba(0, 152, 181, 0.15); --sumi-accent-subtle: rgba(0, 152, 181, 0.08); --sumi-accent-emphasis: #006B7F; /* Focus ring — cyan diffus dans l'eau */ --sumi-border-focus: rgba(0, 152, 181, 0.50); --sumi-shadow-glow: 0 0 0 3px rgba(0, 152, 181, 0.25); --sumi-shadow-glow-lg: 0 0 20px rgba(0, 152, 181, 0.12); ``` ### 2.2 Pigments fonctionnels — pastel diffuse Les couleurs fonctionnelles sont des pigments dilues dans beaucoup d'eau. Elles apparaissent rarement : en lignes fines, en micro-points, en halos subtils. Jamais en aplats. Elles ne rivalisent jamais avec le cyan. ```css --sumi-success: rgba(90, 140, 100, 0.60); /* Vert sauge dilue */ --sumi-error: rgba(180, 80, 70, 0.55); /* Rouge brique dilue */ --sumi-warning: rgba(190, 150, 60, 0.55); /* Ambre dilue */ --sumi-info: #0098B5; /* Le cyan lui-meme */ ``` Regles des couleurs fonctionnelles : - Jamais en aplat (pas de fond rouge plein pour une erreur) - Toujours en ligne fine, point, bordure, ou halo diffus - L'opacite ne depasse jamais 60% — elles sont toujours "diluees" - Si le cyan est present au meme endroit, la couleur fonctionnelle passe en gris + icone ### 2.3 Fonds — papier washi et encre noire Le fond est du papier. Jamais une surface lisse. Jamais du blanc pur. Jamais du noir pur. Le papier a une texture, une chaleur, une vie. ```css /* Jour (papier washi clair) */ --sumi-bg-void: #EBE5DD; /* Washi vieilli — le papier le plus expose */ --sumi-bg-base: #F2EDE6; /* Fond principal — papier washi */ --sumi-bg-raised: #F7F3EC; /* Surfaces posees sur le papier */ --sumi-bg-overlay: #FBF8F2; /* Dropdowns, overlays */ --sumi-bg-hover: rgba(26, 26, 30, 0.06); /* Encre tres diluee */ --sumi-bg-active: rgba(26, 26, 30, 0.10); /* Encre legerement plus dense */ /* Nuit (lavis inverse — papier sombre, encre claire) */ --sumi-bg-void: #09090B; /* Noir d'encre profond */ --sumi-bg-base: #0D0D0F; /* Fond principal — papier sombre */ --sumi-bg-raised: #141416; /* Surfaces : couche d'encre legerement plus claire */ --sumi-bg-overlay: #1A1A1E; /* Overlays */ --sumi-bg-hover: rgba(232, 227, 219, 0.06); /* Encre claire diluee */ --sumi-bg-active: rgba(232, 227, 219, 0.10); /* Encre claire plus dense */ ``` ### 2.4 Texte — encre sur papier Le texte est de l'encre posee sur du papier. Noir profond le jour, blanc creme la nuit. Le texte secondaire est de l'encre diluee. ```css /* Jour */ --sumi-text-primary: #1A1A1E; /* Encre sumi — dense mais pas pur */ --sumi-text-secondary: #9A958D; /* Encre diluee — gris chaud */ --sumi-text-tertiary: #B5B0A8; /* Encre tres diluee — s'efface comme un lavis dilue */ --sumi-text-link: #0098B5; /* Liens = accent cyan */ /* Nuit (inversion) */ --sumi-text-primary: #E8E3DB; /* Encre claire sur papier sombre */ --sumi-text-secondary: #9A958D; /* Encre diluee — reste gris chaud */ --sumi-text-tertiary: #706B63; /* Encre tres diluee en mode nuit */ --sumi-text-link: #006B7F; /* Cyan profond la nuit */ ``` --- ## 3. Texture washi et couches d'encre ### 3.1 Principe Les surfaces ne sont jamais parfaitement lisses. Le fond porte la texture du papier washi — grain subtil, fibres visibles, surface vivante. Les elements poses dessus sont des couches d'encre diluee a differentes concentrations. ### 3.2 Implementation technique — texture washi **Un seul fichier image : `washi.png` (512x512, ~8 Ko)** Photo d'un vrai papier washi, desaturee, grain visible. Applique sur le body en pseudo-element. ```css /* Pseudo-element sur le body — texture washi globale */ body::after { content: ''; position: fixed; inset: 0; pointer-events: none; z-index: var(--sumi-z-max); background-image: url('/washi.png'); background-repeat: repeat; opacity: var(--sumi-washi-opacity, 0.04); mix-blend-mode: soft-light; } /* Variable de controle */ :root { --sumi-washi-opacity: 0.04; /* Subtil — a peine perceptible */ } /* Desactive en mode eco ou reduced-motion */ @media (prefers-reduced-motion: reduce) { body::after { display: none; } } [data-eco="true"] body::after { display: none; } ``` **Cout en performance :** ~8 Ko au chargement, 0 impact en runtime (image statique, GPU-composited via `position: fixed`). Negligeable. ### 3.3 Couches d'encre par surface Les surfaces elevees sont des couches d'encre diluee posees sur le papier. Pas de box-shadow traditionnelle — des diffusions douces, comme l'encre qui s'etend dans du papier mouille. ```css /* Les cartes sont des voiles d'encre poses sur le washi */ .sumi-card { background: rgba(26, 26, 30, 0.04); backdrop-filter: blur(8px); /* Pas de border — un bord diffus d'encre */ box-shadow: 0 0 8px rgba(26, 26, 30, 0.05); border: none; } /* Overlays — encre plus concentree */ .sumi-overlay { background: rgba(26, 26, 30, 0.08); backdrop-filter: blur(16px); box-shadow: 0 0 16px rgba(26, 26, 30, 0.08); border: none; } /* Modales — couche la plus dense */ .sumi-modal { background: rgba(26, 26, 30, 0.15); backdrop-filter: blur(24px); box-shadow: 0 0 24px rgba(26, 26, 30, 0.10); border: none; } ``` | Elevation | Opacite encre | Blur du backdrop | Effet | |-----------|--------------|-----------------|-------| | Fond (papier) | 0% | — | Le papier washi brut | | Surface (carte) | 4-8% | 8px | Un voile d'encre tres leger | | Overlay (dropdown) | 8-12% | 16px | Une couche plus dense | | Modale | 12-20% | 24px | Une zone nettement marquee | | Player (suzuri) | 20-30% | 32px | L'element le plus "encre" — le plus present | ### 3.4 Bordures = diffusion d'encre Les bordures ne sont pas des lignes CSS nettes. Elles sont des bords diffus d'encre, comme le contour d'une tache d'eau sur du papier. ```css /* MAL — bordure nette, mecanique */ border: 1px solid rgba(0, 0, 0, 0.1); /* BIEN — bord diffus d'encre */ box-shadow: 0 0 6px rgba(26, 26, 30, 0.06); border: none; ``` **Regle : jamais de `border` visible. Toujours un `box-shadow` diffus (spread 0, blur 4-8px, opacite 3-8%).** --- ## 4. Theme circadien — le lavis suit le soleil ### 4.1 Concept La "temperature" du papier change avec l'heure, comme la lumiere naturelle sur un rouleau de papier pose pres d'une fenetre. Le matin, le papier est chaud. Le jour, il est neutre. Le soir, il se rechauffe. La nuit, le lavis s'inverse : papier sombre, encre claire, cyan profond. ### 4.2 Phases circadiennes | Phase | Heures | Ambiance | Modification | |-------|--------|---------|-------------| | **Aube** | 05h-08h | Papier rose/chaud, encre douce | Fonds legerement plus chauds, cyan tire vers le teal | | **Jour** | 08h-17h | Papier neutre, contraste maximal | Palette standard — cyan vif | | **Crepuscule** | 17h-20h | Papier qui se rechauffe, encre dense | Fonds plus chauds, cyan plus profond | | **Nuit** | 20h-05h | Lavis inverse | Papier sombre (#0D0D0F), encre claire (#E8E3DB), cyan profond (#006B7F) | ### 4.3 Implementation technique ```typescript // circadian.ts — calcule les ajustements de palette interface CircadianShift { bgWarmth: number; // -5 to +5 (degres hue shift sur les fonds) accentWarmth: number; // -3 to +3 (degres hue shift sur l'accent) brightness: number; // 0.95 to 1.05 (multiplicateur de luminosite) contrast: number; // 0.97 to 1.0 (multiplicateur de contraste) } function getCircadianShift(hour: number): CircadianShift { if (hour >= 5 && hour < 8) { // Aube — papier plus chaud, encre plus douce return { bgWarmth: 3, accentWarmth: 2, brightness: 1.02, contrast: 0.98 }; } if (hour >= 8 && hour < 17) { // Jour — neutre, contraste maximal, cyan vif return { bgWarmth: 0, accentWarmth: 0, brightness: 1.0, contrast: 1.0 }; } if (hour >= 17 && hour < 20) { // Crepuscule — papier qui se rechauffe, encre plus dense return { bgWarmth: 4, accentWarmth: 3, brightness: 0.98, contrast: 0.99 }; } // Nuit — le lavis s'inverse, cyan s'approfondit return { bgWarmth: 1, accentWarmth: -1, brightness: 0.95, contrast: 0.97 }; } ``` ### 4.4 Application aux tokens CSS ```typescript // Dans ThemeProvider, au montage + toutes les 30 minutes function applyCircadianShift() { const hour = new Date().getHours(); const shift = getCircadianShift(hour); // Applique les shifts via des custom properties sur :root document.documentElement.style.setProperty( '--sumi-circadian-warmth', `${shift.bgWarmth}deg` ); document.documentElement.style.setProperty( '--sumi-circadian-brightness', `${shift.brightness}` ); } // Recalcul toutes les 30 min (pas besoin de plus frequent) setInterval(applyCircadianShift, 30 * 60 * 1000); ``` ```css /* Les fonds utilisent un filtre CSS plutot que de recalculer les couleurs */ :root { --sumi-circadian-warmth: 0deg; --sumi-circadian-brightness: 1; } /* Applique sur le conteneur principal, pas sur chaque element */ #app { filter: hue-rotate(var(--sumi-circadian-warmth)) brightness(var(--sumi-circadian-brightness)); transition: filter 300s linear; /* Transition de 5 min, imperceptible */ } ``` ### 4.5 Contraintes - Le changement est **imperceptible en temps reel** (transition de 5 min) - L'utilisateur ne doit jamais "voir" le changement — il le ressent - L'accent cyan reste toujours reconnaissable (shift max : +/- 3 degres de hue) - Le mode circadien est optionnel : un toggle dans les settings pour le desactiver - En mode jour (papier clair), le circadien ajuste la temperature mais ne passe jamais en nuit (papier sombre), et inversement - La nuit est une inversion complete du lavis : papier sombre + encre claire + cyan profond - La transition jour/nuit est un fondu de 30 minutes, imperceptible - **Cout CPU : un setInterval de 30 min + 2 CSS custom properties. Negligeable.** --- ## 5. Systeme de patine ### 5.1 Concept Avec le temps et l'usage, l'interface se patine comme un rouleau ancien. Le papier vieillit. L'encre s'oxyde et gagne en profondeur. Le grain du washi devient plus prononce. Le cyan prend des nuances plus profondes. ### 5.2 Tokens de patine ```css :root { /* Calculees au login, basees sur le profil utilisateur */ --sumi-patina-level: 0; /* 0-4, entier */ --sumi-patina-warmth: 0; /* 0-10, degres de hue shift additionnel */ --sumi-patina-washi: 0.04; /* 0.04-0.08, opacite de la texture washi */ --sumi-patina-depth: 0; /* 0-3, diffusion d'encre additionnelle */ --sumi-patina-texture: none; /* none | subtle | rich | deep */ } ``` ### 5.3 Calcul de la patine ```typescript interface UserPatinaData { accountAgeDays: number; totalPlayTimeHours: number; totalUploads: number; totalMessages: number; totalExchanges: number; } function calculatePatina(data: UserPatinaData): PatinaTokens { // Score composite (0-100) const ageScore = Math.min(data.accountAgeDays / 365, 1) * 25; const playScore = Math.min(data.totalPlayTimeHours / 500, 1) * 25; const createScore = Math.min(data.totalUploads / 20, 1) * 25; const socialScore = Math.min( (data.totalMessages + data.totalExchanges * 5) / 200, 1 ) * 25; const score = ageScore + playScore + createScore + socialScore; // Niveau discret (0-4) let level = 0; if (score >= 15) level = 1; // Rode if (score >= 35) level = 2; // Vecu if (score >= 60) level = 3; // Patine if (score >= 85) level = 4; // Heritage return { level, warmth: level * 2.5, // 0, 2.5, 5, 7.5, 10 washi: 0.04 + (level * 0.01), // 0.04, 0.05, 0.06, 0.07, 0.08 depth: level * 0.75, // 0, 0.75, 1.5, 2.25, 3 texture: ['none', 'subtle', 'subtle', 'rich', 'deep'][level], }; } ``` ### 5.4 Application visuelle par niveau | Niveau | Texture washi | Chaleur | Diffusion encre | Effet visuel | |--------|--------------|---------|----------------|-------------| | 0 — Neuf | 0.04 | Aucune | Standard | Papier frais, encre nette | | 1 — Rode | 0.05 | +2.5 hue | Legerement plus diffuse | Papier qui commence a jaunir | | 2 — Vecu | 0.06 | +5 hue | Notablement plus riche | Fibres du washi plus visibles, encre plus chaude | | 3 — Patine | 0.07 | +7.5 hue | Diffusion prononcee | Papier ancien, encre oxydee aux reflets bruns | | 4 — Heritage | 0.08 | +10 hue | Profonde et chaleureuse | Rouleau ancien — texture riche, encre patinee, cyan profond | ### 5.5 CSS de la patine ```css /* Applique via data-attribute sur html */ [data-patina="1"] { --sumi-washi-opacity: 0.05; } [data-patina="2"] { --sumi-washi-opacity: 0.06; } [data-patina="3"] { --sumi-washi-opacity: 0.07; /* Bords d'encre legerement plus diffus — l'encre a eu le temps de s'oxyder */ --sumi-border-default: 0 0 8px rgba(26, 26, 30, 0.08); } [data-patina="4"] { --sumi-washi-opacity: 0.08; --sumi-border-default: 0 0 10px rgba(26, 26, 30, 0.12); --sumi-shadow-sm: 0 2px 8px rgba(26, 26, 30, 0.10), 0 0 4px rgba(0, 152, 181, 0.06); } /* La patine se combine avec le circadien */ /* hue-rotate du circadien + hue-rotate de la patine = chaleur cumulee */ #app { filter: hue-rotate(calc(var(--sumi-circadian-warmth) + var(--sumi-patina-warmth, 0deg))) brightness(var(--sumi-circadian-brightness)); } ``` ### 5.6 Regles inviolables de la patine 1. **Invisible consciemment.** Si quelqu'un remarque "tiens l'UI a change", c'est trop fort. 2. **Pas de notification.** Jamais "Felicitations, vous avez atteint le niveau 3". 3. **Pas de badge.** La patine n'est pas visible par les autres utilisateurs. 4. **Pas d'avantage fonctionnel.** Zero feature debloquee par la patine. 5. **Irreversible et non-achetable.** Seul le temps et l'usage la creent. 6. **Calculee au login, pas en temps reel.** Un seul calcul, pas de polling. --- ## 6. Animations — le poids de l'eau ### 6.1 Principe physique Chaque element de l'UI a le poids de ce qu'il represente dans le lavis. Les elements legers bougent comme des gouttes d'encre. Les elements lourds bougent comme des vagues. L'eau est la metaphore universelle du mouvement. ### 6.2 Classification des elements par masse | Masse | Elements | Metaphore lavis | Duree | Easing | |-------|---------|----------------|-------|--------| | **Goutte** | Tooltips, badges, toggles | Goutte d'encre qui tombe | 100ms | ease-out | | **Trait** | Boutons, icones, liens | Trait de pinceau rapide | 150ms | ease-out avec rebond leger | | **Lavis** | Cards, dropdowns, tabs | Encre qui s'etend dans l'eau | 250ms | cubic-bezier(0.25, 0.8, 0.25, 1) | | **Vague** | Modales, sidebar, panneaux | Vague lente sur papier mouille | 350ms | cubic-bezier(0.16, 1, 0.3, 1) | | **Maree** | Navigation de page, player | Le mouvement de fond de l'ocean | 450ms | cubic-bezier(0.33, 1, 0.68, 1) | ### 6.3 Tokens de mouvement v3 ```css :root { /* Durees par masse */ --sumi-motion-goutte: 100ms; --sumi-motion-trait: 150ms; --sumi-motion-lavis: 250ms; --sumi-motion-vague: 350ms; --sumi-motion-maree: 450ms; /* Easings par masse */ --sumi-ease-goutte: cubic-bezier(0.25, 0.1, 0.25, 1); --sumi-ease-trait: cubic-bezier(0.33, 1, 0.68, 1); --sumi-ease-lavis: cubic-bezier(0.25, 0.8, 0.25, 1); --sumi-ease-vague: cubic-bezier(0.16, 1, 0.3, 1); --sumi-ease-maree: cubic-bezier(0.33, 1, 0.68, 1); --sumi-ease-rebond: cubic-bezier(0.34, 1.56, 0.64, 1); /* Pour les gouttes/traits */ } ``` ### 6.4 Regle des 2 animations max **Jamais plus de 2 animations simultanees a l'ecran.** Si un troisieme element doit s'animer, il attend que le premier termine. Implemente via un compteur CSS ou un gestionnaire JS leger. ### 6.5 Transitions de page ```css /* La page entrante apparait par couches — comme un lavis qui se revele */ .page-enter { opacity: 0; transform: translateY(6px); } .page-enter-active { opacity: 1; transform: translateY(0); transition: opacity var(--sumi-motion-lavis) var(--sumi-ease-trait), transform var(--sumi-motion-lavis) var(--sumi-ease-vague); } /* La page sortante se dissout — comme l'encre qui seche */ .page-exit { opacity: 1; } .page-exit-active { opacity: 0; transition: opacity 150ms ease-out; } ``` ### 6.6 Hover — l'encre s'etend Les hover states font "s'etendre" l'element legerement, comme une goutte d'eau qui s'elargit sur le papier. ```css .sumi-interactive:hover { transform: scale(1.01); box-shadow: 0 0 12px rgba(26, 26, 30, 0.08); transition: transform var(--sumi-motion-trait) var(--sumi-ease-trait), box-shadow var(--sumi-motion-trait) var(--sumi-ease-trait); } ``` --- ## 7. Budgets de performance ### 7.1 Budgets stricts | Metrique | Budget | Actuel (SUMI v2) | Methode de mesure | |----------|--------|-----------------|-------------------| | Bundle JS initial (gzip) | **< 120 Ko** | ~150 Ko | `vite build --report` | | Bundle CSS total (gzip) | **< 15 Ko** | ? | Idem | | First Contentful Paint | **< 1.2s** | < 1.5s | Lighthouse | | Largest Contentful Paint | **< 2.0s** | ? | Lighthouse | | Time to Interactive | **< 1.8s** | < 2s | Lighthouse | | Cumulative Layout Shift | **< 0.05** | ? | Lighthouse | | Total Blocking Time | **< 150ms** | ? | Lighthouse | | Requetes au chargement initial | **<= 5** | ? | Network tab | | Polices chargees | **<= 2 fichiers** | 3-4 | Network tab | | Images texture washi | **<= 10 Ko total** | 0 | Taille des assets | | Animations simultanees max | **2** | Illimite | Audit visuel | | `requestAnimationFrame` actifs au repos | **0** | ? | Performance tab | | Memoire JS heap (idle) | **< 30 Mo** | ? | Chrome DevTools | ### 7.2 Strategies d'optimisation **Polices :** - Inter : charger uniquement Regular (400) et Semi-Bold (600) en variable font - Space Grotesk : charger uniquement Bold (700) en variable font - JetBrains Mono : charger uniquement Regular (400) — utilise rarement - Format : `woff2` uniquement - `font-display: swap` obligatoire - Preload les 2 polices principales dans `
` - **Budget total polices : < 80 Ko** **Code splitting :** ```typescript // Chaque page est un chunk separe const Library = lazy(() => import('./pages/Library')); const Discover = lazy(() => import('./pages/Discover')); const Profile = lazy(() => import('./pages/Profile')); const Settings = lazy(() => import('./pages/Settings')); // Le player est dans le bundle initial (toujours visible) ``` **Images :** - Format : WebP (fallback JPEG), AVIF si support - Lazy loading : `loading="lazy"` sur toutes les images hors viewport - Responsive : `srcset` avec 3 tailles (320w, 640w, 1280w) - Pochettes d'album : max 300x300 en liste, 600x600 en page artiste - Avatars : max 128x128 **Mode eco :** ```css [data-eco="true"] { /* Desactive les textures et animations */ body::after { display: none; } /* texture washi */ * { animation-duration: 0ms !important; } #app { filter: none; } /* circadien + patine */ } ``` **Prefetch intelligent :** - Prefetch la page la plus probable (ex: clic sur un artiste -> prefetch sa page) - Ne PAS prefetch toutes les pages - Utiliser `` et non `preload` (priorite basse) --- ## 8. Iconographie — gestes calligraphiques ### 8.1 Principes Les icones SUMI v3 sont des **gestes dessines** — comme des caracteres calligraphiques simplifies. Elles ne sont pas des SVG generes par ordinateur. L'irregularite du trace est volontaire et preservee. ### 8.2 Contraintes techniques | Contrainte | Valeur | |-----------|--------| | Format | SVG inline (pas de sprite, pas de font icon) | | Taille de reference | 24x24 viewBox | | Epaisseur de trait | 2px, variable (plus epaisse au debut du geste, plus fine a la fin) | | Style | Stroke only (pas de fill), sauf exceptions | | Couleur | `currentColor` (herite du texte parent) | | Nombre max d'icones | 30 (pas plus — chaque icone est dessinee specifiquement) | | Lignes | Irreguliere comme un vrai trait de pinceau (pas de Bezier parfaites) | | Coins | Mix organique (pas uniformement arrondis) | ### 8.3 Correspondances icone -> metaphore | Fonction | Metaphore | Description du geste | |----------|-----------|---------------------| | Play | Trait de pinceau diagonal | Un triangle forme par un seul trait rapide | | Pause | Deux colonnes d'encre | Deux traits verticaux paralleles | | Recherche | Enso (cercle zen) | Un cercle ouvert — non ferme, comme un enso | | Profil | Capsule de micro | Forme ovale avec un trait de base | | Chat / Messages | Onde sonore | Trois arcs concentriques | | Upload | Trait ascendant | Un trait qui monte avec une goutte au sommet | | Settings | Ensui (roue) | Cercle avec trait directionnel | | Home | Montagne | Triangle inverse — symbole de montagne minimaliste | | Notifications | Point d'encre | Cercle avec une onde de diffusion | | Like / Favori | Trait croise | Deux traits qui se rencontrent en pointe | | Volume | Ondes | Arc de cercle avec des ondes de diffusion | | Menu | Trois traits | Trois traits horizontaux d'epaisseur variable | | Close | Croix de pinceau | Deux traits croises d'un seul geste | | Download | Trait descendant | Un trait qui descend vers une ligne de base | | Share | Traces | Trois points relies par des traits de pinceau | | Filter | Entonnoir de traits | Deux traits convergents vers le bas | | Expand | Triangle ouvert | Symbole d'amplification — triangle de pinceau | ### 8.4 Production Dessiner les icones a la main (papier ou tablette), puis vectoriser dans Inkscape. L'irregularite du trace est volontaire et ne doit pas etre "corrigee" par l'outil de vectorisation. Les icones sont stockees comme composants React SVG individuels dans `apps/web/src/components/icons/`. --- ## 9. Le Player — suzuri (pierre a encre) ### 9.1 Principe Le player est la pierre a encre (suzuri) de l'interface — l'objet le plus lourd, le plus present, le plus ancre. C'est la surface la plus "encree", la plus opaque, la plus dense. Ce n'est pas une barre generique en bas de page. C'est le coeur de l'experience. ### 9.2 Contraintes de design | Contrainte | Valeur | |-----------|--------| | Position | Bas de page, pleine largeur, toujours visible | | Hauteur collapsed | 80px (--sumi-player-height) | | Hauteur expanded | Jusqu'a 60vh (vue immersive — le lavis s'intensifie) | | Waveform | Traits de pinceau horizontaux — le son EST l'encre | | Controle volume | Trait vertical avec un point (goutte de pinceau) — pas un slider classique | | Patine | Le suzuri est l'element qui se patine le plus vite et le plus visiblement | | Adaptabilite | Le player prend les couleurs de l'artiste en cours d'ecoute | | Animation au repos | Micro-oscillation du VU-metre (respiration) — CSS uniquement | | Transition expand/collapse | --sumi-motion-vague + --sumi-ease-vague | | Touch | Swipe up pour expand, swipe down pour collapse | ### 9.3 Waveform La waveform est pre-calculee cote serveur (tableau de 200 valeurs d'amplitude par morceau). Les barres ne sont pas rectangulaires — elles ont des bords arrondis et une opacite variable, comme des traits d'encre poses cote a cote. ```css .waveform-bar { width: 2px; margin: 0 1px; background: var(--sumi-accent); border-radius: 50%; /* Bords arrondis comme un trait de pinceau */ transition: height var(--sumi-motion-goutte) var(--sumi-ease-goutte); } .waveform-bar.played { opacity: 1; /* Encre dense — la partie jouee */ } .waveform-bar.unplayed { opacity: 0.25; /* Lavis dilue — la partie a jouer */ } ``` **Pas de canvas WebGL, pas de Three.js, pas de librairie audio-visualizer.** 200 divs CSS = leger et suffisant. ### 9.4 Controle de volume Le volume est un trait vertical avec un point — comme un trait de pinceau interrompu par une goutte. Implementation : - SVG : un trait vertical (`