4518ed8311
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>
8.0 KiB
8.0 KiB
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
- Hit targets ≥ 44 × 44 px sur TOUT élément tactile. Pas de petits boutons.
- Pas de hover — c'est du tactile. Utilise la pression au touch via
.touch-press. - Tooltips sur tous les
<IconButton>isolés (la proplabelles active automatiquement). - Toujours des polices natives mobile : Inter / JetBrains Mono / Share Tech Mono via tokens.
- Animations fluides : 180-300ms, easing
cubic-bezier(.3,.7,.3,1.2)pour entrée,cubic-bezier(.3,.6,.3,1)pour mouvement. - Toujours
<TabBar>en bas comme navigation primaire (3-5 sections). - JAMAIS de popup centrée modale standard — utilise
BottomSheet,ActionSheet,AlertDialogouToast. - Bouton Avatar en haut à droite de chaque écran principal pour accès rapide au menu utilisateur.
- Variables CSS uniquement — pas de hex en dur (
color: var(--accent), jamaiscolor: #fe8019). - Smartphone d'abord — toute interaction doit fonctionner avec un seul pouce.
🧩 Cas → Composant à utiliser
Structure d'écran (toujours)
<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
<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é
// 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
<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
<BottomSheet open={...} onClose={...} title="Choisir">
<RadioGroup value={...} onChange={...} options={[...]} />
</BottomSheet>
Menu d'actions sur élément
<ActionSheet open={...} onClose={...} title="Actions"
actions={[
{ label: 'Modifier', icon: 'cog' },
{ label: 'Partager', icon: 'download' },
{ label: 'Supprimer', icon: 'close', danger: true },
]} />
Feedback après action
<Toast open={msg !== null} onClose={() => setMsg(null)} message={msg} variant="ok" />
Menu utilisateur (avatar haut-droite)
<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→ utiliseAlertDialog - ❌
<button>nu sans hit target → utiliseIconButtonouPrimaryButton - ❌ 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/autocompleteadapté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: 18pxpour 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
durationdiffé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.htmlmontre 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.