chore(design): ajout du package design system smartphone

Contient les tokens, composants et exemples adaptés au mobile,
à utiliser comme référence lors du développement des vues smartphone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 08:53:36 +02:00
co-authored by Claude Sonnet 4.6
parent 4c616fa5d3
commit 4518ed8311
25 changed files with 10222 additions and 0 deletions
@@ -0,0 +1,192 @@
# Consignes mobile — mon design system
> **Tu es un agent IA qui produit du code mobile avec ce design system.**
> Lis ce fichier ENTIER avant d'écrire la moindre ligne. Suis les règles à la lettre.
---
## 🎯 Identité
- **Cibles** : iOS et Android via HTML/JS (Cordova, Capacitor, ou PWA)
- **Largeur ref** : 390px (iPhone 14 / Galaxy S22)
- **Hit target min** : 44 × 44px (Apple HIG / Material)
- **Style** : Gruvbox seventies — orange brûlé, fond brun délavé en sombre / gris clair usé en clair
---
## 📁 Fichiers
| Fichier | Composants exposés sur window |
|-------------------------------|----------------------------------------------------------------------------|
| `components/ui-kit.jsx` | Icon, Tooltip, Button, IconButton, Toggle, StatusLed, Popup, BatteryGauge, RadialGauge, BigRadialGauge, TreeNav, Sparkline, LineChart |
| `components/mobile-kit.jsx` | StatusBar, NavBar, TabBar, ListRow, ListSection, ActionCard, PrimaryButton, SegmentedControl, SearchBar |
| `components/mobile-sheets.jsx`| BottomSheet, ActionSheet, AlertDialog, Toast, FAB, PullToRefresh |
| `components/mobile-gestures.jsx`| useGesture, GestureZone, GESTURE_CATALOG |
| `components/mobile-swipeable.jsx`| SwipeableRow |
| `components/mobile-forms.jsx` | FormField, TextInput, DateInput, Dropdown, CheckboxItem, RadioGroup, MediaInsert, AvatarLogo, BiometricButton |
| `components/mobile-apps.jsx` | Avatar, AvatarMenu, OnboardingSlider, ChatBubble, ChatComposer, CalendarMonth, MapView, FilterChips, QrScannerView, CameraView, FileExplorer |
---
## ⚠️ Règles absolues
1. **Hit targets ≥ 44 × 44 px** sur TOUT élément tactile. Pas de petits boutons.
2. **Pas de hover** — c'est du tactile. Utilise la pression au touch via `.touch-press`.
3. **Tooltips** sur tous les `<IconButton>` isolés (la prop `label` les active automatiquement).
4. **Toujours des polices natives mobile** : Inter / JetBrains Mono / Share Tech Mono via tokens.
5. **Animations fluides** : 180-300ms, easing `cubic-bezier(.3,.7,.3,1.2)` pour entrée, `cubic-bezier(.3,.6,.3,1)` pour mouvement.
6. **Toujours `<TabBar>` en bas** comme navigation primaire (3-5 sections).
7. **JAMAIS de popup centrée modale standard** — utilise `BottomSheet`, `ActionSheet`, `AlertDialog` ou `Toast`.
8. **Bouton Avatar en haut à droite** de chaque écran principal pour accès rapide au menu utilisateur.
9. **Variables CSS uniquement** — pas de hex en dur (`color: var(--accent)`, jamais `color: #fe8019`).
10. **Smartphone d'abord** — toute interaction doit fonctionner avec un seul pouce.
---
## 🧩 Cas → Composant à utiliser
### Structure d'écran (toujours)
```jsx
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<StatusBar />
<NavBar large title="Mon écran" right={<Avatar name="M" onClick={openMenu} />} />
<div style={{ flex: 1, overflowY: 'auto', padding: 14 }}>
{/* contenu */}
</div>
</div>
```
Si la page fait partie d'une nav principale, ajoute `<TabBar>` après. Si elle a une action principale flottante, ajoute `<FAB>`.
### Liste à actions cachées
```jsx
<SwipeableRow
onTap={() => open(item)}
leftActions={[{ label: 'Suppr.', icon: 'close', color: 'var(--err)', onClick: del }]} // ← swipe gauche
rightActions={[{ label: 'Lu', icon: 'play', color: 'var(--info)', onClick: read }]} // ← swipe droit
>
<div>{contenu de la ligne}</div>
</SwipeableRow>
```
### Champ texte typé
```jsx
// Email
<TextInput value={...} onChange={...} keyboard="email" autocomplete="email" autocapitalize="off" />
// Mot de passe
<TextInput value={...} onChange={...} type="password" autocomplete="current-password" />
// OTP SMS
<TextInput value={...} onChange={...} keyboard="numeric" autocomplete="one-time-code" maxLength={6} />
// Recherche
<TextInput value={...} onChange={...} keyboard="search" enterHint="search" />
```
### Confirmation destructive
```jsx
<AlertDialog open={open} onClose={...}
icon="alert" iconColor="var(--err)"
title="Supprimer ?"
message="Cette action est irréversible."
actions={[
{ label: 'Annuler' },
{ label: 'Supprimer', danger: true, primary: true, onClick: del },
]} />
```
### Choix dans une liste
```jsx
<BottomSheet open={...} onClose={...} title="Choisir">
<RadioGroup value={...} onChange={...} options={[...]} />
</BottomSheet>
```
### Menu d'actions sur élément
```jsx
<ActionSheet open={...} onClose={...} title="Actions"
actions={[
{ label: 'Modifier', icon: 'cog' },
{ label: 'Partager', icon: 'download' },
{ label: 'Supprimer', icon: 'close', danger: true },
]} />
```
### Feedback après action
```jsx
<Toast open={msg !== null} onClose={() => setMsg(null)} message={msg} variant="ok" />
```
### Menu utilisateur (avatar haut-droite)
```jsx
<AvatarMenu open={...} onClose={...} name="Marc" email="marc@..."
items={[
{ icon: 'user', label: 'Mon profil' },
{ icon: 'cog', label: 'Paramètres' },
{ icon: 'power', label: 'Se déconnecter', danger: true },
]} />
```
---
## 🚫 Anti-patterns
- ❌ `window.alert / confirm` → utilise `AlertDialog`
- ❌ `<button>` nu sans hit target → utilise `IconButton` ou `PrimaryButton`
- ❌ Hover effects → pas d'hover sur mobile, utilise `.touch-press`
- ❌ Popups centrées pour formulaire court → `BottomSheet`
- ❌ Sidebars > 240px → utilise plutôt drawer ou TabBar
- ❌ Tableaux denses → liste swipeable avec actions
- ❌ Couleurs en dur → toujours `var(--token)`
- ❌ Police arbitraire → `var(--font-ui)`, `var(--font-mono)`, `var(--font-terminal)`
- ❌ Boutons texte sans icône pour actions critiques → ajoute toujours une icône
- ❌ Inputs sans `keyboard`/`autocomplete` adaptés → frustre l'utilisateur
---
## 📐 Tailles
| Élément | Taille |
|----------------------|--------------------------------------------------|
| StatusBar | 44px |
| NavBar compact | 52px |
| NavBar large | ~90px |
| TabBar | 70px (avec safe area) |
| ListRow | min 52px |
| PrimaryButton lg | 52px |
| IconButton | 34px (def) / 26px (compact) / 44px (large) |
| FAB | 56 × 56 ronde |
| Toggle | 42 × 22 |
| Radius cartes | 10-14px |
| Radius boutons | 8-12px |
| Avatar | 36px (header) / 48-72px (profile) |
| Espacement standard | 8 / 12 / 14 / 18 / 24px |
---
## 💡 Détails à respecter
- **Safe area bottom** : la TabBar a déjà un `padding-bottom: 18px` pour la home indicator iOS.
- **Backdrop-filter blur** : utilisé sur NavBar/TabBar/AlertBg pour effet vitre.
- **SwipeableRow** snap : ouvre/ferme à 50% de la largeur des actions.
- **AvatarMenu** : un seul ouvert à la fois, ferme au clic extérieur (backdrop).
- **Toast** : auto-ferme à 2.5s sauf prop `duration` différent.
- **PullToRefresh** : seulement quand `scrollTop === 0`.
---
## 🌗 Dark / Light
Tout fonctionne automatiquement via `data-theme="dark|light"` sur un parent. **Toujours tester les deux** :
- En sombre : tokens chauds bruns
- En clair : gris clair usé (pas blanc pur), accent orange plus contrasté
---
## 🔚 En cas de doute
- Composant pas sûr ? → `examples/exemple-mobile-apps.html` montre quasi tous les cas
- Geste pas clair ? → onglet Gestes du smartphone dans `exemple-mobile.html`
- Saisie spéciale ? → `exemple-mobile-saisie.html` + section "Antisèche"
Toujours préférer un composant existant à un custom. Quand tu doutes, **demande**.