@zevra/ui 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/DECISIONS.md +264 -208
  2. package/README.md +21 -21
  3. package/demo/app.html +6 -6
  4. package/demo/index.html +3 -3
  5. package/dist/actions.d.ts +8 -8
  6. package/dist/actions.d.ts.map +1 -1
  7. package/dist/actions.js +1 -1
  8. package/dist/actions.js.map +1 -1
  9. package/dist/catalogue.d.ts +2 -2
  10. package/dist/catalogue.js +67 -67
  11. package/dist/catalogue.js.map +1 -1
  12. package/dist/content.d.ts +23 -23
  13. package/dist/content.d.ts.map +1 -1
  14. package/dist/content.js +16 -16
  15. package/dist/content.js.map +1 -1
  16. package/dist/css-inline.d.ts +1 -1
  17. package/dist/css-inline.d.ts.map +1 -1
  18. package/dist/css-inline.js +4 -4
  19. package/dist/css-inline.js.map +1 -1
  20. package/dist/data-table.d.ts +1 -1
  21. package/dist/data.d.ts +7 -7
  22. package/dist/data.js +5 -5
  23. package/dist/email-tokens.js +1 -1
  24. package/dist/email.d.ts +6 -6
  25. package/dist/email.js +8 -8
  26. package/dist/email.js.map +1 -1
  27. package/dist/feedback.d.ts +10 -10
  28. package/dist/feedback.d.ts.map +1 -1
  29. package/dist/feedback.js +3 -3
  30. package/dist/forms.d.ts +12 -12
  31. package/dist/forms.js +5 -5
  32. package/dist/glyph-data.d.ts +2 -2
  33. package/dist/glyph-data.js +3 -3
  34. package/dist/glyph-urls.js +1 -1
  35. package/dist/glyphes.d.ts +2 -2
  36. package/dist/glyphes.js +4 -4
  37. package/dist/icon-data.d.ts +1 -1
  38. package/dist/icon-data.js +2 -2
  39. package/dist/icon.d.ts +5 -5
  40. package/dist/icon.js +1 -1
  41. package/dist/index.js +4 -4
  42. package/dist/layout.d.ts +21 -21
  43. package/dist/layout.d.ts.map +1 -1
  44. package/dist/layout.js +15 -15
  45. package/dist/layout.js.map +1 -1
  46. package/dist/navigation.d.ts +16 -16
  47. package/dist/navigation.d.ts.map +1 -1
  48. package/dist/navigation.js +9 -9
  49. package/dist/navigation.js.map +1 -1
  50. package/dist/overlays.d.ts +6 -6
  51. package/dist/overlays.d.ts.map +1 -1
  52. package/dist/overlays.js +5 -5
  53. package/dist/select-menu.d.ts +1 -1
  54. package/dist/select-menu.js +2 -2
  55. package/dist/surfaces.d.ts +8 -8
  56. package/dist/surfaces.d.ts.map +1 -1
  57. package/dist/surfaces.js +6 -6
  58. package/dist/surfaces.js.map +1 -1
  59. package/dist/typography.d.ts +4 -4
  60. package/dist/typography.d.ts.map +1 -1
  61. package/dist/typography.js +3 -3
  62. package/dist/typography.js.map +1 -1
  63. package/icons/index.json +1 -1
  64. package/package.json +2 -2
  65. package/react/actions.tsx +9 -9
  66. package/react/catalogue.tsx +68 -68
  67. package/react/content.tsx +28 -28
  68. package/react/css-inline.ts +4 -4
  69. package/react/data-table.tsx +3 -3
  70. package/react/data.tsx +9 -9
  71. package/react/email-tokens.ts +1 -1
  72. package/react/email.ts +10 -10
  73. package/react/feedback.tsx +12 -12
  74. package/react/forms.tsx +15 -15
  75. package/react/glyph-data.ts +3 -3
  76. package/react/glyph-urls.ts +1 -1
  77. package/react/glyphes.ts +4 -4
  78. package/react/icon-data.ts +2 -2
  79. package/react/icon.tsx +6 -6
  80. package/react/index.ts +4 -4
  81. package/react/layout.tsx +22 -22
  82. package/react/navigation.tsx +18 -18
  83. package/react/overlays.tsx +9 -9
  84. package/react/select-menu.tsx +3 -3
  85. package/react/surfaces.tsx +10 -10
  86. package/react/typography.tsx +6 -6
  87. package/src/base.css +10 -10
  88. package/src/components.css +49 -49
  89. package/src/data.css +41 -41
  90. package/src/feedback.css +55 -55
  91. package/src/fonts.css +2 -2
  92. package/src/forms.css +50 -50
  93. package/src/icons.css +10 -10
  94. package/src/index.css +1 -1
  95. package/src/layout.css +27 -27
  96. package/src/motion.css +16 -16
  97. package/src/motion.js +4 -4
  98. package/src/nav.js +5 -5
  99. package/src/navigation.css +62 -62
  100. package/src/overlays.css +14 -14
  101. package/src/rail.js +6 -6
  102. package/src/surfaces.css +11 -11
  103. package/src/tokens.css +14 -14
  104. package/tokens/index.js +8 -8
@@ -2,13 +2,13 @@
2
2
  //
3
3
  // ⚠️ CE FICHIER N'EST PAS DANS LE BARREL, et c'est délibéré :
4
4
  // `@tanstack/react-table` est une dépendance OPTIONNELLE. L'importer depuis
5
- // `@zevra/ui/react` casserait toutes les apps qui ne l'ont pas installée —
5
+ // `@zevra/ui/react` casserait toutes les apps qui ne l'ont pas installée :
6
6
  // une landing n'a aucun tableau. D'où le sous-chemin dédié :
7
7
  //
8
8
  // import { DataTable } from '@zevra/ui/react/table'
9
9
  //
10
10
  // POURQUOI TANSTACK plutôt que notre propre tri/pagination : tri multi-colonnes,
11
- // filtres, groupement, virtualisation, sélection de lignes — c'est un travail
11
+ // filtres, groupement, virtualisation, sélection de lignes : c'est un travail
12
12
  // de plusieurs mois que personne ici n'a envie de refaire, et mal. TanStack est
13
13
  // HEADLESS : il ne rend rien, il calcule des modèles de lignes. Ce fichier lui
14
14
  // donne l'apparence du paquet, exactement comme shadcn le fait avec ses
@@ -49,7 +49,7 @@ export interface DataTableProps<T> {
49
49
  * d'absence qu'on attend ici (filtre trop étroit), pas l'écran d'absence :
50
50
  * l'en-tête doit rester visible pour qu'on puisse élargir le filtre. */
51
51
  vide?: ReactNode
52
- /** Appelé au clic sur une ligne. Sans lui, la ligne n'est pas cliquable —
52
+ /** Appelé au clic sur une ligne. Sans lui, la ligne n'est pas cliquable :
53
53
  * et elle ne doit pas en avoir l'air. */
54
54
  onRowClick?: (ligne: T) => void
55
55
  className?: string
package/react/data.tsx CHANGED
@@ -1,4 +1,4 @@
1
- // Données — tableau en grille, listes, métadonnées, clé-valeur, vignettes.
1
+ // Données : tableau en grille, listes, métadonnées, clé-valeur, vignettes.
2
2
  //
3
3
  // Le tableau du système n'est PAS un <table> : c'est une grille CSS, parce
4
4
  // qu'une même rangée doit se rendre en colonnes au-dessus du point de rupture
@@ -19,7 +19,7 @@ type Span = Omit<HTMLAttributes<HTMLSpanElement>, 'className'>
19
19
 
20
20
  /** `--zv-cols` est un PARAMÈTRE de grille, pas un token de marque : il se
21
21
  * pose en style inline sur le cadre, et l'en-tête comme les lignes en
22
- * héritent. Une seule déclaration pour les deux — recopié aux deux
22
+ * héritent. Une seule déclaration pour les deux : recopié aux deux
23
23
  * endroits, on l'oublie à l'un et les intitulés se décalent des valeurs
24
24
  * sans que rien ne casse visiblement. */
25
25
  function cols(gabarit: string | undefined, style: CSSProperties | undefined) {
@@ -35,7 +35,7 @@ export interface TableProps extends Div {
35
35
  /** Tableau de PROSE : les cellules reviennent à la ligne au lieu de
36
36
  * tronquer, et les colonnes s'alignent en haut.
37
37
  *
38
- * ⚠️ À employer dès qu'une cellule porte une phrase plutôt qu'une donnée —
38
+ * ⚠️ À employer dès qu'une cellule porte une phrase plutôt qu'une donnée :
39
39
  * une clause de contrat, un fondement juridique, une garantie de
40
40
  * transfert. Tronquer une clause ne la raccourcit pas, ça la CACHE, et le
41
41
  * lecteur n'a aucun moyen de savoir qu'il manque quelque chose. Le défaut
@@ -170,7 +170,7 @@ export interface MetaProps extends Span {
170
170
  }
171
171
 
172
172
  /** Second registre mono du système, en BAS DE CASSE : taille de fichier,
173
- * date, horodatage, latence. Ni capitales ni interlettrage — c'est ce qui le
173
+ * date, horodatage, latence. Ni capitales ni interlettrage : c'est ce qui le
174
174
  * distingue de `Tag` et `Mono`, et une date en capitales espacées devient
175
175
  * illisible. */
176
176
  export function Meta({ taille = 'normal', discret, className, ...reste }: MetaProps) {
@@ -305,7 +305,7 @@ export function CiteRow({ className, ...reste }: Div & { className?: string }) {
305
305
 
306
306
  export interface FileTypeProps extends Span {
307
307
  /** Règle de choix relevée : DOCX si le type MIME contient « word »,
308
- * PDF sinon. Les couleurs ne viennent PAS de la charte — ce sont celles
308
+ * PDF sinon. Les couleurs ne viennent PAS de la charte : ce sont celles
309
309
  * que le monde associe à ces formats. */
310
310
  format: 'pdf' | 'docx'
311
311
  className?: string
@@ -325,7 +325,7 @@ export interface FoldProps
325
325
  extends Omit<DetailsHTMLAttributes<HTMLDetailsElement>, 'className' | 'open'> {
326
326
  titre: ReactNode
327
327
  /** Ouverte au premier rendu. L'état vit ensuite dans le NAVIGATEUR
328
- * (<details> natif) : pas de prop contrôlée, pas de JavaScript — le
328
+ * (<details> natif) : pas de prop contrôlée, pas de JavaScript, le
329
329
  * clavier et l'annonce viennent avec. `open` est retiré des attributs :
330
330
  * deux canaux pour le même état, et c'est le silencieux qui gagnerait. */
331
331
  ouvert?: boolean
@@ -334,7 +334,7 @@ export interface FoldProps
334
334
  }
335
335
 
336
336
  /** Le pliage de l'applicatif : options avancées, détail d'une ligne de
337
- * journal. La FAQ (contenu) reste le pliage de la VITRINE — question en
337
+ * journal. La FAQ (contenu) reste le pliage de la VITRINE : question en
338
338
  * 14px, respiration éditoriale ; celui-ci est un chrome dense. Le chevron
339
339
  * fait partie du composant, comme la flèche de LinkArrow. */
340
340
  export function Fold({ titre, ouvert, className, children, ...reste }: FoldProps) {
@@ -362,7 +362,7 @@ export function Fold({ titre, ouvert, className, children, ...reste }: FoldProps
362
362
 
363
363
  /* ─── Touche de clavier ────────────────────────────────────────────── */
364
364
 
365
- /** Une touche, une seule : « ⌘K » est DEUX touches, donc deux `Kbd` — les
365
+ /** Une touche, une seule : « ⌘K » est DEUX touches, donc deux `Kbd`, les
366
366
  * fondre dans une seule dessine une touche qui n'existe sur aucun
367
367
  * clavier. */
368
368
  export function Kbd({
@@ -384,7 +384,7 @@ export function Code({
384
384
  }
385
385
 
386
386
  /** Bloc de code. Chasse fixe RÉELLE (--font-code) : --font-mono est
387
- * proportionnelle et n'aligne pas du code. Aucune coloration syntaxique —
387
+ * proportionnelle et n'aligne pas du code. Aucune coloration syntaxique :
388
388
  * elle imposerait une palette entière que la charte n'a pas. Le bloc
389
389
  * défile en son sein : une ligne longue ne pousse jamais la page. */
390
390
  export function CodeBlock({
@@ -1,4 +1,4 @@
1
- // GÉNÉRÉ par scripts/build-email-tokens.mjs — ne pas éditer à la main.
1
+ // GÉNÉRÉ par scripts/build-email-tokens.mjs : ne pas éditer à la main.
2
2
  // Source de vérité : tokens/index.js, lui-même miroir de src/tokens.css.
3
3
 
4
4
  export const COULEURS = {
package/react/email.ts CHANGED
@@ -8,10 +8,10 @@
8
8
  // · il n'y a pas de feuille de style à charger dans un e-mail ;
9
9
  // · Outlook et Gmail SUPPRIMENT les propriétés personnalisées : un
10
10
  // `var(--accent)` s'y rend en couleur nulle, donc en texte noir sur fond
11
- // transparent. Les valeurs sont donc écrites en clair — mais tirées du
11
+ // transparent. Les valeurs sont donc écrites en clair : mais tirées du
12
12
  // miroir JS des tokens, que tests/tokens.test.js tient aligné sur le CSS ;
13
13
  // · ni flexbox ni grid ne sont fiables : la mise en page est en TABLES ;
14
- // · les dégradés ne se rendent pas dans Outlook — le bouton plein perd le
14
+ // · les dégradés ne se rendent pas dans Outlook : le bouton plein perd le
15
15
  // sien et devient un aplat, le filet de spectre devient trois aplats.
16
16
  //
17
17
  // Il vit dans react/ parce que c'est la racine de compilation du paquet
@@ -23,7 +23,7 @@
23
23
  // stylées.
24
24
 
25
25
  import { COULEURS, POLICES as PILES } from './email-tokens.js'
26
- // Les URL publiques des glyphes — sept chaînes, aucune image inlinée : un
26
+ // Les URL publiques des glyphes, sept chaînes, aucune image inlinée : un
27
27
  // client de messagerie ne résout pas une data-URI (Gmail les bloque), il lui
28
28
  // faut une adresse absolue.
29
29
  import { LOGOS_PRODUITS } from './glyph-urls.js'
@@ -34,7 +34,7 @@ export { BASE_RESSOURCES, LOGOS_PRODUITS } from './glyph-urls.js'
34
34
  * délimité par ces mêmes guillemets : l'attribut se refermerait au premier
35
35
  * guillemet interne, et TOUT le style du nœud serait perdu. En HTML, un
36
36
  * guillemet simple est équivalent à l'intérieur d'une valeur de police.
37
- * Cette faute ne se voit pas à la relecture — elle se voit au rendu, où
37
+ * Cette faute ne se voit pas à la relecture : elle se voit au rendu, où
38
38
  * l'e-mail entier repasse en Times sans style. */
39
39
  const pourAttribut = (pile: string) => pile.replace(/"/g, "'")
40
40
 
@@ -87,14 +87,14 @@ export function emailTitre(texte: string): string {
87
87
  }
88
88
 
89
89
  /** Un paragraphe. Le HTML d'enrichissement (`<strong>`, `<a>`) est admis :
90
- * c'est du texte rédigé, pas une saisie utilisateur — l'échapper
90
+ * c'est du texte rédigé, pas une saisie utilisateur, l'échapper
91
91
  * afficherait des chevrons. Passer par `echappe` toute valeur qui vient
92
92
  * d'une base. */
93
93
  export function emailTexte(html: string): string {
94
94
  return `<p style="${CORPS}margin:0 0 16px;">${html}</p>`
95
95
  }
96
96
 
97
- /** L'action. UN SEUL bouton plein par e-mail, comme par page — c'est ce qui
97
+ /** L'action. UN SEUL bouton plein par e-mail, comme par page : c'est ce qui
98
98
  * fait qu'on sait où cliquer.
99
99
  * Construit en table : le `padding` d'un `<a>` est ignoré par Outlook, qui
100
100
  * rendrait un lien nu au milieu du message. Aplat indigo et non dégradé,
@@ -107,7 +107,7 @@ export function emailBouton({ href, libelle }: { href: string; libelle: string }
107
107
  }
108
108
 
109
109
  /** Le lien secondaire, sous le bouton : l'autre chemin, pour qui ne veut pas
110
- * du premier. Souligné — dans un e-mail, la couleur seule ne suffit pas à
110
+ * du premier. Souligné : dans un e-mail, la couleur seule ne suffit pas à
111
111
  * annoncer un lien. */
112
112
  export function emailLienSecondaire({ href, libelle }: { href: string; libelle: string }): string {
113
113
  return `<p style="${CORPS}font-size:14px;margin:0 0 16px;"><a href="${echappe(href)}" style="color:${COULEURS.accent};text-decoration:underline;">${echappe(libelle)}</a></p>`
@@ -138,7 +138,7 @@ export interface EmailOptions {
138
138
  * à l'envoi, dans l'en-tête SMTP. */
139
139
  titre: string
140
140
  /** Le texte gris qui suit l'objet dans la liste des messages. Sans lui,
141
- * le client y affiche les premiers mots du HTML — souvent « Voir cet
141
+ * le client y affiche les premiers mots du HTML : souvent « Voir cet
142
142
  * e-mail dans votre navigateur », ce qui gâche la seule ligne dont on
143
143
  * dispose pour convaincre d'ouvrir. */
144
144
  preheader?: string
@@ -146,7 +146,7 @@ export interface EmailOptions {
146
146
  * un logo : une image sur deux est bloquée par défaut, et un e-mail qui
147
147
  * ne dit pas de qui il vient part à la corbeille. */
148
148
  marque: string
149
- /** L'URL ABSOLUE du logo — une image d'e-mail ne se résout pas depuis un
149
+ /** L'URL ABSOLUE du logo : une image d'e-mail ne se résout pas depuis un
150
150
  * chemin relatif, et les data-URI sont bloquées par Gmail. */
151
151
  logo?: { src: string; hauteur?: number }
152
152
  /** Raccourci : le glyphe d'un produit Zevra, à son URL publiée avec la
@@ -165,7 +165,7 @@ export interface EmailOptions {
165
165
  *
166
166
  * ⚠️ Le mode sombre est REFUSÉ explicitement (`color-scheme: light`) : la
167
167
  * charte est claire par nature, et l'inversion automatique d'Apple Mail
168
- * retourne les neutres sans toucher aux aplats — l'indigo du bouton
168
+ * retourne les neutres sans toucher aux aplats, l'indigo du bouton
169
169
  * resterait sur un fond devenu noir, et les contrastes réglés dans un sens
170
170
  * partiraient dans l'autre. C'est la même règle que pour les surfaces
171
171
  * sombres du système : elles se font avec la bande manifeste, jamais en
@@ -1,11 +1,11 @@
1
- // Retours — bandeaux d'état, étiquettes, pastilles, absence, travail en cours.
1
+ // Retours : bandeaux d'état, étiquettes, pastilles, absence, travail en cours.
2
2
  //
3
3
  // Rien ici n'est interactif : pas de fermeture, pas de survol. Aucun de ces
4
4
  // composants n'a besoin de tourner côté navigateur.
5
5
  //
6
6
  // ⚠️ Ce fichier ne livre NI roue de chargement, NI squelette, NI infobulle :
7
7
  // le CSS n'en a pas, parce que le relevé de production n'en contenait aucun.
8
- // Le travail en cours se dit par `Loading`, qui nomme ce qu'on attend — une
8
+ // Le travail en cours se dit par `Loading`, qui nomme ce qu'on attend : une
9
9
  // roue dit « attendez », cette phrase dit sur quoi.
10
10
 
11
11
  import type { HTMLAttributes, ReactNode } from 'react'
@@ -22,7 +22,7 @@ export interface AlertProps extends Div {
22
22
  /** OBLIGATOIRE : sans ton, le bandeau hérite d'un filet en `currentColor`,
23
23
  * soit un trait d'encre autour d'un fond blanc. */
24
24
  ton: AlertTon
25
- /** `sm` resserre, `lg` amplifie — ce dernier pour le message qui REMPLACE
25
+ /** `sm` resserre, `lg` amplifie : ce dernier pour le message qui REMPLACE
26
26
  * un contenu attendu (incident, réponse impossible). */
27
27
  taille?: 'sm' | 'normal' | 'lg'
28
28
  className?: string
@@ -92,8 +92,8 @@ export function Badge({
92
92
  export type DotEtat = 'recu' | 'ocr' | 'indexation' | 'indexe' | 'ok' | 'erreur'
93
93
 
94
94
  export interface DotProps extends Span {
95
- /** Sans état, la pastille reste GRISE : un statut inconnu — ajouté côté
96
- * serveur, pas encore côté interface — ne doit pas passer pour un succès. */
95
+ /** Sans état, la pastille reste GRISE : un statut inconnu (ajouté côté
96
+ * serveur, pas encore côté interface) ne doit pas passer pour un succès. */
97
97
  etat?: DotEtat
98
98
  taille?: 'sm' | 'normal' | 'lg'
99
99
  /** CARRÉE : marque une SOURCE, pas un état. Ne pas uniformiser. */
@@ -204,7 +204,7 @@ export interface ProgressProps extends Div {
204
204
  /** Entre 0 et 1. Hors bornes, la valeur est ramenée dedans : une barre à
205
205
  * 140 % déborde de son cadre. */
206
206
  valeur: number
207
- /** Ce que la barre mesure — annoncé, jamais dessiné. */
207
+ /** Ce que la barre mesure : annoncé, jamais dessiné. */
208
208
  label?: string
209
209
  className?: string
210
210
  }
@@ -234,14 +234,14 @@ export interface SpinnerProps extends Span {
234
234
  /** En tête d'un panneau d'attente : la roue seule, plus grande. */
235
235
  grand?: boolean
236
236
  /** Ce qu'on attend, annoncé au lecteur d'écran. Sans lui la roue est
237
- * DÉCORATIVE et masquée — le bon défaut quand une phrase voisine dit
237
+ * DÉCORATIVE et masquée : le bon défaut quand une phrase voisine dit
238
238
  * déjà le travail (`Loading`, ou le libellé d'un bouton `occupe`). */
239
239
  label?: string
240
240
  className?: string
241
241
  }
242
242
 
243
243
  /** Attente COURTE ET LOCALE : un bouton, une cellule, un panneau sans la
244
- * place d'une phrase. Pour l'attente d'un écran, préférer `Loading` — la
244
+ * place d'une phrase. Pour l'attente d'un écran, préférer `Loading` : la
245
245
  * phrase qui dit le travail réel reste le motif par défaut du système.
246
246
  * Le libellé est un TEXTE masqué visuellement, pas un aria-label : sur un
247
247
  * span vide, l'aria-label n'est pas annoncé par tous les lecteurs. */
@@ -270,7 +270,7 @@ export interface SkeletonProps extends Div {
270
270
  className?: string
271
271
  }
272
272
 
273
- /** Il ANNONCE une structure connue (la carte, la ligne qui va venir) — pour
273
+ /** Il ANNONCE une structure connue (la carte, la ligne qui va venir) : pour
274
274
  * un contenu dont la forme est inconnue, c'est `Loading` qui dit ce cas.
275
275
  * Le squelette est masqué aux lecteurs d'écran : c'est la zone qui charge
276
276
  * qui porte aria-busy, pas chaque os. */
@@ -288,11 +288,11 @@ export function Skeleton({ texte, largeur, hauteur, className, style, ...reste }
288
288
  /* ─── Infobulle ────────────────────────────────────────────────────── */
289
289
 
290
290
  export interface TooltipProps extends Span {
291
- /** Le complément — un raccourci, une date exacte, une unité. JAMAIS le
291
+ /** Le complément : un raccourci, une date exacte, une unité. JAMAIS le
292
292
  * nom de la commande : lui vit sur la cible (libellé ou aria-label), une
293
293
  * infobulle n'existe pas au doigt. */
294
294
  contenu: ReactNode
295
- /** Identifiant EXPLICITE, à répéter en `aria-describedby` sur la cible —
295
+ /** Identifiant EXPLICITE, à répéter en `aria-describedby` sur la cible,
296
296
  * le contrat du couple htmlFor/id de Field, pour la même raison : pas de
297
297
  * useId, le composant reste rendu au serveur. */
298
298
  id?: string
@@ -305,7 +305,7 @@ export interface TooltipProps extends Span {
305
305
  }
306
306
 
307
307
  /** Échap ferme la bulle jusqu'au blur (WCAG 1.4.13) : la classe est posée
308
- * sur le nœud sans état React — le composant reste rendu au serveur, et
308
+ * sur le nœud sans état React, le composant reste rendu au serveur, et
309
309
  * sans script la bulle reste simplement au comportement survol/focus. */
310
310
  export function Tooltip({ contenu, id, bas, className, children, ...reste }: TooltipProps) {
311
311
  return (
package/react/forms.tsx CHANGED
@@ -1,12 +1,12 @@
1
- // Saisie — champs, sélection, bascules, puces de filtre, zone de dépôt.
1
+ // Saisie : champs, sélection, bascules, puces de filtre, zone de dépôt.
2
2
  //
3
3
  // Aucun composant de ce fichier ne porte « use client » : ils sont tous sans
4
4
  // état. Ceux qui prennent un gestionnaire d'événement (Switch, Seg, Drop)
5
- // doivent être rendus DEPUIS un composant client, qui détient l'état — le
5
+ // doivent être rendus DEPUIS un composant client, qui détient l'état : le
6
6
  // paquet ne le fait pas à ta place, sinon un simple champ de recherche
7
7
  // tirerait React côté navigateur sur une page vitrine.
8
8
  //
9
- // ACCESSIBILITÉ — le liage intitulé ↔ contrôle est EXPLICITE. Field ne
9
+ // ACCESSIBILITÉ : le liage intitulé ↔ contrôle est EXPLICITE. Field ne
10
10
  // fabrique pas d'identifiant (il faudrait useId, donc un composant client) :
11
11
  // passe le même `htmlFor` à Field et `id` au contrôle. Field pose l'erreur
12
12
  // sur `${htmlFor}-err` pour que tu puisses la citer dans aria-describedby.
@@ -28,7 +28,7 @@ import { Icon } from './icon.js'
28
28
  export interface FieldProps
29
29
  extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
30
30
  label?: ReactNode
31
- /** Identifiant du contrôle enveloppé — à répéter en `id` sur le contrôle. */
31
+ /** Identifiant du contrôle enveloppé : à répéter en `id` sur le contrôle. */
32
32
  htmlFor?: string
33
33
  /** Posé à droite de l'intitulé, sur la même ligne de base
34
34
  * (« Mot de passe oublié ? »). */
@@ -89,7 +89,7 @@ export interface InputProps
89
89
  /** Ni fond ni filet : c'est le CADRE qui l'entoure qui fait le champ.
90
90
  * Lui laisser sa bordure dessinerait deux cadres imbriqués. */
91
91
  nu?: boolean
92
- /** Pose `aria-invalid` — c'est lui qui rend l'erreur audible, et le CSS le
92
+ /** Pose `aria-invalid` : c'est lui qui rend l'erreur audible, et le CSS le
93
93
  * suit autant que la classe. */
94
94
  erreur?: boolean
95
95
  className?: string
@@ -122,7 +122,7 @@ export function Textarea({ erreur, className, ...reste }: TextareaProps) {
122
122
  }
123
123
 
124
124
  /** <select> NATIF habillé comme le champ : la flèche, l'ouverture au clavier
125
- * et l'annonce viennent du navigateur. C'est le motif de choix par défaut —
125
+ * et l'annonce viennent du navigateur. C'est le motif de choix par défaut :
126
126
  * `SelectTrigger` est un menu maison, à réserver aux filtres. */
127
127
  export function Select({
128
128
  className,
@@ -223,7 +223,7 @@ export interface SwitchProps
223
223
  extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'onChange'> {
224
224
  /** État courant. Composant CONTRÔLÉ : il ne mémorise rien. */
225
225
  on: boolean
226
- /** Libellé accolé — c'est lui qui porte le sens, la piste seule ne dit pas
226
+ /** Libellé accolé : c'est lui qui porte le sens, la piste seule ne dit pas
227
227
  * ce qui est activé. */
228
228
  texte?: ReactNode
229
229
  className?: string
@@ -389,27 +389,27 @@ export function DropInput({
389
389
  export interface InputAffixProps
390
390
  extends Omit<HTMLAttributes<HTMLDivElement>, 'className'> {
391
391
  /** Préfixe, en mono label : « € », « HT ». Une unité est un libellé, pas
392
- * une donnée — elle ne se saisit pas, elle qualifie ce qui est saisi. */
392
+ * une donnée : elle ne se saisit pas, elle qualifie ce qui est saisi. */
393
393
  avant?: ReactNode
394
394
  /** Suffixe : « JOURS », « / MOIS », « .zevra.tech ». */
395
395
  apres?: ReactNode
396
396
  /** Identifiants EXPLICITES des affixes, à citer dans `aria-describedby`
397
- * sur le champ intérieur — le contrat htmlFor/id de `Field`, pour la
397
+ * sur le champ intérieur, le contrat htmlFor/id de `Field`, pour la
398
398
  * même raison : pas de useId, le composant reste rendu au serveur.
399
399
  * ⚠️ Sans ce câblage, un lecteur d'écran en mode formulaire entend
400
- * « 1200 » sans jamais apprendre qu'il s'agit d'euros HT — exactement le
400
+ * « 1200 » sans jamais apprendre qu'il s'agit d'euros HT : exactement le
401
401
  * défaut que le composant existe pour corriger. */
402
402
  idAvant?: string
403
403
  idApres?: string
404
404
  className?: string
405
- /** Le champ lui-même — un `<Input nu>` : c'est le CADRE qui fait le champ,
405
+ /** Le champ lui-même, un `<Input nu>` : c'est le CADRE qui fait le champ,
406
406
  * un `<Input>` bordé dessinerait deux cadres imbriqués. */
407
407
  children?: ReactNode
408
408
  }
409
409
 
410
410
  /** Cadre à affixe : « 30 », seul dans un champ, ne dit pas s'il s'agit de
411
411
  * jours ou d'euros. Même grammaire que `Search` (cadre + champ nu) ; à ne
412
- * pas employer quand l'unité est déjà dans l'intitulé du champ — deux fois
412
+ * pas employer quand l'unité est déjà dans l'intitulé du champ : deux fois
413
413
  * la même unité se lisent comme deux informations. */
414
414
  export function InputAffix({
415
415
  avant,
@@ -446,7 +446,7 @@ export interface RangeProps
446
446
 
447
447
  /** Curseur natif habillé (`<input type="range">`) : clavier, annonce et
448
448
  * bornes viennent du navigateur. Le remplissage de la piste est calculé
449
- * ici depuis value/min/max — une géométrie, comme la largeur de
449
+ * ici depuis value/min/max : une géométrie, comme la largeur de
450
450
  * `Progress`, pas une valeur de charte. En NON contrôlé, il reflète la
451
451
  * valeur initiale et ne suit plus le doigt : contrôler le champ dès que le
452
452
  * remplissage doit suivre.
@@ -493,8 +493,8 @@ export interface InputPasswordProps
493
493
  * l'appelant, qui est déjà un composant client puisqu'il tient la valeur du
494
494
  * champ. Le paquet ne tire pas React côté navigateur pour un champ.
495
495
  *
496
- * Le libellé du bouton change avec l'état — une icône seule ne dit pas ce qui
497
- * va se passer — et `aria-pressed` l'annonce aux lecteurs d'écran.
496
+ * Le libellé du bouton change avec l'état, une icône seule ne dit pas ce qui
497
+ * va se passer, et `aria-pressed` l'annonce aux lecteurs d'écran.
498
498
  */
499
499
  export function InputPassword({
500
500
  visible,
@@ -1,4 +1,4 @@
1
- // GÉNÉRÉ par scripts/build-glyph-data.mjs — ne pas éditer à la main.
1
+ // GÉNÉRÉ par scripts/build-glyph-data.mjs : ne pas éditer à la main.
2
2
  // Source : logos/glyphes/*.png, réduits par scripts/build-glyphes.py.
3
3
 
4
4
  export type GlypheId =
@@ -21,7 +21,7 @@ export const GLYPHES: Record<GlypheId, string> = {
21
21
  'zevra': 'data:image/png;base64,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',
22
22
  }
23
23
 
24
- /** Le mot « Zevra » — le wordmark officiel, tracé, en data-URI. */
24
+ /** Le mot « Zevra » : le wordmark officiel, tracé, en data-URI. */
25
25
  export const MOT_ZEVRA = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NTMuOCAxMDAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iWmV2cmEiPgogIDx0aXRsZT5aZXZyYTwvdGl0bGU+CiAgPCEtLSBMZSBtb3QgWmV2cmEsIHJlbGV2w6kgc3VyIGxlIGxvY2t1cCBkJ29yaWdpbmUuIExhIGhhdXRldXIgZGUgY2FwaXRhbGUKICAgICAgIHZhdXQgMTAwIHVuaXTDqXMgOiB1bmUgaGF1dGV1ciBDU1MgcG9zw6llIHN1ciBjZSBmaWNoaWVyIEVTVCBsYSBjYXBpdGFsZSwKICAgICAgIGNlIHF1aSByZW5kIGxlcyBwcm9wb3J0aW9ucyBkdSBsb2NrdXAgY2FsY3VsYWJsZXMgKHZvaXIgLnp2LWxvY2t1cCkuIC0tPgogIDxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNIDQxMy4zMSAwLjE4IEwgNDEzLjg1IDAuMTggTCA0MTQuMjEgMC4wMCBMIDQxNC43NCAwLjAwIEwgNDE1LjI4IDAuMDAgTCA0MTUuODEgMC4wMCBMIDQxNi4zNSAwLjAwIEwgNDE2Ljg4IDAuMDAgTCA0MTcuNDIgMC4wMCBMIDQxNy45NiAwLjAwIEwgNDE4LjQ5IDAuMDAgTCA0MTkuMDMgMC4wMCBMIDQxOS41NiAwLjAwIEwgNDIwLjEwIDAuMDAgTCA0MjAuNjMgMC4wMCBMIDQyMC45OSAwLjE4IEwgNDIxLjM1IDAuMzYgTCA0MjEuNzEgMC4zNiBMIDQ1My44MCA4MC4yOCBMIDQ0MS44MyA4NS4wNiBMIDQzMi41NyA2MS43NyBMIDQwMi40OSA2MS43NyBMIDM5My4xOSA4NS4wMiBMIDM4MS4yMiA4MC4zMCBaIE0gMC4xOCA4MC44OCBMIDQ4Ljg2IDEzLjIxIEwgNy4zMiAxMi44NSBMIDYuOTYgMTIuMzIgTCA2Ljk2IDExLjQzIEwgNi45NiAxMC41MyBMIDYuOTYgOS42NCBMIDYuOTYgOC43NSBMIDYuOTYgNy44NiBMIDYuOTYgNi45NiBMIDYuOTYgNi4wNyBMIDYuOTYgNS4xOCBMIDYuOTYgNC4yOCBMIDYuOTYgMy4zOSBMIDYuOTYgMi41MCBMIDYuOTYgMS42MSBMIDcuMTQgMC44OSBMIDcuMzIgMC41NCBMIDc0LjA4IDAuMTggTCAxOS41MCA3Ni4wNiBMIDYxLjA2IDc2LjU5IEwgNjEuMjQgNzcuMzEgTCA2MS40MiA3OC4wMiBMIDYxLjQyIDc4LjkxIEwgNjEuNDIgNzkuODEgTCA2MS40MiA4MC43MCBMIDYxLjQyIDgxLjU5IEwgNjEuNDIgODIuNDggTCA2MS40MiA4My4zOCBMIDYxLjQyIDg0LjI3IEwgNjEuNDIgODUuMTYgTCA2MS40MiA4Ni4wNSBMIDYxLjQyIDg2Ljk1IEwgNjEuNDIgODcuODQgTCA2MS4wNiA4OC4zOCBMIDYxLjA2IDg4LjczIEwgMC4zNiA4OC43MyBMIDAuMTggODguMzggTCAwLjAwIDg4LjAyIEwgMC4wMCA4Ny40OCBMIDAuMDAgODYuOTUgTCAwLjAwIDg2LjQxIEwgMC4wMCA4NS44OCBMIDAuMDAgODUuMzQgTCAwLjAwIDg0LjgxIEwgMC4wMCA4NC4yNyBMIDAuMDAgODMuNzMgTCAwLjAwIDgzLjIwIEwgMC4wMCA4Mi42NiBMIDAuMDAgODIuMTMgTCAwLjAwIDgxLjU5IEwgMC4xOCA4MS4yMyBaIE0gOTYuMjMgMC41NCBMIDE1MC41MSAwLjcxIEwgMTUwLjY5IDEuNDMgTCAxNTAuODYgMi4xNCBMIDE1MC44NiAzLjA0IEwgMTUwLjg2IDMuOTMgTCAxNTAuODYgNC44MiBMIDE1MC44NiA1LjcxIEwgMTUwLjg2IDYuNjEgTCAxNTAuODYgNy41MCBMIDE1MC44NiA4LjM5IEwgMTUwLjg2IDkuMjggTCAxNTAuODYgMTAuMTggTCAxNTAuODYgMTEuMDcgTCAxNTAuODYgMTEuOTYgTCAxNTAuNTEgMTIuNTAgTCAxNTAuNTEgMTIuNjggTCAxMDkuMDkgMTMuMzkgTCAxMDguNzMgMTQuODIgTCAxMDguNzMgMTYuMjUgTCAxMDguNzMgMTguMDMgTCAxMDguNzMgMTkuODIgTCAxMDguNzMgMjEuNjAgTCAxMDguNzMgMjMuMzkgTCAxMDguNzMgMjUuMTcgTCAxMDguNzMgMjYuOTYgTCAxMDguNzMgMjguNzQgTCAxMDguNzMgMzAuNTMgTCAxMDguNzMgMzIuMzIgTCAxMDguNzMgMzQuMTAgTCAxMDguNzMgMzUuODkgTCAxMDguOTEgMzcuNDkgTCAxMDkuMDkgMzguMjEgTCAxNDMuMjIgMzguMzkgTCAxNDMuMTEgNTEuMjQgTCAxMDkuMDkgNTEuNDIgTCAxMDguNzMgNTIuODUgTCAxMDguNzMgNTQuNjMgTCAxMDguNzMgNTYuNDIgTCAxMDguNzMgNTguMjAgTCAxMDguNzMgNTkuOTkgTCAxMDguNzMgNjEuNzcgTCAxMDguNzMgNjMuNTYgTCAxMDguNzMgNjUuMzQgTCAxMDguNzMgNjcuMTMgTCAxMDguNzMgNjguOTIgTCAxMDguNzMgNzAuNzAgTCAxMDguNzMgNzIuNDkgTCAxMDguNzMgNzQuMjcgTCAxMDkuMDkgNzUuNzAgTCAxMDkuMDkgNzUuODggTCAxNTAuMTUgNzYuNTkgTCAxNTAuNTEgNzcuMTMgTCAxNTAuODYgNzcuNjYgTCAxNTAuODYgNzguNTYgTCAxNTAuODYgNzkuNDUgTCAxNTAuODYgODAuMzQgTCAxNTAuODYgODEuMjMgTCAxNTAuODYgODIuMTMgTCAxNTAuODYgODMuMDIgTCAxNTAuODYgODMuOTEgTCAxNTAuODYgODQuODEgTCAxNTAuODYgODUuNzAgTCAxNTAuODYgODYuNTkgTCAxNTAuODYgODcuNDggTCAxNTAuNjkgODguMjAgTCAxNTAuNjkgODguMzggTCA5Ni40MSA4OC45MSBMIDk1Ljg3IDgzLjU2IEwgOTUuODcgNzcuMzEgTCA5NS44NyA3MS4wNiBMIDk1Ljg3IDY0LjgxIEwgOTUuODcgNTguNTYgTCA5NS44NyA1Mi4zMSBMIDk1Ljg3IDQ2LjA2IEwgOTUuODcgMzkuODEgTCA5NS44NyAzMy41NyBMIDk1Ljg3IDI3LjMyIEwgOTUuODcgMjEuMDcgTCA5NS44NyAxNC44MiBMIDk1Ljg3IDguNTcgTCA5NS44NyAyLjMyIFogTSAyODcuMjcgMC41NCBMIDI5NS44NCAwLjE4IEwgMzA0Ljc2IDAuMTggTCAzMTMuNjkgMC4xOCBMIDMyMi4yNiAwLjU0IEwgMzI5LjIyIDIuNTAgTCAzMzQuOTQgNS43MSBMIDMzOS41OCAxMC4wMCBMIDM0Mi45NyAxNS41MyBMIDM0NS40NyAyMS45NiBMIDM0NS42NSAzMC4zNSBMIDM0NC4wNCAzNy42NyBMIDM0MC42NSA0My4yMSBMIDMzNi4zNiA0Ny44NSBMIDMzMS4xOSA1MS42MCBMIDMzMC44MyA1MS45NSBMIDM1MS4zNCA3OC43MCBMIDM0MS4yMCA4Ni42MyBMIDMxNS45MyA1NC4yOCBMIDMwMC4xMiA1NC40NSBMIDI5OS43NiA1Ni4wNiBMIDI5OS43NiA1OC4wMiBMIDI5OS43NiA1OS45OSBMIDI5OS43NiA2MS45NSBMIDI5OS43NiA2My45MiBMIDI5OS43NiA2NS44OCBMIDI5OS43NiA2Ny44NCBMIDI5OS43NiA2OS44MSBMIDI5OS43NiA3MS43NyBMIDI5OS43NiA3My43NCBMIDI5OS43NiA3NS43MCBMIDI5OS43NiA3Ny42NiBMIDI5OS43NiA3OS42MyBMIDI5OS43NiA4MS41OSBMIDI5OS40MSA4Mi40OCBMIDI4Ny4yNyA4Mi40OCBMIDI4Ni45MSA3Ni45NSBMIDI4Ni45MSA3MS4wNiBMIDI4Ni45MSA2NS4xNyBMIDI4Ni45MSA1OS4yNyBMIDI4Ni45MSA1My4zOCBMIDI4Ni45MSA0Ny40OSBMIDI4Ni45MSA0MS42MCBMIDI4Ni45MSAzNS43MSBMIDI4Ni45MSAyOS44MiBMIDI4Ni45MSAyMy45MiBMIDI4Ni45MSAxOC4wMyBMIDI4Ni45MSAxMi4xNCBMIDI4Ni45MSA2LjI1IEwgMjg3LjI3IDAuNzEgWiBNIDE4NS43NSA5LjI1IEwgMTk3LjY5IDQuNDMgTCAyMjIuMDIgNjUuMzQgTCAyNDYuMzUgNC40MyBMIDI1OC4yOCA5LjI0IEwgMjIyLjAyIDEwMC4wMCBaIE0gMzAwLjEyIDEzLjU3IEwgMzAyLjgwIDEzLjIxIEwgMzA1LjgzIDEzLjIxIEwgMzA4Ljg3IDEzLjIxIEwgMzExLjkwIDEzLjIxIEwgMzE0Ljk0IDEzLjIxIEwgMzE3Ljk3IDEzLjIxIEwgMzIwLjgzIDEzLjM5IEwgMzIzLjE1IDE0LjEwIEwgMzI1LjQ3IDE0LjgyIEwgMzI3LjI2IDE2LjA3IEwgMzI4Ljg3IDE3LjUwIEwgMzMwLjI5IDE5LjEwIEwgMzMxLjU0IDIwLjg5IEwgMzMyLjQ0IDIzLjAzIEwgMzMyLjYyIDIzLjIxIEwgMzMyLjk3IDI2LjQyIEwgMzMyLjc5IDI5LjgyIEwgMzMxLjkwIDMyLjQ5IEwgMzMwLjgzIDM0Ljk5IEwgMzI5LjIyIDM2Ljk2IEwgMzI3LjI2IDM4LjU2IEwgMzI1LjEyIDM5Ljk5IEwgMzIyLjQ0IDQwLjg5IEwgMzE5LjQwIDQxLjQyIEwgMzE1LjgzIDQxLjQyIEwgMzEyLjI2IDQxLjQyIEwgMzA4LjY5IDQxLjQyIEwgMzA1LjEyIDQxLjQyIEwgMzAxLjU1IDQxLjQyIEwgMzAwLjEyIDQxLjA2IEwgMjk5Ljc2IDM5LjQ2IEwgMjk5Ljc2IDM3LjQ5IEwgMjk5Ljc2IDM1LjUzIEwgMjk5Ljc2IDMzLjU3IEwgMjk5Ljc2IDMxLjYwIEwgMjk5Ljc2IDI5LjY0IEwgMjk5Ljc2IDI3LjY3IEwgMjk5Ljc2IDI1LjcxIEwgMjk5Ljc2IDIzLjc1IEwgMjk5Ljc2IDIxLjc4IEwgMjk5Ljc2IDE5LjgyIEwgMjk5Ljc2IDE3Ljg1IEwgMjk5Ljc2IDE1Ljg5IEwgMjk5Ljk0IDE0LjEwIFogTSA0MTcuNTIgMjQuMTIgTCA0MjcuMzMgNDguNzQgTCA0MDcuNzAgNDguNzQgWiIvPgo8L3N2Zz4K'
26
26
 
27
27
  /** Le nom lisible de chaque produit, depuis logos/index.json. */
@@ -35,7 +35,7 @@ export const NOMS_PRODUITS: Record<GlypheId, string> = {
35
35
  'zevra': "Zevra",
36
36
  }
37
37
 
38
- /** Ce que chaque produit FAIT, en deux mots — pour le lanceur d'apps. */
38
+ /** Ce que chaque produit FAIT, en deux mots : pour le lanceur d'apps. */
39
39
  export const ACCROCHES_PRODUITS: Record<GlypheId, string> = {
40
40
  'campus': "Formation",
41
41
  'lexform': "Anonymisation",
@@ -1,4 +1,4 @@
1
- // GÉNÉRÉ par scripts/build-glyph-data.mjs — ne pas éditer à la main.
1
+ // GÉNÉRÉ par scripts/build-glyph-data.mjs : ne pas éditer à la main.
2
2
 
3
3
  /** Où la planche publie les glyphes (staticDirs de .storybook/main.ts). */
4
4
  export const BASE_RESSOURCES = 'https://design.zevra.tech'
package/react/glyphes.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // Les glyphes couleur des produits Zevra, prêts à poser.
2
2
  //
3
3
  // ⚠️ ENTRÉE SÉPARÉE, et c'est délibéré : ces images pèsent ~80 Ko une fois
4
- // inlinées. Le barrel React n'en porte pas un octet — seule une app qui
4
+ // inlinées. Le barrel React n'en porte pas un octet : seule une app qui
5
5
  // affiche des logos importe ce module.
6
6
  //
7
7
  // POURQUOI INLINÉS plutôt que servis en fichiers : un bundler ne sait pas
@@ -9,7 +9,7 @@
9
9
  // Storybook, un canvas sans réseau, un catalogue compilé par tsc qui ne sait
10
10
  // pas importer un PNG). C'est le même arbitrage que pour les icônes.
11
11
  //
12
- // ⚠️ Ce sont les versions COULEUR, à ne jamais recolorer ni resaturer — la
12
+ // ⚠️ Ce sont les versions COULEUR, à ne jamais recolorer ni resaturer : la
13
13
  // charte l'interdit. Sous 24 px, la charte demande une version monochrome :
14
14
  // elle n'existe pas encore (retirée le 24/08, à regénérer), et ces glyphes
15
15
  // ne doivent donc pas descendre sous cette taille.
@@ -30,11 +30,11 @@ export { ACCROCHES_PRODUITS, ADRESSES_PRODUITS, GLYPHES, MOT_ZEVRA, NOMS_PRODUIT
30
30
  export const PRODUITS = Object.keys(GLYPHES) as GlypheId[]
31
31
 
32
32
  /**
33
- * Les produits À METTRE DANS LE LANCEUR — ceux qui ont une adresse.
33
+ * Les produits À METTRE DANS LE LANCEUR : ceux qui ont une adresse.
34
34
  *
35
35
  * ⚠️ CE N'EST PAS `PRODUITS`, et l'écart est le sujet. Plume et Supernovia
36
36
  * ont leur glyphe depuis le 26/08 et rien à ouvrir : une tuile qui mène à
37
- * rien est pire qu'une absence — elle promet un produit, le visiteur clique,
37
+ * rien est pire qu'une absence, elle promet un produit, le visiteur clique,
38
38
  * et il tombe. Le lanceur se construit sur CETTE liste.
39
39
  *
40
40
  * Elle se déduit du registre, elle n'est pas recopiée : le jour où Plume
@@ -1,4 +1,4 @@
1
- // GÉNÉRÉ par scripts/build-icon-react.mjs — ne pas éditer à la main.
1
+ // GÉNÉRÉ par scripts/build-icon-react.mjs : ne pas éditer à la main.
2
2
  // Source de vérité : icons/index.json.
3
3
 
4
4
  export type IconId =
@@ -150,7 +150,7 @@ export const CORPS: Record<IconId, string> = {
150
150
  'video': "<path d=\"M3.5 6 H15 V18 H3.5 Z\" /><path d=\"M15 10 L20.5 7 V17 L15 14 Z\" />",
151
151
  }
152
152
 
153
- /** Nom lisible de chaque icône — sert de titre accessible par défaut à
153
+ /** Nom lisible de chaque icône : sert de titre accessible par défaut à
154
154
  * `IconButton`, et de libellé dans la page de consultation de l'admin. */
155
155
  export const NOMS: Record<IconId, string> = {
156
156
  'accueil': "Accueil",
package/react/icon.tsx CHANGED
@@ -1,4 +1,4 @@
1
- // Icônes — jeu de 55, tracées, sur grille de 24.
1
+ // Icônes : jeu de 55, tracées, sur grille de 24.
2
2
  //
3
3
  // Le corps du tracé est INLINÉ (voir scripts/build-icon-react.mjs) : pas de
4
4
  // <use href="…sprite.svg">, donc rien à résoudre côté bundler et rien à
@@ -12,7 +12,7 @@ import { CORPS, NOMS, type IconId } from './icon-data.js'
12
12
  export type { IconId }
13
13
  export { NOMS as NOMS_ICONES }
14
14
 
15
- /** Les 55 identifiants, triés — pour construire une planche ou un sélecteur. */
15
+ /** Les 55 identifiants, triés : pour construire une planche ou un sélecteur. */
16
16
  export const ICONES = Object.keys(CORPS) as IconId[]
17
17
 
18
18
  export interface IconProps
@@ -23,7 +23,7 @@ export interface IconProps
23
23
  taille?: 'sm' | 'normal' | 'lg' | 'xl'
24
24
  discret?: boolean
25
25
  /** Titre accessible. Sans lui l'icône est DÉCORATIVE et masquée aux
26
- * lecteurs d'écran — ce qui est le bon défaut : une icône accolée à un
26
+ * lecteurs d'écran, ce qui est le bon défaut : une icône accolée à un
27
27
  * libellé qui la répète crée une annonce double. */
28
28
  titre?: string
29
29
  className?: string
@@ -50,12 +50,12 @@ export function Icon({ nom, taille = 'normal', discret, titre, className, ...res
50
50
 
51
51
  export interface IconButtonProps
52
52
  extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
53
- /** Icône du jeu. À défaut, passe le glyphe en `children` — mais préfère le
53
+ /** Icône du jeu. À défaut, passe le glyphe en `children`, mais préfère le
54
54
  * jeu : trois tracés maison de la même « croix » ont déjà divergé. */
55
55
  icone?: IconId
56
56
  /** OBLIGATOIRE : une icône seule n'a pas de libellé pour la nommer.
57
57
  * L'écrire pour l'ACTION (« Fermer le panneau ») et non pour le dessin
58
- * (« Croix ») — c'est ce que le lecteur d'écran annonce. */
58
+ * (« Croix ») : c'est ce que le lecteur d'écran annonce. */
59
59
  label: string
60
60
  taille?: IconProps['taille']
61
61
  className?: string
@@ -88,7 +88,7 @@ export function IconButton({
88
88
  export interface IconTileProps
89
89
  extends Omit<HTMLAttributes<HTMLSpanElement>, 'className'> {
90
90
  nom: IconId
91
- /** Titre accessible. Sans lui la tuile est DÉCORATIVE — le bon défaut :
91
+ /** Titre accessible. Sans lui la tuile est DÉCORATIVE, le bon défaut :
92
92
  * elle accompagne un libellé qui la nomme déjà. */
93
93
  titre?: string
94
94
  className?: string
package/react/index.ts CHANGED
@@ -1,10 +1,10 @@
1
- // @zevra/ui/react — bibliothèque de composants du design system « Encre & Papier ».
1
+ // @zevra/ui/react : bibliothèque de composants du design system « Encre & Papier ».
2
2
  //
3
3
  // COMMENT C'EST ORGANISÉ. Le CSS reste la source de vérité : chaque composant
4
4
  // n'est qu'une façade typée et documentée au-dessus de classes `zv-`. Rien
5
5
  // n'est calculé au rendu, aucune valeur de charte n'est écrite en JavaScript.
6
6
  // Un test (tests/react.test.js) vérifie que toute classe qu'un composant peut
7
- // émettre existe bien dans le CSS — c'est ce qui empêche les deux de diverger.
7
+ // émettre existe bien dans le CSS : c'est ce qui empêche les deux de diverger.
8
8
  //
9
9
  // CE QU'IL FAUT CHARGER. Les composants ne portent PAS le CSS : il s'importe
10
10
  // à part, une fois, dans la page ou la mise en page racine.
@@ -16,7 +16,7 @@
16
16
  // ou '@zevra/ui/css' pour tout charger d'un coup.
17
17
  //
18
18
  // SERVEUR / CLIENT. Un seul module est marqué « use client » : `overlays`
19
- // (tiroir, modale, notification) — eux seuls ont besoin du navigateur. Tout
19
+ // (tiroir, modale, notification) : eux seuls ont besoin du navigateur. Tout
20
20
  // le reste se rend au serveur, y compris dans une île Astro à zéro JS.
21
21
  // Les composants qui prennent un gestionnaire d'événement (Switch, Seg,
22
22
  // Tabs…) restent sans état : c'est un composant client à toi qui détient
@@ -24,7 +24,7 @@
24
24
  //
25
25
  // ÉCRITURE DES PROPS. En français, comme le CSS et les commentaires. Chaque
26
26
  // prop qui porte une décision de charte est documentée à l'endroit où on la
27
- // lit — les avertissements ⚠️ signalent ce qui casse le système si on le
27
+ // lit : les avertissements ⚠️ signalent ce qui casse le système si on le
28
28
  // contourne, pas de simples préférences.
29
29
 
30
30
  export { cx, mod } from './cx.js'