Batterie critique
Le Modèle 42 #08 est descendu sous 20 % de charge depuis 4 heures.
Design system atomique construit de zéro pour le Groupe Bénéteau
Depuis deux ans, en tâche de fond de mes autres missions, je construis le de Seanapps Pro, la plateforme de gestion de flotte du Groupe Bénéteau. Une atomique, documentée par composant, et lisible par une IA : les écrans ne se dessinent plus, ils s'assemblent. Tout ce qui suit n'existe que pour rendre ça possible : les deux couches de , les sources verrouillées, chaque propriété nommée.
La library compte 2 069 composants répartis en 106 familles, organisées en cinq niveaux dans Figma, et elle sert à construire les 845 écrans répartis sur 34 pages du fichier produit. Tout ce qui suit prend la comme exemple, parce qu'elle traverse les trois niveaux atomiques et que c'est le composant le mieux documenté. Mais c'est 3 familles sur 106, à peu près 1 % du système. Tout le reste suit exactement les mêmes règles.
La library, page par page
Fondation
1 famille
1 517
Atoms
51 familles
280
Molecules
18 familles
92
Organisms
35 familles
173
Templates
1 famille
7
Deux ans de travail en tâche de fond, entre les autres missions. Un design system ne se livre pas, il se tient dans la durée : chaque écran de production ramène une question que le système doit apprendre à trancher. Les pastilles de statut ne sont pas décoratives, elles disent ce qui est validé, ce qui reste à reprendre, et c'est visible par toute l'équipe directement dans le nom des pages.
Deux couches de tokens, trois niveaux de composants, et des sources verrouillées. Les fondations décident de tout ce qui vient après.
La règle du système : un composant ne référence jamais une valeur brute. Il passe toujours par un . Le nom dit l'intention, pas la valeur.
Couche 1 : primitives
Couche 2 : tokens sémantiques
Typographie : Poppins, quatre tailles
Formes et profondeur
Ci-dessus, les 13 tokens que la modale consomme réellement, sur les 24 de la collection sémantique et les 80 variables du système. Le jour où le gris de second niveau change, il change à un seul endroit et tous les écrans suivent. C'est tout l'intérêt de la deuxième couche.
Un ne connaît pas la modale, et la modale ne redessine aucun atome. C'est cette indépendance qui permet de changer le bouton une seule fois et de le voir changer partout.
Survolez une branche pour l'allumer dans la modale, ou l'inverse.
Les composants sources portent un cadenas et ne sont pas exposés. La library publie un par-dessus.
📖 SeanappsPro / Library
├── 🔒 CtaButtons-Master // verrouillé, jamais exposé
├── 🔒 IconsMaster // verrouillé, jamais exposé
│
├── CtaButtons // publié : Primary · Destructive · Tertiary
├── IconsVariants // publié
├── Dialog/Basic // publié : 10 propriétés
└── Dialog/FullScreen // publié : 9 propriétésPersonne ne casse la source
Un designer ne peut ni ni modifier le par accident. L' qu'il pose est toujours conforme.
Je peux faire évoluer l'intérieur
Le wrapper garde son contrat de propriétés. Je change la mécanique dessous sans casser une seule instance posée dans un écran.
Le système reste lisible
Dans le panneau des composants, on ne voit que ce qu'on a le droit d'utiliser. Le reste, c'est de la plomberie.
La question s'est posée sérieusement : fusionner les deux modales en un seul set avec un variant Type. J'ai tranché contre, pour trois raisons.
Figma n'a pas de propriétés conditionnelles
Toutes les propriétés d'un set s'affichent sur tous ses variants. Fusionner les deux modales réintroduirait des propriétés inertes, affichées mais sans effet.
La distinction est sémantique, pas dimensionnelle
Le scroll, la fermeture au scrim, l'empilement et la forme du titre diffèrent. Ce ne sont pas deux tailles du même composant, ce sont deux composants.
Fusionner deux sets publiés est cassant
Toutes les instances posées dans les fichiers produit seraient à relier à la main. Le coût de la fusion retombe sur les autres.
Une décision de design system se documente avec ses raisons, sinon elle sera reprise dans six mois par quelqu'un qui refera le même raisonnement.
Un composant non documenté est un composant mal utilisé. La modale a sa propre section : à quoi elle sert, comment elle est faite, comment elle se comporte, et comment on écrit dedans.
Une modale bloque toute l'interface tant qu'elle est ouverte. À n'utiliser que quand l'interruption est justifiée.
Dialog/Basic
Faire confirmer, valider ou abandonner une action.
Mobile 288 · Desktop 448 · Ne scrolle pas
Dialog/FullScreen
Faire saisir plusieurs informations, dérouler une tâche, ou régler des préférences.
Mobile plein écran 360 × 744 · Desktop 640 centré · Body scrollable
Si l'utilisateur peut continuer à travailler sans y répondre, ce n'est pas une modale.
Combien d'informations distinctes l'utilisateur doit-il fournir ? Zéro ou une donne un Basic, plusieurs donnent un FullScreen. Le critère n'est pas le nombre de champs : « Modifier l'adresse email » demande deux champs (la nouvelle adresse et sa confirmation) mais une seule information, c'est un Basic. « Ajouter un bateau » demande le point de vente, le modèle et le numéro de série, trois informations sans rapport, c'est un FullScreen. Autrement dit : une action donne un Basic, une tâche donne un FullScreen.
Les deux composants partagent la même structure et le même jeu de propriétés. C'est ce qui rend le système apprenable : on comprend l'un, on sait utiliser l'autre.
Header
Titre, description et croix de fermeture. Fixe : ne scrolle jamais.
Body
Le contenu. Seule zone scrollable, et uniquement sur Dialog/FullScreen.
Footer
Les actions, alignées à droite. Action 1 est toujours la plus à droite. Peut être masqué (Show footer) quand il n'y a rien à valider.
Propriétés de Dialog/Basic
Le titre. Une ligne, deux au maximum, au-delà il est tronqué.
Le contexte et la conséquence de l'action. Peut rester vide si le titre se suffit.
Pictogramme de statut à gauche du titre (alerte, erreur). Vide par défaut.
Croix en haut à droite. Activée par défaut sur toutes les modales. Ne la retirer que lorsque l'utilisateur doit impérativement trancher, et jamais sur une modale sans Footer, où elle est la seule sortie.
Affiche la zone de contenu entre le texte et les actions.
Zone libre entre le texte et les actions : un bandeau de notification, un à deux champs pour une même information, ou des lignes de réglage. Ne scrolle pas.
Bouton principal, le plus à droite. Le résultat de l'action, ou l'étape suivante si la modale fait partie d'un parcours. CtaButtons / Primary par défaut, CtaButtons / Destructive pour une action irréversible.
Bouton secondaire. Selon le cas : abandon (Annuler), action alternative qui ne ferme pas la modale, retour en arrière, ou report. CtaButtons / Tertiary par défaut.
Affiche la zone des actions. À désactiver quand les réglages s'appliquent instantanément : la modale n'a alors plus de bouton et se ferme par la croix.
Mobile (288, boutons S) ou Desktop (448, boutons M).
Propriétés de Dialog/FullScreen
Le nom de la tâche en cours, pas une question.
Une phrase de cadrage sous le titre. Optionnelle.
Le contenu de la tâche : formulaire, liste, étapes. C'est la seule zone qui scrolle.
Barre de scroll du Body. À activer dès que le contenu dépasse la hauteur disponible.
Croix en haut à droite. Activée par défaut sur toutes les modales. Ne la retirer que lorsque l'utilisateur doit impérativement trancher, et jamais sur une modale sans Footer, où elle est la seule sortie.
Bouton principal, le plus à droite. Le résultat de l'action, ou l'étape suivante si la modale fait partie d'un parcours.
Bouton secondaire. Abandon, action alternative, retour en arrière ou report. CtaButtons / Tertiary par défaut.
Affiche la zone des actions. À désactiver quand les réglages s'appliquent instantanément.
Mobile (plein écran 360 × 744, boutons S) ou Desktop (640 centré, boutons M).
Un slot accueille n'importe quel composant de la library, sans détacher l'instance. Y glisser un composant existant plutôt que de dessiner dedans : sinon la modale ne suivra pas les évolutions de la library.
Un seul pictogramme, jamais un groupe. Réservé aux modales d'alerte ou d'erreur : une confirmation neutre n'en a pas besoin.
Un bandeau, un à deux champs quand la modale demande une seule information, ou des lignes de réglage. Au-delà de plusieurs informations distinctes, ce n'est plus un Basic.
Champs de formulaire, listes, étapes. Prévoir le comportement au scroll : le Header et le Footer restent fixes.
Un détail dont je suis assez content : les textes par défaut ne sont pas du faux texte, ce sont les règles elles-mêmes. Le designer lit la consigne au moment exact où il remplit le champ, sans avoir à ouvrir la documentation.
Dialog/Basic
Titre de la modale
Ce qui va se passer, et pourquoi ça compte. Une à trois lignes.
Dialog/FullScreen
Nom de la tâche
Une phrase de sous le titre. Optionnelle.
Une modale ne se ferme jamais d'elle-même, et jamais après un délai. Il faut une action explicite.
Croix
Dialog/Basic
Ferme sans rien appliquer
Dialog/FullScreen
Ouvre une modale/Basic de confirmation si des saisies sont en cours
Touche Échap
Dialog/Basic
Ferme
Dialog/FullScreen
Même comportement que la croix
Clic sur le scrim
Dialog/Basic
Ferme
Dialog/FullScreen
Ne ferme pas : trop de risque de perte de saisie
Action 2
Dialog/Basic
Ferme sans appliquer
Dialog/FullScreen
Même comportement que la croix
Action 1
Dialog/Basic
Applique puis ferme
Dialog/FullScreen
Enregistre puis ferme
Le titre dit ce qui va se passer. La description dit pourquoi ça compte. Les boutons disent ce que l'on fait. Si les trois disent la même chose, il y en a deux de trop.
Modifier l'adresse email
Un nom de tâche : la forme par défaut
Supprimer ce bateau de l'inventaire ?
Interrogatif, réservé au destructif
Abonnement Seanapps expiré
Affirmatif : pour une alerte, c'est un constat
Êtes-vous sûr ?
Sûr de quoi ? Oblige à lire la description
Attention !
Alarmiste et vide de contenu
Édition
Un nom d'écran, pas une tâche
Le bateau Modèle 42 #08 et son historique de trajets seront définitivement supprimés.
Dit exactement ce qui disparaît
L'invitation sera envoyée à adeline.durand@dealer.fr. Elle expire dans 7 jours.
Donne les deux informations que l'utilisateur n'a pas à l'écran
Cette action est irréversible. Voulez-vous continuer ?
Répète le titre et repose la question déjà posée par les boutons
Vous êtes sur le point de supprimer cet élément.
« Cet élément » : l'utilisateur doit deviner lequel
Action 1 dit ce qui se passe quand on clique. Un à trois mots, pas de point final, majuscule sur le premier mot uniquement.
Ne dit pas ce qui va se passer. Écrire plutôt : Le verbe de l'action : Supprimer, Envoyer, Enregistrer.
Oblige à remonter lire le titre pour savoir à quoi on répond. Écrire plutôt : Deux verbes explicites.
Ambigu : est-ce enregistré, ou abandonné ?. Écrire plutôt : Enregistrer.
Jargon de formulaire. Écrire plutôt : Envoyer, Créer, Enregistrer.
Acceptable quand le bouton n'a réellement aucun effet, ou quand la modale n'a pas de Footer et se ferme par la croix. Écrire plutôt : Annuler.
C'est le raccourci le plus courant, et il coûte cher : on met « Annuler » partout alors que le bouton secondaire prend quatre formes différentes, chacune avec son cas réel dans le produit.
Modifier l'adresse email
Action 1
Continuer
Action 2
Annuler (abandon)
Transférer la propriété
Action 1
Valider
Action 2
Nouveau client (alternative)
Ajouter un bateau
Action 1
Confirmer
Action 2
Saisir un autre numéro (retour)
Réglage d'une alerte
Action 1
Enregistrer
Action 2
Annuler (abandon)
Nouvelle commande enregistrée
Action 1
aucune
Action 2
Show footer off
La doc se termine par des cas réels du produit, chacun choisi pour ce qu'il enseigne. Les exemples sont des instances vivantes des composants : quand la library change, la doc change avec elle.
Deux champs, une seule information, donc un Basic. Action 1 vaut « Continuer » parce qu'une confirmation suit.
Une recherche volumineuse reste une seule information. Action 2 vaut « Nouveau client » : une alternative qui ne ferme pas la modale.
Une modale peut n'avoir aucune action. La croix devient alors la seule sortie.
Le seul cas où « Annuler » est le bon libellé : il y a un réglage non enregistré à jeter.
Trois informations distinctes, donc un formulaire, donc un FullScreen. Action 2 vaut « Saisir un autre numéro » : un retour en amont.
Une action irréversible se signale par la couleur, pas seulement par le texte.
Bon composant, wording à refaire. Le composant juste ne sauve pas un texte raté.
Toutes ces vignettes sont le même composant. Seules les propriétés changent. C'est ce qui évite de redessiner une modale à chaque nouvel écran.
showCloseButton · showFooter
icon={…}
variant="destructive"
action2={undefined}
description={undefined}
showContent
showFooter={false}
showCloseButton={false}
Des fichiers .md déposés dans l'IDE permettent à Claude d'utiliser la library pour construire des écrans directement dans Figma. Et les propriétés Figma se traduisent une à une en props React.
C'est la partie que je trouve la plus intéressante. J'ai écrit des fichiers .md qui décrivent la library : composants disponibles, propriétés, règles de choix, conventions. On les dépose dans son IDE, et Claude peut alors utiliser correctement la library pour construire un écran directement dans Figma. La documentation passe de « ce qu'un humain lit » à « ce qu'une machine applique ».
La library Figma
Composants atomiques, variants, tokens sémantiques.
Les fichiers .md
La library décrite en règles applicables, posée dans l'IDE.
Claude dans l'IDE
Choisit les bons composants avec les bonnes propriétés.
L'écran dans Figma
Assemblé d'instances conformes, pas dessiné.
Les fichiers
La spec complète des deux modales : règle de choix, propriétés, wording, décisions d'architecture et écarts connus avec la production.
# Dialog · SeanappsPro Library
Fichier Figma : 📖 SeanappsPro Library, page ✅ DialogElements (accès interne)
Refonte du 30/07/2026, révisée le même jour à partir de cinq écrans réellement en production.
## Les deux composants
| Composant | Rôle | Mobile | Desktop |
|---|---|---|---|
| `Dialog/Basic` | **Une action** : la faire confirmer, valider ou abandonner | 288, boutons S | 448, boutons M |
| `Dialog/FullScreen` | **Une tâche** : formulaire, sélection multiple, étapes | plein écran 360 × 744, boutons S | 640 centré, boutons M |
### Règle de choix
**« Combien d'informations distinctes l'utilisateur doit-il fournir ? »**
Zéro ou une → `Dialog/Basic`. Plusieurs → `Dialog/FullScreen`.
Le critère n'est **pas le nombre de champs**, ni la hauteur de la dialog, mais le nombre d'informations distinctes :
- « Modifier l'adresse email » = deux champs (la nouvelle adresse + sa confirmation) mais **une seule information** → Basic
- « Transférer la propriété » = une recherche qui prend beaucoup de place, mais **une seule information** (le nouveau propriétaire) → Basic
- « Nouvelle commande enregistrée » = **zéro information**, juste des réglages → Basic, avec `Show footer` off
- « Ajouter un bateau » = point de vente + modèle + numéro de série, **trois informations sans rapport** → FullScreen
- « Réglage d'une alerte » = plusieurs réglages composites non enregistrés au fil de l'eau → FullScreen
Autrement dit : une action → Basic, une tâche → FullScreen.
Le Basic accueille champs et réglages dans son slot `Content`, mais **il ne scrolle pas** : si ça déborde, passer sur FullScreen.
Le FullScreen n'est plein écran que sur mobile. Sur desktop c'est une modale large scrollable (640, volontairement plus large que le Basic à 448).
Boutons en taille S sur mobile et M à partir de la tablette, même règle sur les deux composants.
## Convention de structure
```
Header
├ Title row
│ ├ Icon (slot, Basic uniquement)
│ ├ Title
│ └ Close button
└ Description
Content slot · Basic : bandeau, 1-2 champs ou réglages, ne scrolle pas
Body FullScreen → Content (slot) + Scrollbar > Scrollbar handle
Footer (masquable via Show footer)
├ Action 2
└ Action 1
```
## Propriétés
Nommage : anglais, `Show <x>` pour les booléens de visibilité, `Action 1` / `Action 2` pour les instance swaps.
**Dialog/Basic** : Title, Description, Icon, Show close button, Show content, Content, Action 1, Action 2, Show footer, Device
**Dialog/FullScreen** : Title, Description, Content, Show scrollbar, Show close button, Action 1, Action 2, Show footer, Device
Les libellés de boutons ne sont pas des props du Dialog : ils vivent sur l'instance `🔒CtaButtons-Master` imbriquée (`Label#99:0`).
### Valeurs par défaut
| Composant | Title | Description | Show close button |
|---|---|---|---|
| `Dialog/Basic` | Titre de la dialog | Ce qui va se passer, et pourquoi ça compte. Une à trois lignes. | true |
| `Dialog/FullScreen` | Nom de la tâche | Une phrase de cadrage sous le titre. Optionnelle. | true |
Les défauts texte sont rédigés comme des consignes : le designer lit la règle en remplissant le champ.
### Action 1 et Action 2
`Action 1` = ce qui se passe quand on clique. Le plus souvent le résultat (« Supprimer », « Enregistrer », « Confirmer »), mais **« Continuer » quand la dialog n'est qu'une étape d'un parcours**. Toujours la plus à droite.
**Action irréversible → variant `Destructive` de CtaButtons.** Le rouge signale la conséquence, le libellé seul ne suffit pas.
`Action 2` **n'est pas un bouton d'annulation.** C'est le bouton secondaire, sous quatre formes :
| Forme | Exemple réel |
|---|---|
| Abandon | `Annuler` : Réglage d'une alerte |
| Action alternative, ne ferme pas la dialog | `Nouveau client` : Transférer la propriété |
| Retour à l'étape précédente | `Saisir un autre numéro` : Ajouter un bateau |
| Report | `Plus tard` |
Et parfois il n'y a **aucune action** : réglages appliqués instantanément → `Show footer` off.
### Règle de la croix
Croix active **par défaut sur toutes les dialogs** : `Show close button` vaut `true` sur les deux composants depuis le 30/07/2026.
Retirée uniquement quand l'utilisateur doit impérativement trancher, et dans ce cas on retire aussi la fermeture au scrim.
Sur une dialog sans Footer, `Show close button` ne se désactive jamais : c'est la seule sortie.
Croix et Action 2 ne font pas doublon : la croix sort sans rien faire, Action 2 porte une intention nommée.
⚠️ **À surveiller** : ce changement de défaut fait apparaître une croix sur toutes les instances `Dialog/Basic` des fichiers produit qui n'avaient pas surchargé la prop. C'est voulu, mais ça mérite une relecture des écrans où une réponse est obligatoire, qui doivent repasser `Show close button` à `false`.
## Décision : deux composants, pas un seul avec un variant `Type`
Décision du 30/07/2026 : **on garde deux composants séparés.**
- Figma n'a pas de propriétés conditionnelles par variant : toutes les props d'un set s'affichent sur tous les variants. Fusionner réintroduirait des propriétés inertes.
- La distinction est sémantique, pas dimensionnelle : scroll, fermeture au scrim, empilement, forme du titre diffèrent.
- Fusionner deux sets publiés est cassant : toutes les instances des fichiers produit seraient à relier à la main.
## Ce qui a été corrigé lors de la refonte
- Sets renommés : `Dialog/BasicDialog` → `Dialog/Basic`, `Dialog` → `Dialog/FullScreen`
- Axes de variants morts supprimés (`Slot Active ? = Yes`, `New Slot active = on`)
- Propriétés renommées sans casser les overrides (IDs `#xxx` préservés par `editComponentProperty`)
- `Show scrollbar` était une propriété **morte** sur FullScreen : recâblée sur la visibilité du Scrollbar
- `Description`, `Show close button`, `Action 1`, `Action 2` ajoutées sur FullScreen
- **`Show footer` ajoutée sur les deux** pour supporter les dialogs sans actions
- **`Notification` / `Show notification` renommés `Content` / `Show content`** sur Basic : slot générique, même nom sur les deux composants
- **Valeurs par défaut passées en français**, et `Show close button` passé à `true` sur Basic
- Calques par défaut renommés, noms à espace en fin supprimés
- Descriptions réécrites en français + `documentationLinks` vers la page
## Documentation
5 frames, section `📖 Dialogs · Documentation` : **Usage**, **Anatomie et propriétés**, **Comportement**, **Wording**, **Exemples**.
Anatomie et Exemples utilisent des instances vivantes des composants.
L'ancienne doc Material en anglais est dans la section `🗄️ Archive`, à supprimer une fois la nouvelle validée.
## Règles de wording retenues
- Le titre dit ce qui va se passer, la description pourquoi ça compte, les boutons ce qu'on fait. Pas de redite.
- **Titre : un nom de tâche par défaut**. La forme interrogative est réservée aux confirmations destructives. Pas de point final, 2 lignes max, pas d'« Attention ! ».
- Description : 1 à 3 lignes, nomme l'objet concerné plutôt que « cet élément ».
- Actions : 1 à 3 mots, pas de point final. Interdits : OK, Oui/Non, Terminé, Soumettre.
- « Fermer » toléré quand le bouton n'a aucun effet, ou sur une dialog sans Footer.
- Textes traduits : jamais de phrase construite par concaténation.
## Bugs produit repérés (à remonter)
- Écran « Add a boat » : titre non traduit, « innexistant » (deux N), « Concession de reception » (accent manquant), pas de croix.
- Écran « Réglage d'une alerte » : pas de croix alors que le choix n'est pas obligatoire.
## Point à trancher
L'instance de l'exemple 5 est affichée à **448 de large** alors que le variant Desktop de `Dialog/FullScreen` est spécifié à **640**. Soit la spec passe à 448, soit l'exemple doit être remis à 640.La démonstration
Les propriétés Figma se traduisent une à une en , parce qu'elles ont été pensées pour ça dès le départ. C'est là qu'on vérifie que le système tient : s'il faut renommer ou réinventer des propriétés au moment du code, c'est que le composant Figma était mal découpé.
La modale ci-dessous est le vrai composant, avec ses vrais tokens. Elle reste en identité Seanapps même si vous passez le site en .
Dialog/Basic · Desktop · 448 px · boutons M
<Dialog.Basic
device="desktop"
title="Modifier l'adresse email"
description="L'invitation sera envoyée à adeline.durand@dealer.fr. Elle expire dans 7 jours."
showCloseButton
showContent
showFooter
action1={{ label: "Continuer", variant: "primary" }}
action2={{ label: "Annuler" }}
>
<TextField label="Nouvelle adresse" />
<TextField label="Confirmer l'adresse" />
</Dialog.Basic>Le nommage est le vrai travail
Une propriété bien nommée s'utilise sans lire la doc. Le reste du temps passé à documenter compense un nommage raté.
Documenter le « quand ne pas l'utiliser »
C'est ce que les gens cherchent réellement. Le « comment » se devine, le « quand » se décide.
Le wording est un composant
Une modale parfaite avec un bouton « OK » reste un mauvais écran. Le texte n'est pas une couche par-dessus, il fait partie du design.
Verrouiller rend modifiable
Ce n'est pas de la rigidité. C'est ce qui me permet de faire évoluer un composant sans casser les écrans qui l'utilisent.
Vous venez de voir un composant sur des dizaines, traité en entier. C'est le niveau d'exigence que j'applique au reste. Que ce soit pour poser les fondations ou pour rationaliser une library qui a dérivé, on peut en parler.
Ouvert aux missions freelance immédiatement et au CDI à partir de septembre 2026.