@zevra/ui 0.35.0 → 0.37.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 +306 -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 +84 -59
  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
package/dist/catalogue.js CHANGED
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- // Catalogue — la planche de tous les composants, avec un exemple rendu et le
3
+ // Catalogue : la planche de tous les composants, avec un exemple rendu et le
4
4
  // code qui le produit.
5
5
  //
6
6
  // Il est PUBLIÉ avec le paquet, pas cantonné aux tests, et c'est délibéré :
7
7
  // · la page « Design system » de l'admin le rend tel quel, donc la
8
- // documentation ne peut pas dériver des composants — c'est le même code ;
8
+ // documentation ne peut pas dériver des composants : c'est le même code ;
9
9
  // · tests/react.test.js le rend en HTML et vérifie que chaque classe émise
10
10
  // existe dans le CSS. Un composant absent du catalogue n'est pas couvert :
11
11
  // c'est le rappel le plus efficace qu'on ait trouvé pour le tenir complet.
@@ -21,7 +21,7 @@ import { Alert, Badge, Caret, Dot, Empty, Loading, Progress, Skeleton, Spinner,
21
21
  import { Checkbox, Drop, Field, Input, InputAffix, InputPassword, Radio, Range, Search, Seg, SegGroup, Select, SelectAnchor, SelectTrigger, Switch, Textarea, } from './forms.js';
22
22
  import { ICONES, Icon, IconButton, IconTile, NOMS_ICONES } from './icon.js';
23
23
  // Les glyphes sont INLINÉS (data-URI) : le catalogue est rendu hors du dépôt
24
- // — planche, kit de canvas, page d'admin — où un chemin d'image ne résout pas.
24
+ // - planche, kit de canvas, page d'admin) où un chemin d'image ne résout pas.
25
25
  import { ACCROCHES_PRODUITS, ADRESSES_PRODUITS, APPS, GLYPHES, MOT_ZEVRA, NOMS_PRODUITS, } from './glyphes.js';
26
26
  import { Banner, CTA, Carousel, CarouselItem, Col, Compte, CompteLien, Footer, FooterMini, Grid, Grid12, Lanceur, LanceurApp, LanceurApps, LanceurGroupe, NavActions, Gutter, Hero, Langue, Langues, Lockup, LockupMark, LockupWord, Nav, NavDrawer, NavDrawerGroup, NavDrawerLink, NavDropdown, NavDropdownButton, NavDropdownMenu, NavDropdownSep, NavLink, NavLinks, NavLogo, ProofBar, Rail, RailItem, SectionHead, SectionRule, SheetSection, Stat, Stats, Step, Steps, } from './layout.js';
27
27
  import { ChromeButton, CitePanel, Crumbs, Menu, MenuItem, Pager, PagerArrow, PagerRank, PagerStep, Popover, Scroll, SideGroup, SideLink, Sidebar, SidebarFoot, SidebarHead, Tabs, Topbar, TopbarTitle, } from './navigation.js';
@@ -106,7 +106,7 @@ function DemoPopover() {
106
106
  return (_jsxs(SelectAnchor, { children: [_jsx(Button, { variante: "fantome", compact: true, "aria-expanded": ouvert, onClick: () => setOuvert(!ouvert), children: "Colonnes affich\u00E9es" }), ouvert && (_jsx(Popover, { children: _jsx("div", { style: { display: 'grid', gap: 8 }, children: ['Nom', 'Taille', 'Statut'].map((c) => (_jsxs("label", { style: { display: 'flex', gap: 8, alignItems: 'center' }, children: [_jsx(Checkbox, { defaultChecked: c !== 'Taille' }), _jsx("span", { className: "zv-body zv-body--dense", children: c })] }, c))) }) }))] }));
107
107
  }
108
108
  /** La barre de vitrine dans ses deux états. Rendue au REPOS : survoler le
109
- * lanceur ou la pastille déplie le panneau, comme sur un vrai site — c'est
109
+ * lanceur ou la pastille déplie le panneau, comme sur un vrai site, c'est
110
110
  * du CSS, il n'y a rien à hydrater. La réserve sous chaque barre existe pour
111
111
  * que le panneau ait la place de s'ouvrir sans être coupé. */
112
112
  function DemoNav() {
@@ -125,7 +125,7 @@ export const CATALOGUE = [
125
125
  {
126
126
  id: 'headings',
127
127
  nom: 'Titres',
128
- resume: 'Six paliers. Une page n’a qu’un <h1>, mais peut avoir plusieurs blocs à sa taille — passer « as » plutôt que de casser le plan du document.',
128
+ resume: 'Six paliers. Une page n’a qu’un <h1>, mais peut avoir plusieurs blocs à sa taille : passer « as » plutôt que de casser le plan du document.',
129
129
  code: `<H1>La recherche juridique, sourcée</H1>
130
130
  <H2 as="h3">Ce que voit votre équipe</H2>
131
131
  <H3>Indexation continue</H3>
@@ -135,7 +135,7 @@ export const CATALOGUE = [
135
135
  {
136
136
  id: 'display',
137
137
  nom: 'Display',
138
- resume: 'Chiffre ou mot posé pour sa présence, HORS de la hiérarchie des titres — annoncé comme titre de section, il pollue le plan du document.',
138
+ resume: 'Chiffre ou mot posé pour sa présence, HORS de la hiérarchie des titres : annoncé comme titre de section, il pollue le plan du document.',
139
139
  code: `<Display>4 812</Display>`,
140
140
  rendu: _jsx(Display, { children: "4 812" }),
141
141
  },
@@ -152,7 +152,7 @@ export const CATALOGUE = [
152
152
  {
153
153
  id: 'kicker',
154
154
  nom: 'Kicker et filet',
155
- resume: 'Le filet de spectre 56×2 devant un sur-titre. PREMIER des trois emplois autorisés du spectre — jamais en fond, jamais en texte.',
155
+ resume: 'Le filet de spectre 56×2 devant un sur-titre. PREMIER des trois emplois autorisés du spectre : jamais en fond, jamais en texte.',
156
156
  code: `<Kicker>Méthode</Kicker>
157
157
  <Kicker variante="stack">Sécurité</Kicker>
158
158
  <Spec petit />`,
@@ -184,7 +184,7 @@ export const CATALOGUE = [
184
184
  nom: 'Actions',
185
185
  couche: 'commun',
186
186
  css: 'components.css',
187
- resume: "Quatre variantes, et UNE SEULE pleine par page — « danger » comprise, la règle compte les aplats, pas les couleurs : c'est ce qui fait que l'action principale se voit. Un écran à deux boutons pleins n'en a plus aucun.",
187
+ resume: "Quatre variantes, et UNE SEULE pleine par page, « danger » comprise, la règle compte les aplats, pas les couleurs : c'est ce qui fait que l'action principale se voit. Un écran à deux boutons pleins n'en a plus aucun.",
188
188
  exemples: [
189
189
  {
190
190
  id: 'boutons',
@@ -209,7 +209,7 @@ export const CATALOGUE = [
209
209
  {
210
210
  id: 'etats',
211
211
  nom: 'États',
212
- resume: '« danger » COMPTE comme le bouton plein de la page — la règle compte les aplats, pas les couleurs ; à réserver à la confirmation destructive. « occupe » pose aria-busy et aria-disabled et AVALE le clic sans sortir le bouton du parcours clavier : le LIBELLÉ doit changer avec lui, l’estompement seul ne dit pas ce qu’on attend. Survol et appui sont dans le CSS, rien à câbler.',
212
+ resume: '« danger » COMPTE comme le bouton plein de la page : la règle compte les aplats, pas les couleurs ; à réserver à la confirmation destructive. « occupe » pose aria-busy et aria-disabled et AVALE le clic sans sortir le bouton du parcours clavier : le LIBELLÉ doit changer avec lui, l’estompement seul ne dit pas ce qu’on attend. Survol et appui sont dans le CSS, rien à câbler.',
213
213
  code: `<Button variante="danger">Supprimer définitivement</Button>
214
214
  <Button occupe>Connexion…</Button>
215
215
  <Button variante="fantome" disabled>Envoyer</Button>`,
@@ -218,7 +218,7 @@ export const CATALOGUE = [
218
218
  {
219
219
  id: 'boutons-icones',
220
220
  nom: 'Boutons à icône',
221
- resume: 'L’icône vient du jeu (16 px) et hérite de la couleur du libellé — en prop, jamais recopiée à la main. « carre » est l’action à icône SEULE : aria-label OBLIGATOIRE, écrit pour l’ACTION. Ne pas confondre avec IconButton (chrome nu, 40 px) ni ChromeButton (croix bordée) : le carré est une action, il en a le poids.',
221
+ resume: 'L’icône vient du jeu (16 px) et hérite de la couleur du libellé : en prop, jamais recopiée à la main. « carre » est l’action à icône SEULE : aria-label OBLIGATOIRE, écrit pour l’ACTION. Ne pas confondre avec IconButton (chrome nu, 40 px) ni ChromeButton (croix bordée) : le carré est une action, il en a le poids.',
222
222
  code: `<Button icone="telecharger">Télécharger le rapport</Button>
223
223
  <Button variante="fantome" iconeApres="chevron-droit">Étape suivante</Button>
224
224
  <Button carre variante="encre" icone="envoyer" aria-label="Envoyer le message" />`,
@@ -260,7 +260,7 @@ export const CATALOGUE = [
260
260
  {
261
261
  id: 'select',
262
262
  nom: 'Sélection',
263
- resume: 'Liste DESSINÉE jusqu’au bout — c’est le motif par défaut depuis le 25/08. Le <select> natif ne peut pas être habillé : sa liste est peinte par le système, et sort en menu noir sur un macOS en mode sombre. Clavier, annonce et recherche à la frappe sont réimplémentés ici.',
263
+ resume: 'Liste DESSINÉE jusqu’au bout : c’est le motif par défaut depuis le 25/08. Le <select> natif ne peut pas être habillé : sa liste est peinte par le système, et sort en menu noir sur un macOS en mode sombre. Clavier, annonce et recherche à la frappe sont réimplémentés ici.',
264
264
  code: `<Field label="RÔLE" htmlFor="role">
265
265
  <SelectMenu id="role" valeur={role} onChange={setRole} options={[
266
266
  { valeur: 'admin', label: 'Administrateur' },
@@ -289,7 +289,7 @@ export const CATALOGUE = [
289
289
  {
290
290
  id: 'recherche',
291
291
  nom: 'Recherche',
292
- resume: 'Cadre + loupe + champ nu. Deux marquages au clavier (l’anneau du champ ET le filet du cadre) : assumé — supprimer l’un rend le champ invisible, l’autre prive la souris de retour.',
292
+ resume: 'Cadre + loupe + champ nu. Deux marquages au clavier (l’anneau du champ ET le filet du cadre) : assumé, supprimer l’un rend le champ invisible, l’autre prive la souris de retour.',
293
293
  code: `<Search placeholder="Rechercher un document…" />`,
294
294
  rendu: (_jsx("div", { style: { maxWidth: 380 }, children: _jsx(Search, { placeholder: "Rechercher un document\u2026" }) })),
295
295
  },
@@ -326,7 +326,7 @@ export const CATALOGUE = [
326
326
  {
327
327
  id: 'curseur',
328
328
  nom: 'Curseur',
329
- resume: 'Natif habillé : clavier, bornes et annonce viennent du navigateur ; le remplissage suit la valeur en contrôlé. Toujours un intitulé ET la valeur affichée en clair — un curseur seul ne dit ni ce qu’il règle, ni sur combien. Pas de spectre sur la piste : ce n’est pas une barre de progression.',
329
+ resume: 'Natif habillé : clavier, bornes et annonce viennent du navigateur ; le remplissage suit la valeur en contrôlé. Toujours un intitulé ET la valeur affichée en clair : un curseur seul ne dit ni ce qu’il règle, ni sur combien. Pas de spectre sur la piste : ce n’est pas une barre de progression.',
330
330
  code: `<Field label="SOURCES INTERROGÉES" htmlFor="prof" help={\`\${sources} sources sur 17\`}>
331
331
  <Range id="prof" min={1} max={17} value={sources} onChange={…} />
332
332
  </Field>`,
@@ -353,7 +353,7 @@ export const CATALOGUE = [
353
353
  {
354
354
  id: 'mot-de-passe',
355
355
  nom: 'Mot de passe',
356
- resume: 'Le seul champ où l’on tape sans voir ce qu’on écrit : sur un mot de passe long saisi au clavier d’un téléphone, la bascule évite l’échec silencieux. L’état est tenu par l’appelant, et le libellé du bouton change — une icône seule ne dit pas ce qui va se passer.',
356
+ resume: 'Le seul champ où l’on tape sans voir ce qu’on écrit : sur un mot de passe long saisi au clavier d’un téléphone, la bascule évite l’échec silencieux. L’état est tenu par l’appelant, et le libellé du bouton change : une icône seule ne dit pas ce qui va se passer.',
357
357
  code: `const [visible, setVisible] = useState(false)
358
358
 
359
359
  <Field label="MOT DE PASSE" htmlFor="mdp">
@@ -370,7 +370,7 @@ export const CATALOGUE = [
370
370
  nom: 'Retours',
371
371
  couche: 'applicatif',
372
372
  css: 'feedback.css',
373
- resume: "Rien n'y crie : le bandeau porte le ton, la pastille l'état, la phrase le travail. La roue, le squelette et l'infobulle — longtemps refusés faute de relevé — sont arbitrés depuis le 25/08/2026 (DECISIONS.md), chacun avec son périmètre écrit ; « Loading », qui nomme ce qu'on attend, reste le motif d'attente par défaut.",
373
+ resume: "Rien n'y crie : le bandeau porte le ton, la pastille l'état, la phrase le travail. La roue, le squelette et l'infobulle, longtemps refusés faute de relevé, sont arbitrés depuis le 25/08/2026 (DECISIONS.md), chacun avec son périmètre écrit ; « Loading », qui nomme ce qu'on attend, reste le motif d'attente par défaut.",
374
374
  exemples: [
375
375
  {
376
376
  id: 'alertes',
@@ -421,7 +421,7 @@ export const CATALOGUE = [
421
421
  {
422
422
  id: 'roue',
423
423
  nom: 'Roue',
424
- resume: 'Attente COURTE et LOCALE : un bouton, une cellule, un panneau sans la place d’une phrase. Pour l’attente d’un écran, « Loading » reste le motif — la phrase dit sur quoi et sur combien. Sans label, la roue est décorative et masquée aux lecteurs d’écran.',
424
+ resume: 'Attente COURTE et LOCALE : un bouton, une cellule, un panneau sans la place d’une phrase. Pour l’attente d’un écran, « Loading » reste le motif : la phrase dit sur quoi et sur combien. Sans label, la roue est décorative et masquée aux lecteurs d’écran.',
425
425
  code: `<Spinner label="Vérification du dépôt…" />
426
426
  <Spinner grand />`,
427
427
  rendu: (_jsxs("div", { style: { display: 'flex', gap: 16, alignItems: 'center' }, children: [_jsx(Spinner, { label: "V\u00E9rification du d\u00E9p\u00F4t\u2026" }), _jsx(Spinner, { grand: true })] })),
@@ -429,7 +429,7 @@ export const CATALOGUE = [
429
429
  {
430
430
  id: 'squelette',
431
431
  nom: 'Squelette',
432
- resume: 'Il ANNONCE une structure connue — la carte, la ligne qui va venir ; pour un contenu de forme inconnue, c’est « Loading ». La géométrie vient de l’appelant : un squelette qui invente ses dimensions ne ressemble jamais au contenu. C’est la zone qui charge qui porte aria-busy, pas chaque os.',
432
+ resume: 'Il ANNONCE une structure connue : la carte, la ligne qui va venir ; pour un contenu de forme inconnue, c’est « Loading ». La géométrie vient de l’appelant : un squelette qui invente ses dimensions ne ressemble jamais au contenu. C’est la zone qui charge qui porte aria-busy, pas chaque os.',
433
433
  code: `<div aria-busy="true">
434
434
  <Skeleton texte largeur="60%" />
435
435
  <Skeleton texte />
@@ -440,7 +440,7 @@ export const CATALOGUE = [
440
440
  {
441
441
  id: 'infobulle',
442
442
  nom: 'Infobulle',
443
- resume: 'Un COMPLÉMENT dispensable — un raccourci, une date exacte, une unité : une commande n’a pas le droit d’y cacher son nom, et le tactile ne survole pas. Liage aria-describedby EXPLICITE (id), comme le htmlFor de Field — pas de useId, le rendu serveur reste possible. Apparition retardée de 300 ms, disparition immédiate, Échap la ferme.',
443
+ resume: 'Un COMPLÉMENT dispensable, un raccourci, une date exacte, une unité : une commande n’a pas le droit d’y cacher son nom, et le tactile ne survole pas. Liage aria-describedby EXPLICITE (id), comme le htmlFor de Field : pas de useId, le rendu serveur reste possible. Apparition retardée de 300 ms, disparition immédiate, Échap la ferme.',
444
444
  code: `<Tooltip id="th-date" contenu="Déposé le 12/08/2026 à 14 h 02">
445
445
  <Button variante="fantome" compact aria-describedby="th-date">12 août</Button>
446
446
  </Tooltip>`,
@@ -458,7 +458,7 @@ export const CATALOGUE = [
458
458
  {
459
459
  id: 'page-erreur',
460
460
  nom: 'Page d’erreur',
461
- resume: '404, 403, 410, 500. ⚠️ Le glyphe est DÉCORATIF par défaut (glypheAlt=""), et le mot « Erreur » se passe en prop : la 0.32.0 posait alt="Zevra" et « Erreur » en dur, si bien que la 404 de Mémoire annonçait « Zevra » au lecteur d’écran et affichait « Erreur 404 » sous /en/. Le composant ne porte AUCUN texte : une plaisanterie câblée dans un design system part dans les sept apps, dans une seule langue, et n’en revient jamais. La copie ci-dessous est la RÉFÉRENCE, à copier et à adapter. Elle plaisante en vocabulaire juridique — un mot juste employé de travers, pas une blague sur un ton qu’Encre & Papier n’a pas — et l’ordre est fixe : le titre dit la panne en clair, l’esprit vient ensuite et se dédit lui-même. Une page d’erreur qui fait rire avant d’informer est ratée.',
461
+ resume: '404, 403, 410, 500. ⚠️ Le glyphe est DÉCORATIF par défaut (glypheAlt=""), et le mot « Erreur » se passe en prop : la 0.32.0 posait alt="Zevra" et « Erreur » en dur, si bien que la 404 de Mémoire annonçait « Zevra » au lecteur d’écran et affichait « Erreur 404 » sous /en/. Le composant ne porte AUCUN texte : une plaisanterie câblée dans un design system part dans les sept apps, dans une seule langue, et n’en revient jamais. La copie ci-dessous est la RÉFÉRENCE, à copier et à adapter. Elle plaisante en vocabulaire juridique (un mot juste employé de travers, pas une blague sur un ton qu’Encre & Papier n’a pas) et l’ordre est fixe : le titre dit la panne en clair, l’esprit vient ensuite et se dédit lui-même. Une page d’erreur qui fait rire avant d’informer est ratée.',
462
462
  code: `<PageErreur
463
463
  code="404"
464
464
  libelle="Erreur" {/* le mot est À VOUS : « Error » sous /en/ */}
@@ -513,7 +513,7 @@ export const CATALOGUE = [
513
513
  {
514
514
  id: 'preuve',
515
515
  nom: 'Aperçu d’interface',
516
- resume: '⚠️ RÈGLE DE CHARTE : toujours de VRAIES données réalistes, jamais de lorem — c’est ce bloc qui porte la crédibilité de la page.',
516
+ resume: '⚠️ RÈGLE DE CHARTE : toujours de VRAIES données réalistes, jamais de lorem, c’est ce bloc qui porte la crédibilité de la page.',
517
517
  code: `<UiPreview question="Quelle est la durée de préavis ?"
518
518
  sources="12 sources" puces={<SrcRow>…</SrcRow>}>
519
519
  Trois mois pour les cadres…
@@ -560,7 +560,7 @@ export const CATALOGUE = [
560
560
  {
561
561
  id: 'page-legale',
562
562
  nom: 'Page légale',
563
- resume: 'La FORME d’un document contractuel ; le TEXTE vit dans @zevra/legal, versionné et archivé — le design system ne dépend pas du contenu, sinon corriger une clause deviendrait une livraison de charte. La version est AFFICHÉE : c’est ce que l’utilisateur cite quand il conteste. ⚠️ Jamais de « voir plus » sur un contrat : ce qu’on accepte se lit en entier là où on l’accepte. Le sommaire se pose à côté, avec « Rail ».',
563
+ resume: 'La FORME d’un document contractuel ; le TEXTE vit dans @zevra/legal, versionné et archivé : le design system ne dépend pas du contenu, sinon corriger une clause deviendrait une livraison de charte. La version est AFFICHÉE : c’est ce que l’utilisateur cite quand il conteste. ⚠️ Jamais de « voir plus » sur un contrat : ce qu’on accepte se lit en entier là où on l’accepte. Le sommaire se pose à côté, avec « Rail ».',
564
564
  code: `import { enVigueur } from '@zevra/legal'
565
565
 
566
566
  <LegalPage document={enVigueur('mentions-legales')} />`,
@@ -600,10 +600,10 @@ export const CATALOGUE = [
600
600
  {
601
601
  id: 'citation',
602
602
  nom: 'Citation client',
603
- resume: '⚠️ RÈGLE DE CHARTE : jamais de fausse citation. Tant qu’un verbatim n’a pas été recueilli, ce bloc reste EN RÉSERVE — un placeholder crédible finit toujours par partir en production.',
604
- code: `<Band><Quote auteur="— Nom, cabinet">Verbatim réel uniquement.</Quote></Band>`,
603
+ resume: '⚠️ RÈGLE DE CHARTE : jamais de fausse citation. Tant qu’un verbatim n’a pas été recueilli, ce bloc reste EN RÉSERVE : un placeholder crédible finit toujours par partir en production.',
604
+ code: `<Band><Quote auteur=": Nom, cabinet">Verbatim réel uniquement.</Quote></Band>`,
605
605
  surSombre: true,
606
- rendu: (_jsx(Band, { children: _jsx(Quote, { auteur: "\u2014 en attente d\u2019un verbatim recueilli", children: "Ce bloc reste en r\u00E9serve tant qu\u2019aucune citation r\u00E9elle n\u2019a \u00E9t\u00E9 recueillie." }) })),
606
+ rendu: (_jsx(Band, { children: _jsx(Quote, { auteur: ": en attente d\u2019un verbatim recueilli", children: "Ce bloc reste en r\u00E9serve tant qu\u2019aucune citation r\u00E9elle n\u2019a \u00E9t\u00E9 recueillie." }) })),
607
607
  },
608
608
  ],
609
609
  },
@@ -637,7 +637,7 @@ export const CATALOGUE = [
637
637
  {
638
638
  id: 'tableau-prose',
639
639
  nom: 'Tableau de prose',
640
- resume: 'Les cellules reviennent à la ligne au lieu de tronquer. ⚠️ Dès qu’une cellule porte une PHRASE et non une donnée — une clause, un fondement juridique, une durée et son motif —, tronquer ne raccourcit pas : ça cache, et le lecteur n’a aucun moyen de savoir qu’il manque quelque chose. La troncature reste juste pour une ligne de données, qui se balaye du regard.',
640
+ resume: 'Les cellules reviennent à la ligne au lieu de tronquer. ⚠️ Dès qu’une cellule porte une PHRASE et non une donnée (une clause, un fondement juridique, une durée et son motif), tronquer ne raccourcit pas : ça cache, et le lecteur n’a aucun moyen de savoir qu’il manque quelque chose. La troncature reste juste pour une ligne de données, qui se balaye du regard.',
641
641
  code: `<Table prose gabarit="minmax(0,1fr) minmax(0,2fr)">
642
642
  <TableHead><Cell>Donnée</Cell><Cell>Durée et fondement</Cell></TableHead>
643
643
  <Row>
@@ -672,7 +672,7 @@ export const CATALOGUE = [
672
672
  {
673
673
  id: 'meta',
674
674
  nom: 'Métadonnées',
675
- resume: 'Second registre mono, en BAS DE CASSE : taille, date, latence. Ni capitales ni interlettrage — c’est ce qui le distingue de « Mono », et une date en capitales espacées devient illisible.',
675
+ resume: 'Second registre mono, en BAS DE CASSE : taille, date, latence. Ni capitales ni interlettrage : c’est ce qui le distingue de « Mono », et une date en capitales espacées devient illisible.',
676
676
  code: `<MetaRow><Meta>12 mars 2026</Meta><Meta taille="sm">1,2 s</Meta></MetaRow>`,
677
677
  rendu: (_jsxs(MetaRow, { children: [_jsx(Meta, { taille: "md", children: "12 mars 2026" }), _jsx(Meta, { children: "14:07:22" }), _jsx(Meta, { taille: "sm", children: "1,24 s" }), _jsx(Meta, { discret: true, children: "2,4 Mo" })] })),
678
678
  },
@@ -689,7 +689,7 @@ export const CATALOGUE = [
689
689
  {
690
690
  id: 'avatar-chip',
691
691
  nom: 'Pastilles et catégories',
692
- resume: 'Avatar CARRÉ, jamais rond : le rond est un usage de réseau social. ⚠️ Sa teinte dit la NATURE de l’entité — la changer, c’est changer le sens.',
692
+ resume: 'Avatar CARRÉ, jamais rond : le rond est un usage de réseau social. ⚠️ Sa teinte dit la NATURE de l’entité : la changer, c’est changer le sens.',
693
693
  code: `<Avatar ton="accent">MD</Avatar>
694
694
  <ChipRow><Chip>Droit social</Chip><Chip variante="outline">2024</Chip></ChipRow>`,
695
695
  rendu: (_jsxs("div", { style: { display: 'grid', gap: 12 }, children: [_jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx(Avatar, { ton: "accent", children: "MD" }), _jsx(Avatar, { ton: "ink", children: "JL" }), _jsx(Avatar, { ton: "soft", children: "AB" }), _jsx(Avatar, { ton: "soft", petit: true, children: "ZV" })] }), _jsxs(ChipRow, { children: [_jsx(Chip, { children: "Droit social" }), _jsx(Chip, { children: "Convention collective" }), _jsx(Chip, { variante: "mono", children: "2024-114" }), _jsx(Chip, { variante: "outline", children: "Archiv\u00E9" })] })] })),
@@ -711,7 +711,7 @@ export const CATALOGUE = [
711
711
  {
712
712
  id: 'depliant',
713
713
  nom: 'Rangée dépliante',
714
- resume: '<details> NATIF : clavier, annonce et état viennent du navigateur, aucun JavaScript. Le pliage APPLICATIF — options avancées, détail d’une ligne de journal ; la FAQ (contenu) reste celui de la vitrine. Plusieurs rangées se posent bord à bord, le filet mitoyen tombe seul.',
714
+ resume: '<details> NATIF : clavier, annonce et état viennent du navigateur, aucun JavaScript. Le pliage APPLICATIF : options avancées, détail d’une ligne de journal ; la FAQ (contenu) reste celui de la vitrine. Plusieurs rangées se posent bord à bord, le filet mitoyen tombe seul.',
715
715
  code: `<Fold titre="Options avancées">…</Fold>
716
716
  <Fold titre="Journal d'indexation" ouvert>…</Fold>`,
717
717
  rendu: (_jsxs("div", { style: { maxWidth: 420 }, children: [_jsx(Fold, { titre: "Options avanc\u00E9es", children: "P\u00E9rim\u00E8tre, profondeur et sources exclues se r\u00E8glent par dossier, jamais par requ\u00EAte." }), _jsx(Fold, { titre: "Journal d\u2019indexation", ouvert: true, children: "Re\u00E7u, OCR, indexation : chaque \u00E9tape est dat\u00E9e, et l\u2019\u00E9chec d\u2019une page n\u2019interrompt pas les suivantes." })] })),
@@ -719,7 +719,7 @@ export const CATALOGUE = [
719
719
  {
720
720
  id: 'code',
721
721
  nom: 'Code et raccourcis',
722
- resume: 'La chasse fixe RÉELLE (--font-code) : --font-mono est proportionnelle et n’aligne pas du code, tabular-nums n’aligne que les chiffres. Aucune coloration syntaxique — elle imposerait une palette que la charte n’a pas. « ⌘K » fait DEUX touches, donc deux Kbd.',
722
+ resume: 'La chasse fixe RÉELLE (--font-code) : --font-mono est proportionnelle et n’aligne pas du code, tabular-nums n’aligne que les chiffres. Aucune coloration syntaxique : elle imposerait une palette que la charte n’a pas. « ⌘K » fait DEUX touches, donc deux Kbd.',
723
723
  code: `<Body>Lancer <Code>pnpm build</Code>, puis <Kbd>⌘</Kbd> <Kbd>K</Kbd>.</Body>
724
724
  <CodeBlock>claude mcp add --transport http zevra-design …</CodeBlock>`,
725
725
  rendu: (_jsxs("div", { style: { display: 'grid', gap: 12, maxWidth: 520 }, children: [_jsxs(Body, { taille: "dense", children: ["Lancer ", _jsx(Code, { children: "pnpm build" }), ", puis ouvrir la recherche avec ", _jsx(Kbd, { children: "\u2318" }), ' ', _jsx(Kbd, { children: "K" }), "."] }), _jsx(CodeBlock, { children: 'claude mcp add --transport http zevra-design \\\n https://mcp.design.zevra.tech/mcp --scope user' })] })),
@@ -731,12 +731,12 @@ export const CATALOGUE = [
731
731
  nom: 'Navigation',
732
732
  couche: 'applicatif',
733
733
  css: 'navigation.css',
734
- resume: "Rien n'a d'état propre : l'entrée courante, la page courante et l'item sélectionné viennent de l'appelant — c'est ce qui permet à une colonne de navigation Next de rester rendue au serveur.",
734
+ resume: "Rien n'a d'état propre : l'entrée courante, la page courante et l'item sélectionné viennent de l'appelant, c'est ce qui permet à une colonne de navigation Next de rester rendue au serveur.",
735
735
  exemples: [
736
736
  {
737
737
  id: 'sidebar',
738
738
  nom: 'Colonne latérale',
739
- resume: 'Une colonne, un filet, rien d’autre. « courant » pose aria-current="page" en plus de la classe : sans lui, l’entrée courante est marquée pour l’œil et muette au lecteur d’écran. « num » accepte un rang mono OU un glyphe (le ⚙ du relevé), sur la même colonne de 16 px — jamais les deux.',
739
+ resume: 'Une colonne, un filet, rien d’autre. « courant » pose aria-current="page" en plus de la classe : sans lui, l’entrée courante est marquée pour l’œil et muette au lecteur d’écran. « num » accepte un rang mono OU un glyphe (le ⚙ du relevé), sur la même colonne de 16 px : jamais les deux.',
740
740
  code: `<Sidebar>
741
741
  <SidebarHead>…</SidebarHead>
742
742
  <SideGroup titre="Espace">
@@ -752,7 +752,7 @@ export const CATALOGUE = [
752
752
  {
753
753
  id: 'topbar',
754
754
  nom: 'Barre supérieure',
755
- resume: '⚠️ Elle REMPLACE la colonne sous le point de rupture : masquée au-dessus, elle n’apparaît que dans le bloc mobile du CSS. Ne jamais la conditionner en JavaScript — le serveur ne connaît pas la largeur de l’écran.',
755
+ resume: '⚠️ Elle REMPLACE la colonne sous le point de rupture : masquée au-dessus, elle n’apparaît que dans le bloc mobile du CSS. Ne jamais la conditionner en JavaScript : le serveur ne connaît pas la largeur de l’écran.',
756
756
  code: `<Topbar>
757
757
  <TopbarBurger aria-label="Ouvrir la navigation">☰</TopbarBurger>
758
758
  <TopbarLogo><img src="/logo.svg" alt="" /></TopbarLogo>
@@ -763,14 +763,14 @@ export const CATALOGUE = [
763
763
  {
764
764
  id: 'crumbs',
765
765
  nom: 'Fil d’Ariane',
766
- resume: '⚠️ Le fil ENTIER est cliquable — c’est un seul nœud, pas une liste de liens. Le DERNIER segment est plus sombre : c’est celui qu’on lit, le reste n’est qu’un chemin.',
766
+ resume: '⚠️ Le fil ENTIER est cliquable : c’est un seul nœud, pas une liste de liens. Le DERNIER segment est plus sombre : c’est celui qu’on lit, le reste n’est qu’un chemin.',
767
767
  code: `<Crumbs href="/dossiers/2024" segments={['Dossiers', '2024', 'Duflot']} />`,
768
768
  rendu: _jsx(Crumbs, { href: "#", segments: ['Dossiers', '2024', 'Duflot & Associés'] }),
769
769
  },
770
770
  {
771
771
  id: 'onglets',
772
772
  nom: 'Onglets en puces',
773
- resume: 'La rangée de puces, grammaire d’état de Seg — le motif des FILTRES, qui restreignent une collection. Ce composant n’ajoute que l’ARIA. Pour les VUES d’un même objet, prendre la variante soulignée.',
773
+ resume: 'La rangée de puces, grammaire d’état de Seg : le motif des FILTRES, qui restreignent une collection. Ce composant n’ajoute que l’ARIA. Pour les VUES d’un même objet, prendre la variante soulignée.',
774
774
  code: `<Tabs actif={actif} onChange={setActif} onglets={[
775
775
  { id: 'apercu', label: 'Aperçu' },
776
776
  { id: 'sources', label: 'Sources', compte: 12 },
@@ -780,7 +780,7 @@ export const CATALOGUE = [
780
780
  {
781
781
  id: 'onglets-soulignes',
782
782
  nom: 'Onglets soulignés',
783
- resume: 'L’arbitrage longtemps en attente, tranché le 25/08/2026 : trait actif de 2 px — l’épaisseur des filets dessinés du système — en accent, PAS en spectre (un quatrième emploi n’existe pas). Pour les VUES d’un même objet (Aperçu / Sources) ; pour filtrer, les puces.',
783
+ resume: 'L’arbitrage longtemps en attente, tranché le 25/08/2026 : trait actif de 2 px, l’épaisseur des filets dessinés du système, en accent, PAS en spectre (un quatrième emploi n’existe pas). Pour les VUES d’un même objet (Aperçu / Sources) ; pour filtrer, les puces.',
784
784
  code: `<Tabs variante="soulignes" actif={actif} onChange={setActif} onglets={[
785
785
  { id: 'apercu', label: 'Aperçu' },
786
786
  { id: 'sources', label: 'Sources', compte: 12 },
@@ -790,7 +790,7 @@ export const CATALOGUE = [
790
790
  {
791
791
  id: 'popover',
792
792
  nom: 'Popover',
793
- resume: 'Surface ancrée LIBRE — aide contextuelle, mini-formulaire de filtre — dans la même ancre que le menu (SelectAnchor). Ce n’est PAS une infobulle : le popover s’ouvre au CLIC et peut porter de l’interactif. Fermeture par clic extérieur et Échap : à l’appelant, comme pour le menu.',
793
+ resume: 'Surface ancrée LIBRE (aide contextuelle, mini-formulaire de filtre) dans la même ancre que le menu (SelectAnchor). Ce n’est PAS une infobulle : le popover s’ouvre au CLIC et peut porter de l’interactif. Fermeture par clic extérieur et Échap : à l’appelant, comme pour le menu.',
794
794
  code: `<SelectAnchor>
795
795
  <Button variante="fantome" compact aria-expanded={ouvert} onClick={…}>Colonnes affichées</Button>
796
796
  {ouvert && <Popover>…</Popover>}
@@ -838,7 +838,7 @@ export const CATALOGUE = [
838
838
  nom: 'Enrobages',
839
839
  couche: 'commun',
840
840
  css: 'overlays.css',
841
- resume: "Le seul module marqué « use client ». Tout ce qui vit ici vole le regard à la page : il faut Échap, un piège de focus et un verrou de défilement — rien de cela ne s'écrit en CSS. Le hook « useOverlay » est exporté pour les enrobages maison.",
841
+ resume: "Le seul module marqué « use client ». Tout ce qui vit ici vole le regard à la page : il faut Échap, un piège de focus et un verrou de défilement, rien de cela ne s'écrit en CSS. Le hook « useOverlay » est exporté pour les enrobages maison.",
842
842
  exemples: [
843
843
  {
844
844
  id: 'tiroir',
@@ -853,7 +853,7 @@ export const CATALOGUE = [
853
853
  {
854
854
  id: 'modale',
855
855
  nom: 'Modale',
856
- resume: 'Fermée, elle n’est pas rendue du tout : son contenu — un formulaire, le plus souvent — ne doit pas rester dans le DOM à l’état fantôme. « danger » met l’avertissement dans le filet HAUT, pas dans un fond rouge qui rendrait le texte pénible à lire.',
856
+ resume: 'Fermée, elle n’est pas rendue du tout : son contenu (un formulaire, le plus souvent) ne doit pas rester dans le DOM à l’état fantôme. « danger » met l’avertissement dans le filet HAUT, pas dans un fond rouge qui rendrait le texte pénible à lire.',
857
857
  code: `<Modal ouvert={ouvert} onClose={…} titre="Supprimer" danger
858
858
  pied={<ButtonRow>…</ButtonRow>}>
859
859
  …
@@ -863,7 +863,7 @@ export const CATALOGUE = [
863
863
  {
864
864
  id: 'toast',
865
865
  nom: 'Notification',
866
- resume: '⚠️ Ne remplace PAS « Alert » : une erreur de formulaire se dit à côté du champ, jamais dans un coin qui disparaît. Réserver à ce qui est vrai APRÈS COUP — « copié », « enregistré », « envoyé ».',
866
+ resume: '⚠️ Ne remplace PAS « Alert » : une erreur de formulaire se dit à côté du champ, jamais dans un coin qui disparaît. Réserver à ce qui est vrai APRÈS COUP : « copié », « enregistré », « envoyé ».',
867
867
  code: `<Toasts>
868
868
  <Toast ton="ok">Lien copié.</Toast>
869
869
  </Toasts>`,
@@ -876,12 +876,12 @@ export const CATALOGUE = [
876
876
  nom: 'Icônes',
877
877
  couche: 'commun',
878
878
  css: 'icons.css',
879
- resume: "55 icônes TRACÉES, jamais pleines : trait 2, grille de 24, BOUTS FRANCS et jonctions vives — le trait carré de la charte, points d'encre carrés compris. Elles héritent de la couleur du texte — c'est ce qui leur permet de fonctionner sur papier comme sur la bande manifeste sans déclinaison. Le tracé est inliné : rien à résoudre côté bundler, rien à autoriser côté CSP.",
879
+ resume: "55 icônes TRACÉES, jamais pleines : trait 2, grille de 24, BOUTS FRANCS et jonctions vives, le trait carré de la charte, points d'encre carrés compris. Elles héritent de la couleur du texte : c'est ce qui leur permet de fonctionner sur papier comme sur la bande manifeste sans déclinaison. Le tracé est inliné : rien à résoudre côté bundler, rien à autoriser côté CSP.",
880
880
  exemples: [
881
881
  {
882
882
  id: 'jeu',
883
883
  nom: 'Le jeu complet',
884
- resume: 'Sans « titre », l’icône est DÉCORATIVE et masquée aux lecteurs d’écran — le bon défaut : accolée à un libellé qui la répète, elle crée une annonce double.',
884
+ resume: 'Sans « titre », l’icône est DÉCORATIVE et masquée aux lecteurs d’écran, le bon défaut : accolée à un libellé qui la répète, elle crée une annonce double.',
885
885
  code: `<Icon nom="recherche" />
886
886
  <Icon nom="document" taille="lg" />
887
887
  <Icon nom="alerte" titre="Alerte" />`,
@@ -898,12 +898,12 @@ export const CATALOGUE = [
898
898
  {
899
899
  id: 'tuile',
900
900
  nom: 'Tuile d’icône',
901
- resume: 'La pastille carrée devant un argument de la vitrine — liste de garanties, grille d’atouts. DÉCORATIVE par défaut : c’est le libellé voisin qui nomme, un titre ne se pose que si elle est seule. UNE par argument, jamais en décor isolé — une tuile sans libellé est un rébus.',
901
+ resume: 'La pastille carrée devant un argument de la vitrine : liste de garanties, grille d’atouts. DÉCORATIVE par défaut : c’est le libellé voisin qui nomme, un titre ne se pose que si elle est seule. UNE par argument, jamais en décor isolé : une tuile sans libellé est un rébus.',
902
902
  code: `<IconTile nom="bouclier-verifie" />
903
903
  <IconTile nom="balance" />`,
904
904
  rendu: (_jsx("div", { style: { display: 'grid', gap: 14, maxWidth: 380 }, children: [
905
905
  ['bouclier-verifie', 'Hébergement souverain', 'Infrastructure dédiée, en France.'],
906
- ['balance', 'Sources officielles', 'BOSS, Judilibre, KALI — datées et citées.'],
906
+ ['balance', 'Sources officielles', 'BOSS, Judilibre, KALI : datées et citées.'],
907
907
  ['cadenas', 'Zéro réutilisation', 'Vos pièces n’entraînent aucun modèle.'],
908
908
  ].map(([nom, titre, texte]) => (_jsxs("div", { style: { display: 'flex', gap: 14, alignItems: 'flex-start' }, children: [_jsx(IconTile, { nom: nom }), _jsxs("div", { children: [_jsx(CardTitle, { as: "p", children: titre }), _jsx(Body, { taille: "dense", children: texte })] })] }, nom))) })),
909
909
  },
@@ -914,12 +914,12 @@ export const CATALOGUE = [
914
914
  nom: 'Blocs éditoriaux',
915
915
  couche: 'vitrine',
916
916
  css: 'components.css',
917
- resume: "Quatre façons de poser du contenu SANS carte. Ils existent pour une règle de composition de la DA (24/08/2026) : jamais deux blocs de même forme qui se suivent, 80 % de formes différentes par page — les grilles de cartes redeviennent un choix, pas un réflexe.",
917
+ resume: "Quatre façons de poser du contenu SANS carte. Ils existent pour une règle de composition de la DA (24/08/2026) : jamais deux blocs de même forme qui se suivent, 80 % de formes différentes par page, les grilles de cartes redeviennent un choix, pas un réflexe.",
918
918
  exemples: [
919
919
  {
920
920
  id: 'rows',
921
921
  nom: 'Rangées à filets',
922
- resume: 'Libellé fort + contenu, un trait fin entre chaque rangée. Pour les listes où l’ALIGNEMENT fait la lecture (profils rencontrés, prestations, caractéristiques) — là où des cartes casseraient la comparaison.',
922
+ resume: 'Libellé fort + contenu, un trait fin entre chaque rangée. Pour les listes où l’ALIGNEMENT fait la lecture (profils rencontrés, prestations, caractéristiques) : là où des cartes casseraient la comparaison.',
923
923
  code: `<Rows>
924
924
  <RowsItem cle="Directions juridiques">Structurer la connaissance interne…</RowsItem>
925
925
  <RowsItem cle="Cabinets d’avocats">Retrouver la jurisprudence du cabinet…</RowsItem>
@@ -936,12 +936,12 @@ export const CATALOGUE = [
936
936
  </IndexList>
937
937
 
938
938
  <IndexList cols>…</IndexList>`,
939
- rendu: (_jsxs(IndexList, { children: [_jsxs(IndexItem, { num: "01", children: [_jsx(CardTitle, { children: "Cadrage" }), _jsx(Body, { taille: "dense", children: "Deux ateliers pour d\u00E9limiter le corpus et les usages prioritaires." })] }), _jsxs(IndexItem, { num: "02", children: [_jsx(CardTitle, { children: "Indexation" }), _jsx(Body, { taille: "dense", children: "Le corpus est oc\u00E9ris\u00E9, anonymis\u00E9 et index\u00E9 \u2014 comptez une semaine pour 10 000 pi\u00E8ces." })] })] })),
939
+ rendu: (_jsxs(IndexList, { children: [_jsxs(IndexItem, { num: "01", children: [_jsx(CardTitle, { children: "Cadrage" }), _jsx(Body, { taille: "dense", children: "Deux ateliers pour d\u00E9limiter le corpus et les usages prioritaires." })] }), _jsxs(IndexItem, { num: "02", children: [_jsx(CardTitle, { children: "Indexation" }), _jsx(Body, { taille: "dense", children: "Le corpus est oc\u00E9ris\u00E9, anonymis\u00E9 et index\u00E9 : comptez une semaine pour 10 000 pi\u00E8ces." })] })] })),
940
940
  },
941
941
  {
942
942
  id: 'flow',
943
943
  nom: 'Déroulé vertical',
944
- resume: 'La déclinaison verticale de « Steps », pour les méthodes qui se lisent comme un récit. Les points reprennent les couleurs des points de méthode — l’un des trois emplois autorisés du spectre.',
944
+ resume: 'La déclinaison verticale de « Steps », pour les méthodes qui se lisent comme un récit. Les points reprennent les couleurs des points de méthode : l’un des trois emplois autorisés du spectre.',
945
945
  code: `<Flow>
946
946
  <FlowStep><CardTitle>Déposer</CardTitle>…</FlowStep>
947
947
  <FlowStep teinte="violet"><CardTitle>Interroger</CardTitle>…</FlowStep>
@@ -952,7 +952,7 @@ export const CATALOGUE = [
952
952
  {
953
953
  id: 'callout',
954
954
  nom: 'Encart d’appel',
955
- resume: 'Une bande sur accent-soft, un titre, une action : l’offre transverse qui doit se voir sans crier (remise, renvoi vers une autre page). L’action passe en enfant — elle se range à droite et tombe sous le titre sur mobile.',
955
+ resume: 'Une bande sur accent-soft, un titre, une action : l’offre transverse qui doit se voir sans crier (remise, renvoi vers une autre page). L’action passe en enfant : elle se range à droite et tombe sous le titre sur mobile.',
956
956
  code: `<Callout titre="Formation Campus incluse la première année">
957
957
  <ButtonLink href="/campus" variante="fantome">Découvrir Campus</ButtonLink>
958
958
  </Callout>`,
@@ -981,7 +981,7 @@ export const CATALOGUE = [
981
981
  {
982
982
  id: 'grille',
983
983
  nom: 'Grilles',
984
- resume: '⚠️ Seules les portées 3, 4, 6, 8 et 9 existent : ce sont les découpes employées par la vitrine. En ajouter une revient à ajouter une mise en page — c’est une décision de DA, pas une utilité.',
984
+ resume: '⚠️ Seules les portées 3, 4, 6, 8 et 9 existent : ce sont les découpes employées par la vitrine. En ajouter une revient à ajouter une mise en page : c’est une décision de DA, pas une utilité.',
985
985
  code: `<Grid12>
986
986
  <Col span={8}>…</Col>
987
987
  <Col span={4}>…</Col>
@@ -993,7 +993,7 @@ export const CATALOGUE = [
993
993
  {
994
994
  id: 'sections',
995
995
  nom: 'Sections',
996
- resume: 'Le bloc « aside » tombe SOUS le titre en dessous du point de rupture — c’est le CSS qui le fait, pas une condition de rendu.',
996
+ resume: 'Le bloc « aside » tombe SOUS le titre en dessous du point de rupture : c’est le CSS qui le fait, pas une condition de rendu.',
997
997
  code: `<SectionHead aside={<LinkArrow href="/methode">Tout voir</LinkArrow>}>
998
998
  <Kicker>Méthode</Kicker>
999
999
  <H2>Trois étapes, rien de plus</H2>
@@ -1004,14 +1004,14 @@ export const CATALOGUE = [
1004
1004
  {
1005
1005
  id: 'sheet-section',
1006
1006
  nom: 'Section de document',
1007
- resume: 'Un intitulé mono, un filet, du corps — pour un document long.',
1007
+ resume: 'Un intitulé mono, un filet, du corps : pour un document long.',
1008
1008
  code: `<SheetSection titre="ENGAGEMENTS">…</SheetSection>`,
1009
1009
  rendu: (_jsx(SheetSection, { titre: "ENGAGEMENTS", children: _jsx(Body, { taille: "dense", children: "Les donn\u00E9es restent h\u00E9berg\u00E9es en France et ne sont jamais vers\u00E9es \u00E0 un entra\u00EEnement." }) })),
1010
1010
  },
1011
1011
  {
1012
1012
  id: 'nav-vitrine',
1013
1013
  nom: 'Navigation',
1014
- resume: 'LA barre du site, dans ses deux états — c’est le design de landing.zevra.tech, et il fait référence : une app qui compose sa propre barre finit par ne plus ressembler aux autres. Deux zones, et la distinction n’est pas cosmétique : « NavLinks » porte ce qui TOMBE dans le tiroir sous le point de rupture (familles, langues, appel à l’action) ; « NavActions » porte ce qui RESTE à toutes les largeurs — lanceur, compte, burger. Un compte placé dans NavLinks s’évanouit sur téléphone, et l’on ne peut plus se connecter. ⚠️ Le lanceur ET le compte sont là dans les deux états : les produits sont publics, et une barre où l’on ne peut pas se connecter est une barre où l’on ne revient pas. Seul le contenu du compte change. Survoler une famille, le lanceur ou la pastille pour déplier — c’est du CSS, il n’y a rien à hydrater. « zv-nav--riche » bascule vers le tiroir à 1150px, où une barre de cette densité déborde (mesuré), et non à 768.',
1014
+ resume: 'LA barre du site, dans ses deux états, c’est le design de landing.zevra.tech, et il fait référence : une app qui compose sa propre barre finit par ne plus ressembler aux autres. Deux zones, et la distinction n’est pas cosmétique : « NavLinks » porte ce qui TOMBE dans le tiroir sous le point de rupture (familles, langues, appel à l’action) ; « NavActions » porte ce qui RESTE à toutes les largeurs, lanceur, compte, burger. Un compte placé dans NavLinks s’évanouit sur téléphone, et l’on ne peut plus se connecter. ⚠️ Le lanceur ET le compte sont là dans les deux états : les produits sont publics, et une barre où l’on ne peut pas se connecter est une barre où l’on ne revient pas. Seul le contenu du compte change. Survoler une famille, le lanceur ou la pastille pour déplier : c’est du CSS, il n’y a rien à hydrater. « zv-nav--riche » bascule vers le tiroir à 1150px, où une barre de cette densité déborde (mesuré), et non à 768.',
1015
1015
  code: `<Nav className="zv-nav--riche">
1016
1016
  <NavLogo>
1017
1017
  <Lockup capitale={15}>
@@ -1043,7 +1043,7 @@ export const CATALOGUE = [
1043
1043
  {/* APPS = les produits QUI ONT UNE ADRESSE, pas les sept glyphes :
1044
1044
  une tuile qui mène à rien promet un produit et fait tomber le
1045
1045
  visiteur. Plume et Supernovia y entreront le jour où ils ouvrent,
1046
- par une ligne dans logos/index.json — sans toucher aux apps. */}
1046
+ par une ligne dans logos/index.json : sans toucher aux apps. */}
1047
1047
  <Lanceur> {/* titre="Applications" par défaut, filet de spectre */}
1048
1048
  <LanceurGroupe>Suite Zevra</LanceurGroupe>
1049
1049
  <LanceurApps>
@@ -1066,7 +1066,7 @@ export const CATALOGUE = [
1066
1066
  {
1067
1067
  id: 'lockup',
1068
1068
  nom: 'Lockup',
1069
- resume: 'La marque et le mot. Tout se pilote par « capitale », qui EST la hauteur de capitale : la marque vaut 2,86 fois cette valeur et l’écart 0,64, proportions relevées au pixel sur l’original. Sous 11 px, poser « marqueSeule » — le mot ne se lit plus, quelle que soit sa netteté, et c’est la marque qui identifie.',
1069
+ resume: 'La marque et le mot. Tout se pilote par « capitale », qui EST la hauteur de capitale : la marque vaut 2,86 fois cette valeur et l’écart 0,64, proportions relevées au pixel sur l’original. Sous 11 px, poser « marqueSeule » : le mot ne se lit plus, quelle que soit sa netteté, et c’est la marque qui identifie.',
1070
1070
  code: `<Lockup capitale={15}>
1071
1071
  <LockupMark src={GLYPHES.zevra} />
1072
1072
  <LockupWord src={MOT_ZEVRA} alt="Zevra" />
@@ -1076,7 +1076,7 @@ export const CATALOGUE = [
1076
1076
  {
1077
1077
  id: 'tiroir-groupe',
1078
1078
  nom: 'Tiroir groupé',
1079
- resume: 'Quand la barre porte des familles, les aplatir en une liste unique rend le tiroir illisible : un label capitale par famille, ses entrées en dessous. Le sélecteur de langue s’y pose en pied, détaché par un filet. L’ouverture et la fermeture viennent de « @zevra/ui/nav.js » — le tiroir doit vivre EN DEHORS du chrome flouté, qui devient sinon son bloc conteneur.',
1079
+ resume: 'Quand la barre porte des familles, les aplatir en une liste unique rend le tiroir illisible : un label capitale par famille, ses entrées en dessous. Le sélecteur de langue s’y pose en pied, détaché par un filet. L’ouverture et la fermeture viennent de « @zevra/ui/nav.js » : le tiroir doit vivre EN DEHORS du chrome flouté, qui devient sinon son bloc conteneur.',
1080
1080
  code: `<NavDrawer>
1081
1081
  <NavDrawerGroup>Services</NavDrawerGroup>
1082
1082
  <NavDrawerLink href="/audit" className="zv-nav-drawer__link--sous">Audit</NavDrawerLink>
@@ -1087,7 +1087,7 @@ export const CATALOGUE = [
1087
1087
  {
1088
1088
  id: 'rail',
1089
1089
  nom: 'Rail de lecture',
1090
- resume: 'La table des matières d’une page longue, posée sur le côté. Elle NAVIGUE (chaque entrée est une ancre) et elle SUIT — importer « @zevra/ui/rail.js » pour que l’entrée courante s’allume ; sans lui le rail navigue toujours, il ne suit plus. Le module lit la POSITION plutôt que d’observer les intersections : les ancres d’un rail sont souvent des points de 1 px en tête de section, qu’un défilement rapide fait manquer à un IntersectionObserver réduit à sa bande centrale. ⚠️ Il disparaît sous le point de rupture — un rail fixe sur un téléphone se pose sur la lecture.',
1090
+ resume: 'La table des matières d’une page longue, posée sur le côté. Elle NAVIGUE (chaque entrée est une ancre) et elle SUIT : importer « @zevra/ui/rail.js » pour que l’entrée courante s’allume ; sans lui le rail navigue toujours, il ne suit plus. Le module lit la POSITION plutôt que d’observer les intersections : les ancres d’un rail sont souvent des points de 1 px en tête de section, qu’un défilement rapide fait manquer à un IntersectionObserver réduit à sa bande centrale. ⚠️ Il disparaît sous le point de rupture : un rail fixe sur un téléphone se pose sur la lecture.',
1091
1091
  code: `<Rail aria-label="Sections">
1092
1092
  <RailItem href="#intro" courant>Intro</RailItem>
1093
1093
  <RailItem href="#methode">Méthode</RailItem>
@@ -1148,17 +1148,17 @@ export const CATALOGUE = [
1148
1148
  {
1149
1149
  id: 'bandeau',
1150
1150
  nom: 'Bandeau d’annonce',
1151
- resume: 'La bande AU-DESSUS de la barre de navigation : annonce de version, information réglementaire. UN SEUL par page, et jamais pour du marketing permanent — un bandeau qui ne part pas devient un bourrelet de chrome. Le lien s’y souligne : sur un fond déjà indigo, la couleur seule ne le distingue plus.',
1151
+ resume: 'La bande AU-DESSUS de la barre de navigation : annonce de version, information réglementaire. UN SEUL par page, et jamais pour du marketing permanent : un bandeau qui ne part pas devient un bourrelet de chrome. Le lien s’y souligne : sur un fond déjà indigo, la couleur seule ne le distingue plus.',
1152
1152
  code: `<Banner>
1153
- La charte évolue — le journal des livraisons est en ligne.{' '}
1153
+ La charte évolue : le journal des livraisons est en ligne.{' '}
1154
1154
  <a href="/journal">Ce qui change</a>
1155
1155
  </Banner>`,
1156
- rendu: (_jsxs(Banner, { children: ["La charte \u00E9volue \u2014 le journal des livraisons est en ligne.", ' ', _jsx("a", { href: "#", children: "Ce qui change" })] })),
1156
+ rendu: (_jsxs(Banner, { children: ["La charte \u00E9volue : le journal des livraisons est en ligne.", ' ', _jsx("a", { href: "#", children: "Ce qui change" })] })),
1157
1157
  },
1158
1158
  {
1159
1159
  id: 'carrousel',
1160
1160
  nom: 'Carrousel',
1161
- resume: 'Défilement natif scroll-snap, AUCUN JavaScript : pas de flèches, pas de points ; le conteneur prend le focus, et son label est OBLIGATOIRE — une région qui défile sans nom est une énigme au lecteur d’écran. Un carrousel reste un aveu d’espace : la règle de variété lui préfère une grille tant que le contenu tient.',
1161
+ resume: 'Défilement natif scroll-snap, AUCUN JavaScript : pas de flèches, pas de points ; le conteneur prend le focus, et son label est OBLIGATOIRE, une région qui défile sans nom est une énigme au lecteur d’écran. Un carrousel reste un aveu d’espace : la règle de variété lui préfère une grille tant que le contenu tient.',
1162
1162
  code: `<Carousel label="La suite Zevra">
1163
1163
  <CarouselItem style={{ width: 260 }}>
1164
1164
  <Card>…</Card>
@@ -1175,7 +1175,7 @@ export const CATALOGUE = [
1175
1175
  {
1176
1176
  id: 'media',
1177
1177
  nom: 'Cadre média',
1178
- resume: 'Le rapport vit sur le CADRE, jamais sur l’image : posé sur un enfant de grille aligné au centre, il ne se résout pas et la colonne s’effondre à zéro. Le cadre ne charge ni ne décode rien — pour masquer le temps de décodage, composer Spinner par-dessus.',
1178
+ resume: 'Le rapport vit sur le CADRE, jamais sur l’image : posé sur un enfant de grille aligné au centre, il ne se résout pas et la colonne s’effondre à zéro. Le cadre ne charge ni ne décode rien : pour masquer le temps de décodage, composer Spinner par-dessus.',
1179
1179
  code: `<Media ratio="16-9">
1180
1180
  <MediaImg src={couverture} alt="" />
1181
1181
  <MediaVoile />
@@ -1195,12 +1195,12 @@ export const CATALOGUE = [
1195
1195
  nom: 'Surfaces',
1196
1196
  couche: 'vitrine',
1197
1197
  css: 'surfaces.css + motion.css',
1198
- resume: "Ce qui distingue une page Zevra d'une page blanche bien composée. ⚠️ Tout y est DÉCORATIF : posé en absolu derrière le contenu, sans capter le pointeur, masqué aux lecteurs d'écran — et suppose un parent « position: relative; overflow: hidden ».",
1198
+ resume: "Ce qui distingue une page Zevra d'une page blanche bien composée. ⚠️ Tout y est DÉCORATIF : posé en absolu derrière le contenu, sans capter le pointeur, masqué aux lecteurs d'écran, et suppose un parent « position: relative; overflow: hidden ».",
1199
1199
  exemples: [
1200
1200
  {
1201
1201
  id: 'bande',
1202
1202
  nom: 'Bande manifeste',
1203
- resume: '⚠️ LA surface sombre du système. Les surfaces sombres se font avec elle et --accent-on-dark, JAMAIS en inversant les neutres. La bande ne porte AUCUNE gouttière propre : c’est la page qui la donne — poser un Gutter dedans, sinon le contenu colle au bord.',
1203
+ resume: '⚠️ LA surface sombre du système. Les surfaces sombres se font avec elle et --accent-on-dark, JAMAIS en inversant les neutres. La bande ne porte AUCUNE gouttière propre : c’est la page qui la donne, poser un Gutter dedans, sinon le contenu colle au bord.',
1204
1204
  code: `<Band lueur filigrane={<Glyph variante="watermark">…</Glyph>}>
1205
1205
  <Gutter>
1206
1206
  <Kicker surSombre>Souveraineté</Kicker>
@@ -1213,7 +1213,7 @@ export const CATALOGUE = [
1213
1213
  {
1214
1214
  id: 'panneau-navy',
1215
1215
  nom: 'Panneau sombre',
1216
- resume: 'Plus petit qu’une bande : une carte, un encart. « TextOnDark » repeint un texte isolé — « Band » le fait déjà pour son contenu courant, l’ajouter dedans ne sert à rien.',
1216
+ resume: 'Plus petit qu’une bande : une carte, un encart. « TextOnDark » repeint un texte isolé : « Band » le fait déjà pour son contenu courant, l’ajouter dedans ne sert à rien.',
1217
1217
  code: `<PanelNavy><TextOnDark>…</TextOnDark></PanelNavy>`,
1218
1218
  surSombre: true,
1219
1219
  rendu: (_jsx(PanelNavy, { children: _jsxs(TextOnDark, { children: [_jsx(Mono, { ton: "on-dark", children: "CHIFFREMENT" }), _jsx(Body, { children: "AES-256 au repos, TLS 1.3 en transit." })] }) })),
@@ -1221,7 +1221,7 @@ export const CATALOGUE = [
1221
1221
  {
1222
1222
  id: 'halo',
1223
1223
  nom: 'Halo, sol, voile',
1224
- resume: 'Le violet et le cyan du spectre, très dilués. Ce n’est PAS un emploi du spectre en aplat : c’est une lumière — elle ne compte pas dans les trois emplois autorisés.',
1224
+ resume: 'Le violet et le cyan du spectre, très dilués. Ce n’est PAS un emploi du spectre en aplat : c’est une lumière, elle ne compte pas dans les trois emplois autorisés.',
1225
1225
  code: `<section style={{ position: 'relative', overflow: 'hidden' }}>
1226
1226
  <Halo place="hero" />
1227
1227
  <GridFloor />
@@ -1256,7 +1256,7 @@ export const CATALOGUE = [
1256
1256
  {
1257
1257
  id: 'format-social',
1258
1258
  nom: 'Formats sociaux',
1259
- resume: 'Planches d’EXPORT — paysage 1200×630 (OpenGraph, lien LinkedIn), carré 1080 (post), story 1080×1920 — FIGÉES en pixels : on les capture, on ne les publie pas en HTML, et elles ignorent le point de rupture. Un visuel social EST une publication : jamais de faux chiffre ni de fausse citation. Rendue ici à l’échelle 0,35.',
1259
+ resume: 'Planches d’EXPORT (paysage 1200×630 (OpenGraph, lien LinkedIn), carré 1080 (post), story 1080×1920) FIGÉES en pixels : on les capture, on ne les publie pas en HTML, et elles ignorent le point de rupture. Un visuel social EST une publication : jamais de faux chiffre ni de fausse citation. Rendue ici à l’échelle 0,35.',
1260
1260
  code: `<SocialFrame manifeste pied={<><Mono ton="on-dark">ZEVRA</Mono><Mono ton="on-dark">zevra.tech</Mono></>}>
1261
1261
  <Kicker surSombre><Mono ton="on-dark">Manifeste · Article 1ᵉʳ</Mono></Kicker>
1262
1262
  <Heading taille="h2" as="p">
@@ -1271,6 +1271,6 @@ export const CATALOGUE = [
1271
1271
  function Heading2Cta() {
1272
1272
  return _jsx(Heading, { taille: "h2-cta", children: "Voir Zevra sur vos propres pi\u00E8ces." });
1273
1273
  }
1274
- /** Tous les exemples à plat — pratique pour une recherche ou un test. */
1274
+ /** Tous les exemples à plat : pratique pour une recherche ou un test. */
1275
1275
  export const EXEMPLES = CATALOGUE.flatMap((g) => g.exemples.map((e) => ({ ...e, groupe: g.id })));
1276
1276
  //# sourceMappingURL=catalogue.js.map