Knowledge base of ~80+ markdown files across 14 domains (00-13), Logseq graph, hardware design files (KiCAD), infrastructure configs, and talas-wiki static site. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
851 lines
32 KiB
Markdown
851 lines
32 KiB
Markdown
# 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 `<head>`
|
|
- **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 `<link rel="prefetch">` 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 (`<line>`) avec un cercle (`<circle>`) positionne dessus
|
|
- Evenements pointer (pointerdown/move/up) pour le drag vertical
|
|
- Le composant entier < 50 lignes de code
|
|
- Fallback accessibilite : `<input type="range">` pour screen readers (visuellement cache)
|
|
|
|
### 9.5 Mode expanded
|
|
|
|
Quand le player est en mode expanded (vue immersive) :
|
|
- Le papier de fond s'assombrit (le lavis s'intensifie)
|
|
- L'encre de la waveform s'elargit et prend plus de place
|
|
- Le cyan s'illumine — il devient plus vif, comme un pigment concentre
|
|
- L'espace negatif augmente — le vide (ma) entoure les elements
|
|
|
|
---
|
|
|
|
## 10. Mode eco
|
|
|
|
### 10.1 Declenchement
|
|
|
|
Le mode eco est active :
|
|
- Manuellement par l'utilisateur (toggle dans les settings)
|
|
- Automatiquement si `prefers-reduced-motion: reduce`
|
|
- Automatiquement si `Save-Data: on` (header HTTP)
|
|
- Automatiquement si la batterie < 20% (`navigator.getBattery()` si disponible)
|
|
|
|
### 10.2 Ce que le mode eco desactive
|
|
|
|
| Fonctionnalite | Etat en mode eco |
|
|
|---------------|-----------------|
|
|
| Texture washi | Desactivee |
|
|
| Circadien | Desactive (palette statique) |
|
|
| Patine | Desactivee (palette standard) |
|
|
| Animations | Reduites a 0ms |
|
|
| Waveform player | Barre de progression simple |
|
|
| Controle volume | Slider lineaire classique |
|
|
| Prefetch | Desactive |
|
|
| Images pochettes | Taille reduite (160x160 max) |
|
|
| Diffusions d'encre (box-shadow) | Desactivees |
|
|
| Backdrop-filter (blur) | Desactive |
|
|
|
|
Le mode eco ne change PAS :
|
|
- La palette de couleurs de base (cyan, encre, papier)
|
|
- La typographie
|
|
- La disposition
|
|
- Les fonctionnalites
|
|
|
|
**L'app reste complete et utilisable. Elle perd juste sa texture —
|
|
comme un lavis sur papier lisse au lieu de papier washi.**
|
|
|
|
---
|
|
|
|
## 11. Themes artistes
|
|
|
|
### 11.1 Concept
|
|
|
|
Quand l'utilisateur navigue sur le profil d'un artiste ou ecoute un de ses
|
|
morceaux, l'interface s'adapte aux couleurs choisies par l'artiste.
|
|
|
|
### 11.2 Variables d'artiste
|
|
|
|
Chaque artiste peut definir :
|
|
|
|
```css
|
|
/* Stocke dans le profil artiste, charge avec les donnees du profil */
|
|
--artist-hue: 220; /* Teinte principale (0-360) */
|
|
--artist-saturation: 0.6; /* Saturation (0-1) */
|
|
--artist-lightness: 0.5; /* Luminosite (0-1) */
|
|
```
|
|
|
|
### 11.3 Application
|
|
|
|
Quand un profil artiste est affiche ou qu'un morceau est en cours de lecture :
|
|
|
|
```css
|
|
/* Le player et la page profil utilisent la couleur artiste */
|
|
.artist-context {
|
|
--sumi-accent: hsl(
|
|
var(--artist-hue),
|
|
calc(var(--artist-saturation) * 100%),
|
|
calc(var(--artist-lightness) * 100%)
|
|
);
|
|
}
|
|
```
|
|
|
|
Les autres elements (navigation, sidebar) restent en cyan standard.
|
|
Seuls le player (suzuri) et la zone de contenu s'adaptent.
|
|
|
|
### 11.4 Contraintes
|
|
|
|
- La couleur artiste ne doit jamais nuire a la lisibilite (contraste WCAG AA)
|
|
- Si le contraste est insuffisant, fallback sur le cyan standard
|
|
- L'artiste ne controle QUE la teinte — pas les fonds, pas la typo, pas le layout
|
|
- En mode high contrast, les couleurs artistes sont ignorees
|
|
|
|
---
|
|
|
|
## 12. Regles inviolables de SUMI v3
|
|
|
|
1. **Cyan partout.** L'accent est toujours cyan (#0098B5), sauf dans un contexte
|
|
artiste. Pas d'autre couleur primaire. Le cyan est la goutte de pigment
|
|
dans l'eau noire — rare et precieux.
|
|
|
|
2. **Texture washi toujours.** Sauf en mode eco. Opacite max 0.08.
|
|
Le fond n'est jamais une surface lisse.
|
|
|
|
3. **2 animations max.** Jamais 3 elements qui bougent en meme temps.
|
|
|
|
4. **Pas de blanc pur (#FFFFFF).** Le blanc le plus clair est #F2EDE6 (papier washi).
|
|
Le blanc pur n'existe pas dans un lavis — le papier a toujours une teinte.
|
|
|
|
5. **Pas de noir pur (#000000).** Le noir le plus profond est #0D0D0F (noir d'encre).
|
|
Le noir pur est plat et sans vie — l'encre sumi a toujours de la profondeur.
|
|
|
|
6. **Pas d'icone generique.** Chaque icone est un geste calligraphique dessine
|
|
specifiquement. Si une icone n'existe pas dans le set, elle n'est pas utilisee.
|
|
|
|
7. **Le suzuri (player) est sacre.** C'est l'element le plus soigne, le plus teste,
|
|
le plus patine. Ne jamais le traiter comme un composant secondaire.
|
|
|
|
8. **Rien n'est lineaire.** Pas de `transition: all 0.3s linear`. Toute animation
|
|
a un easing organique — le mouvement de l'eau, pas d'un mecanisme.
|
|
|
|
9. **Le mode eco doit etre beau aussi.** L'app sans texture/animation/circadien
|
|
doit rester coherente et agreable. C'est le lavis sur papier lisse — sobre
|
|
mais pas pauvre.
|
|
|
|
10. **Accessible d'abord.** Chaque decision esthetique doit passer le test WCAG AA
|
|
(4.5:1 pour le texte, 3:1 pour les grands elements). Si l'esthetique et
|
|
l'accessibilite sont en conflit, l'accessibilite gagne.
|
|
|
|
11. **Pas de bordures nettes.** Jamais de `border: 1px solid`. Les bords sont
|
|
des diffusions d'encre — `box-shadow` diffus, toujours.
|
|
|
|
12. **Le vide est un choix.** L'espace negatif (ma) n'est pas du padding vide.
|
|
C'est le papier qui respire entre les traits.
|
|
|
|
---
|
|
|
|
## 13. Checklist d'implementation
|
|
|
|
| Tache | Priorite | Complexite | Impact |
|
|
|-------|----------|-----------|--------|
|
|
| Remplacer la palette indigo par cyan (#0098B5) | 1 | Faible | Fondamental |
|
|
| Ajouter la texture washi (washi.png + pseudo-element) | 1 | Faible | Texture immediate |
|
|
| Adapter les fonds (papier washi jour/nuit) | 1 | Faible | Ambiance |
|
|
| Remplacer les border par des box-shadow diffus | 1 | Moyenne | Langage visuel |
|
|
| Implementer les couches d'encre (backdrop-filter + opacites) | 1 | Moyenne | Profondeur |
|
|
| Renommer les tokens de mouvement (goutte/trait/lavis/vague/maree) | 1 | Faible | Coherence |
|
|
| Implementer les couleurs fonctionnelles pastel diffuse | 1 | Faible | Etats |
|
|
| Dessiner les 10 icones prioritaires (gestes calligraphiques) | 2 | Moyenne | Identite |
|
|
| Implementer le circadien (ThemeProvider + inversion nuit) | 2 | Moyenne | Vie |
|
|
| Refaire le player en suzuri (waveform pinceau + volume trait) | 2 | Elevee | Coeur de l'UX |
|
|
| Implementer la patine (niveau 0-2 d'abord) | 3 | Moyenne | Singularite |
|
|
| Ajouter le mode eco | 3 | Faible | Ethique |
|
|
| Implementer les couleurs artistes | 3 | Moyenne | Personnalisation |
|
|
| Patine niveaux 3-4 (encre oxydee, rouleau ancien) | 4 | Moyenne | Profondeur |
|
|
| Micro-mouvements du papier de fond (ondulation CSS) | 4 | Faible | Vie subtile |
|
|
| Variantes de texture washi pour la patine (2-3 fichiers) | 4 | Faible | Richesse |
|