@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.
- package/DECISIONS.md +264 -208
- package/README.md +21 -21
- package/demo/app.html +6 -6
- package/demo/index.html +3 -3
- package/dist/actions.d.ts +8 -8
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +1 -1
- package/dist/actions.js.map +1 -1
- package/dist/catalogue.d.ts +2 -2
- package/dist/catalogue.js +67 -67
- package/dist/catalogue.js.map +1 -1
- package/dist/content.d.ts +23 -23
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +16 -16
- package/dist/content.js.map +1 -1
- package/dist/css-inline.d.ts +1 -1
- package/dist/css-inline.d.ts.map +1 -1
- package/dist/css-inline.js +4 -4
- package/dist/css-inline.js.map +1 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/data.d.ts +7 -7
- package/dist/data.js +5 -5
- package/dist/email-tokens.js +1 -1
- package/dist/email.d.ts +6 -6
- package/dist/email.js +8 -8
- package/dist/email.js.map +1 -1
- package/dist/feedback.d.ts +10 -10
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +3 -3
- package/dist/forms.d.ts +12 -12
- package/dist/forms.js +5 -5
- package/dist/glyph-data.d.ts +2 -2
- package/dist/glyph-data.js +3 -3
- package/dist/glyph-urls.js +1 -1
- package/dist/glyphes.d.ts +2 -2
- package/dist/glyphes.js +4 -4
- package/dist/icon-data.d.ts +1 -1
- package/dist/icon-data.js +2 -2
- package/dist/icon.d.ts +5 -5
- package/dist/icon.js +1 -1
- package/dist/index.js +4 -4
- package/dist/layout.d.ts +21 -21
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +15 -15
- package/dist/layout.js.map +1 -1
- package/dist/navigation.d.ts +16 -16
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +9 -9
- package/dist/navigation.js.map +1 -1
- package/dist/overlays.d.ts +6 -6
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +5 -5
- package/dist/select-menu.d.ts +1 -1
- package/dist/select-menu.js +2 -2
- package/dist/surfaces.d.ts +8 -8
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +6 -6
- package/dist/surfaces.js.map +1 -1
- package/dist/typography.d.ts +4 -4
- package/dist/typography.d.ts.map +1 -1
- package/dist/typography.js +3 -3
- package/dist/typography.js.map +1 -1
- package/icons/index.json +1 -1
- package/package.json +2 -2
- package/react/actions.tsx +9 -9
- package/react/catalogue.tsx +68 -68
- package/react/content.tsx +28 -28
- package/react/css-inline.ts +4 -4
- package/react/data-table.tsx +3 -3
- package/react/data.tsx +9 -9
- package/react/email-tokens.ts +1 -1
- package/react/email.ts +10 -10
- package/react/feedback.tsx +12 -12
- package/react/forms.tsx +15 -15
- package/react/glyph-data.ts +3 -3
- package/react/glyph-urls.ts +1 -1
- package/react/glyphes.ts +4 -4
- package/react/icon-data.ts +2 -2
- package/react/icon.tsx +6 -6
- package/react/index.ts +4 -4
- package/react/layout.tsx +22 -22
- package/react/navigation.tsx +18 -18
- package/react/overlays.tsx +9 -9
- package/react/select-menu.tsx +3 -3
- package/react/surfaces.tsx +10 -10
- package/react/typography.tsx +6 -6
- package/src/base.css +10 -10
- package/src/components.css +49 -49
- package/src/data.css +41 -41
- package/src/feedback.css +55 -55
- package/src/fonts.css +2 -2
- package/src/forms.css +50 -50
- package/src/icons.css +10 -10
- package/src/index.css +1 -1
- package/src/layout.css +27 -27
- package/src/motion.css +16 -16
- package/src/motion.js +4 -4
- package/src/nav.js +5 -5
- package/src/navigation.css +62 -62
- package/src/overlays.css +14 -14
- package/src/rail.js +6 -6
- package/src/surfaces.css +11 -11
- package/src/tokens.css +14 -14
- package/tokens/index.js +8 -8
package/react/layout.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Mise en page vitrine
|
|
1
|
+
// Mise en page vitrine : page, gouttières, grille 12, sections, hero, frise
|
|
2
2
|
// d'étapes, statistiques, pieds de page, couches d'empilement.
|
|
3
3
|
//
|
|
4
4
|
// Couche VITRINE : une application n'a aucune raison de charger ce fichier.
|
|
@@ -34,7 +34,7 @@ export function Page({
|
|
|
34
34
|
|
|
35
35
|
export interface GutterProps extends Div {
|
|
36
36
|
/** `wide` pour une bande pleine largeur, `chrome` pour la barre de
|
|
37
|
-
* navigation
|
|
37
|
+
* navigation : qui ne s'aligne pas sur la colonne de texte. */
|
|
38
38
|
variante?: 'wide' | 'chrome'
|
|
39
39
|
as?: ElementType
|
|
40
40
|
className?: string
|
|
@@ -67,9 +67,9 @@ export function Grid12({ className, ...reste }: Div & { className?: string }) {
|
|
|
67
67
|
export type Span12 = 3 | 4 | 5 | 6 | 7 | 8 | 9
|
|
68
68
|
|
|
69
69
|
/** ⚠️ Seules ces sept portées existent : ce sont les découpes employées par
|
|
70
|
-
* la vitrine (5+7 vient du prototype experience.html
|
|
70
|
+
* la vitrine (5+7 vient du prototype experience.html, le glyphe en 5, le
|
|
71
71
|
* discours en 7). En ajouter une revient à ajouter une mise en page, pas
|
|
72
|
-
* une utilité
|
|
72
|
+
* une utilité : c'est une décision de DA. */
|
|
73
73
|
export function Col({
|
|
74
74
|
span,
|
|
75
75
|
className,
|
|
@@ -170,7 +170,7 @@ export function NavLinks({ className, ...reste }: Div & { className?: string })
|
|
|
170
170
|
* `AnchorHTMLAttributes` et non `HTMLAttributes` : sans quoi ils refusent
|
|
171
171
|
* `target`, `rel`, `download` et `hreflang`. Un lien sortant ne peut alors
|
|
172
172
|
* pas porter `rel="noopener"`, et un lien qui doit s'ouvrir à côté ne le
|
|
173
|
-
* peut pas non plus
|
|
173
|
+
* peut pas non plus : une app consommatrice a dû écrire la classe à la main
|
|
174
174
|
* pour cette seule raison. (0.19.0) */
|
|
175
175
|
export interface NavLinkProps
|
|
176
176
|
extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'className'> {
|
|
@@ -181,7 +181,7 @@ export interface NavLinkProps
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/** ⚠️ `.zv-navlink` est la navigation de VITRINE. L'entrée applicative
|
|
184
|
-
* s'appelle `SideLink`
|
|
184
|
+
* s'appelle `SideLink` : les deux ne doivent jamais se recouvrir. */
|
|
185
185
|
export function NavLink({ discret, className, ...reste }: NavLinkProps) {
|
|
186
186
|
return (
|
|
187
187
|
<a className={cx('zv-navlink', discret && 'zv-navlink--quiet', className)} {...reste} />
|
|
@@ -201,7 +201,7 @@ export function NavBurger({
|
|
|
201
201
|
return <button type={type} className={cx('zv-nav__burger', className)} {...reste} />
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
/** Le contenu du tiroir de navigation vitrine
|
|
204
|
+
/** Le contenu du tiroir de navigation vitrine : à poser dans `Drawer`. */
|
|
205
205
|
export function NavDrawer({ className, ...reste }: Div & { className?: string }) {
|
|
206
206
|
return <div className={cx('zv-nav-drawer', className)} {...reste} />
|
|
207
207
|
}
|
|
@@ -233,7 +233,7 @@ export function NavDrawerGroup({ className, ...reste }: Span & { className?: str
|
|
|
233
233
|
/* ─── Entrées à menu déroulant ─────────────────────────────────────── */
|
|
234
234
|
|
|
235
235
|
/** Une famille qui se déplie. Ouverture au survol ET au focus, en CSS seul :
|
|
236
|
-
* pas d'état à tenir, donc rien à hydrater
|
|
236
|
+
* pas d'état à tenir, donc rien à hydrater, une barre de vitrine reste
|
|
237
237
|
* rendue au serveur. */
|
|
238
238
|
export function NavDropdown({ className, ...reste }: Span & { className?: string }) {
|
|
239
239
|
return <span className={cx('zv-navdd', className)} {...reste} />
|
|
@@ -367,7 +367,7 @@ export function LockupWord({
|
|
|
367
367
|
/* ─── Rail de lecture ──────────────────────────────────────────────── */
|
|
368
368
|
|
|
369
369
|
/** La table des matières d'une page longue, posée sur le côté. Elle navigue
|
|
370
|
-
* (chaque entrée est une ancre) et elle suit
|
|
370
|
+
* (chaque entrée est une ancre) et elle suit : poser `data-zv-rail` et
|
|
371
371
|
* importer `@zevra/ui/rail.js` pour que l'entrée courante s'allume. Sans le
|
|
372
372
|
* module, le rail navigue toujours : rien ne casse, le suivi manque.
|
|
373
373
|
*
|
|
@@ -392,7 +392,7 @@ export function Rail({
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
/** ⚠️ `href` doit désigner une ancre de la page (`#section`) : c'est elle
|
|
395
|
-
* que le module surveille, et c'est le balisage qui fait foi
|
|
395
|
+
* que le module surveille, et c'est le balisage qui fait foi, il n'y a
|
|
396
396
|
* pas de liste de sections à tenir à jour ailleurs. */
|
|
397
397
|
export function RailItem({
|
|
398
398
|
courant,
|
|
@@ -420,7 +420,7 @@ export function RailItem({
|
|
|
420
420
|
/* ─── Frise d'étapes ───────────────────────────────────────────────── */
|
|
421
421
|
|
|
422
422
|
export interface StepsProps extends Div {
|
|
423
|
-
/** Avancement du rail, entre 0 et 1
|
|
423
|
+
/** Avancement du rail, entre 0 et 1 : piloté au défilement. */
|
|
424
424
|
avancement?: number
|
|
425
425
|
className?: string
|
|
426
426
|
children?: ReactNode
|
|
@@ -562,7 +562,7 @@ export function OnlyMobile({ className, ...reste }: Div & { className?: string }
|
|
|
562
562
|
|
|
563
563
|
/** La bande AU-DESSUS de la barre de navigation : annonce de version,
|
|
564
564
|
* information réglementaire. UN SEUL par page, et jamais pour du marketing
|
|
565
|
-
* permanent
|
|
565
|
+
* permanent : un bandeau qui ne part pas devient un bourrelet de chrome. */
|
|
566
566
|
export function Banner({ className, ...reste }: Div & { className?: string }) {
|
|
567
567
|
return <div className={cx('zv-banner', className)} {...reste} />
|
|
568
568
|
}
|
|
@@ -577,7 +577,7 @@ export interface CarouselProps extends Div {
|
|
|
577
577
|
}
|
|
578
578
|
|
|
579
579
|
/** Défilement natif scroll-snap, AUCUN JavaScript : pas de flèches, pas de
|
|
580
|
-
* points. Le conteneur est focalisable
|
|
580
|
+
* points. Le conteneur est focalisable : c'est lui que les flèches du
|
|
581
581
|
* clavier font défiler. La largeur des éléments vient du consommateur.
|
|
582
582
|
* ⚠️ Un carrousel reste un aveu d'espace : la règle de variété lui préfère
|
|
583
583
|
* une grille tant que le contenu tient. */
|
|
@@ -602,7 +602,7 @@ export function CarouselItem({ className, ...reste }: Div & { className?: string
|
|
|
602
602
|
* ⚠️ À poser À CÔTÉ de `NavLinks`, jamais DEDANS. `NavLinks` disparaît sous
|
|
603
603
|
* le point de rupture (et sous 1150px en `nav--riche`), remplacée par le
|
|
604
604
|
* burger : un compte placé dedans s'évanouit sur téléphone, et l'on ne peut
|
|
605
|
-
* plus se connecter
|
|
605
|
+
* plus se connecter, c'est pourtant la première chose qu'on y fait. */
|
|
606
606
|
export function NavActions({ className, ...reste }: Div & { className?: string }) {
|
|
607
607
|
return <div className={cx('zv-nav__actions', className)} {...reste} />
|
|
608
608
|
}
|
|
@@ -610,12 +610,12 @@ export function NavActions({ className, ...reste }: Div & { className?: string }
|
|
|
610
610
|
/* ─── Lanceur d'applications ───────────────────────────────────────── */
|
|
611
611
|
|
|
612
612
|
/** Le lanceur : passer d'un produit Zevra à l'autre, depuis n'importe lequel.
|
|
613
|
-
* Il se pose À DROITE de la barre, à côté du compte
|
|
613
|
+
* Il se pose À DROITE de la barre, à côté du compte : c'est la convention
|
|
614
614
|
* que tout le monde connaît, et ce n'est pas le moment d'en inventer une.
|
|
615
615
|
*
|
|
616
616
|
* Ouvert au survol et au focus en CSS seul : la barre reste rendue au
|
|
617
617
|
* serveur, sans rien à hydrater. Une app qui préfère l'ouverture au CLIC
|
|
618
|
-
* (la convention des lanceurs) tient l'état elle-même et passe `ouvert`
|
|
618
|
+
* (la convention des lanceurs) tient l'état elle-même et passe `ouvert` :
|
|
619
619
|
* un état se dit en prop, il ne se peint pas depuis l'app. */
|
|
620
620
|
export function Lanceur({
|
|
621
621
|
label = 'Applications Zevra',
|
|
@@ -627,7 +627,7 @@ export function Lanceur({
|
|
|
627
627
|
}: Div & {
|
|
628
628
|
label?: string
|
|
629
629
|
/** L'intitulé du panneau, avec le filet de spectre. Le passer à `null`
|
|
630
|
-
* pour un panneau nu
|
|
630
|
+
* pour un panneau nu : mais c'est ce filet qui fait qu'un lanceur
|
|
631
631
|
* ressemble à Zevra plutôt qu'à n'importe quel autre. */
|
|
632
632
|
titre?: ReactNode
|
|
633
633
|
ouvert?: boolean
|
|
@@ -668,7 +668,7 @@ export function LanceurGroupe({ className, ...reste }: Span & { className?: stri
|
|
|
668
668
|
return <span className={cx('zv-lanceur__groupe', className)} {...reste} />
|
|
669
669
|
}
|
|
670
670
|
|
|
671
|
-
/** La grille de tuiles. Elle enveloppe les `LanceurApp` d'une même famille
|
|
671
|
+
/** La grille de tuiles. Elle enveloppe les `LanceurApp` d'une même famille :
|
|
672
672
|
* c'est ce qui permet à un panneau d'en porter plusieurs. */
|
|
673
673
|
export function LanceurApps({ className, ...reste }: Span & { className?: string }) {
|
|
674
674
|
return <span className={cx('zv-lanceur__apps', className)} {...reste} />
|
|
@@ -682,7 +682,7 @@ export interface LanceurAppProps
|
|
|
682
682
|
* qui ne connaît pas encore la suite : un nom de marque seul ne dit rien
|
|
683
683
|
* à un client qui découvre. */
|
|
684
684
|
accroche?: string
|
|
685
|
-
/** Le glyphe du produit
|
|
685
|
+
/** Le glyphe du produit : `GLYPHES.memoire` de `@zevra/ui/glyphes`. Sans
|
|
686
686
|
* lui, la tuile porte l'initiale : une image qui ne charge pas ne doit
|
|
687
687
|
* pas laisser un trou. */
|
|
688
688
|
glyphe?: string
|
|
@@ -732,7 +732,7 @@ export interface CompteProps extends Div {
|
|
|
732
732
|
ouvert?: boolean
|
|
733
733
|
label?: string
|
|
734
734
|
className?: string
|
|
735
|
-
/** Les entrées du panneau
|
|
735
|
+
/** Les entrées du panneau : des `CompteLien`. */
|
|
736
736
|
children?: ReactNode
|
|
737
737
|
}
|
|
738
738
|
|
|
@@ -743,7 +743,7 @@ export interface CompteProps extends Div {
|
|
|
743
743
|
*
|
|
744
744
|
* ⚠️ Le panneau n'est pas un `role="menu"` : ce rôle promet une navigation
|
|
745
745
|
* aux flèches que le CSS seul ne peut pas tenir. Ce sont des liens dans un
|
|
746
|
-
* panneau, atteignables par la tabulation
|
|
746
|
+
* panneau, atteignables par la tabulation : une promesse tenue vaut mieux
|
|
747
747
|
* qu'un rôle exact et menteur. */
|
|
748
748
|
export function Compte({
|
|
749
749
|
utilisateur,
|
|
@@ -799,7 +799,7 @@ export function Compte({
|
|
|
799
799
|
}
|
|
800
800
|
|
|
801
801
|
/** Une entrée du panneau de compte. Rendue en `<a>` avec `href`, en
|
|
802
|
-
* `<button>` sans
|
|
802
|
+
* `<button>` sans : se déconnecter est une ACTION, pas une navigation, et
|
|
803
803
|
* un lien qui n'en est pas un se copie, s'ouvre dans un onglet et casse. */
|
|
804
804
|
export function CompteLien({
|
|
805
805
|
sortie,
|
package/react/navigation.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// Navigation applicative
|
|
1
|
+
// Navigation applicative : colonne latérale, barre supérieure, fil d'Ariane,
|
|
2
2
|
// menu, pagination, feuille de source, zones défilantes.
|
|
3
3
|
//
|
|
4
4
|
// Rien ici n'a d'état propre : l'entrée courante, la page courante, l'item
|
|
5
|
-
// sélectionné viennent de l'appelant. Aucun « use client », donc
|
|
5
|
+
// sélectionné viennent de l'appelant. Aucun « use client », donc : c'est ce
|
|
6
6
|
// qui permet à une colonne de navigation Next de rester rendue au serveur.
|
|
7
7
|
//
|
|
8
8
|
// ⚠️ Les OVERLAYS (tiroir, modale, notification) sont dans overlays.tsx : eux
|
|
@@ -26,7 +26,7 @@ type Btn = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'className'>
|
|
|
26
26
|
/* ─── Colonne latérale ─────────────────────────────────────────────── */
|
|
27
27
|
|
|
28
28
|
/** Une colonne, un filet, rien d'autre : ni ombre, ni rayon. Une ombre ferait
|
|
29
|
-
* flotter une pièce qui ne bouge jamais. Elle défile pour son propre compte
|
|
29
|
+
* flotter une pièce qui ne bouge jamais. Elle défile pour son propre compte :
|
|
30
30
|
* une nav longue n'entraîne jamais le contenu. */
|
|
31
31
|
export function Sidebar({ className, ...reste }: Div & { className?: string }) {
|
|
32
32
|
return <aside className={cx('zv-sidebar', className)} {...reste} />
|
|
@@ -61,7 +61,7 @@ export interface SideLinkProps extends A {
|
|
|
61
61
|
/** Pose `aria-current="page"` en plus de la classe : sans lui, l'entrée
|
|
62
62
|
* courante est marquée pour l'œil et muette au lecteur d'écran. */
|
|
63
63
|
courant?: boolean
|
|
64
|
-
/** Rang mono ou glyphe, sur une colonne de largeur fixe
|
|
64
|
+
/** Rang mono ou glyphe, sur une colonne de largeur fixe : sinon les
|
|
65
65
|
* libellés se décalent d'un groupe à l'autre. */
|
|
66
66
|
num?: ReactNode
|
|
67
67
|
className?: string
|
|
@@ -85,7 +85,7 @@ export function SideLink({ courant, num, className, children, ...reste }: SideLi
|
|
|
85
85
|
|
|
86
86
|
/** Elle REMPLACE la colonne sous le point de rupture : masquée au-dessus,
|
|
87
87
|
* elle n'apparaît que dans le bloc mobile du CSS. Ne pas la conditionner en
|
|
88
|
-
* JavaScript
|
|
88
|
+
* JavaScript : le rendu serveur ne connaît pas la largeur de l'écran. */
|
|
89
89
|
export function Topbar({ className, ...reste }: Div & { className?: string }) {
|
|
90
90
|
return <header className={cx('zv-topbar', className)} {...reste} />
|
|
91
91
|
}
|
|
@@ -104,7 +104,7 @@ export function TopbarBurger({ className, type = 'button', ...reste }: Btn & { c
|
|
|
104
104
|
return <button type={type} className={cx('zv-topbar__burger', className)} {...reste} />
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/** Croix bordée du chrome
|
|
107
|
+
/** Croix bordée du chrome : fermeture d'un tiroir, d'une feuille de source.
|
|
108
108
|
* Angles vifs : le rayon signale l'action, le chrome n'en est pas une. */
|
|
109
109
|
export function ChromeButton({
|
|
110
110
|
className,
|
|
@@ -125,7 +125,7 @@ export interface CrumbsProps extends Omit<A, 'children'> {
|
|
|
125
125
|
className?: string
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
/** ⚠️ Le fil ENTIER est cliquable
|
|
128
|
+
/** ⚠️ Le fil ENTIER est cliquable : c'est un seul nœud, pas une liste de
|
|
129
129
|
* liens. Sans `href`, il se rend en `<span>` : un `<a>` sans cible reste
|
|
130
130
|
* annoncé comme un lien et déçoit. */
|
|
131
131
|
export function Crumbs({
|
|
@@ -159,7 +159,7 @@ export function Crumbs({
|
|
|
159
159
|
/* ─── Menu déroulant ───────────────────────────────────────────────── */
|
|
160
160
|
|
|
161
161
|
/** À poser dans un `SelectAnchor` (forms) : c'est l'enveloppe qui ancre le
|
|
162
|
-
* menu, pas le déclencheur
|
|
162
|
+
* menu, pas le déclencheur, l'ancrer sur le déclencheur casse sa largeur
|
|
163
163
|
* maximale calculée sur la fenêtre. */
|
|
164
164
|
export function Menu({ className, ...reste }: Div & { className?: string; role?: string }) {
|
|
165
165
|
return <div role="listbox" className={cx('zv-menu', className)} {...reste} />
|
|
@@ -185,13 +185,13 @@ export function MenuItem({ actif, className, type = 'button', ...reste }: MenuIt
|
|
|
185
185
|
|
|
186
186
|
/* ─── Popover ──────────────────────────────────────────────────────── */
|
|
187
187
|
|
|
188
|
-
/** Surface ancrée LIBRE
|
|
188
|
+
/** Surface ancrée LIBRE : aide contextuelle, mini-formulaire de filtre.
|
|
189
189
|
* Même ancre que le menu : à poser dans un `SelectAnchor`, rendu à
|
|
190
190
|
* l'ouverture seulement (`{ouvert && <Popover>…}`). Ce n'est PAS une
|
|
191
191
|
* infobulle : le popover s'ouvre au CLIC et peut porter de l'interactif ;
|
|
192
192
|
* l'infobulle (retours) se montre au survol et n'en porte jamais.
|
|
193
193
|
* ⚠️ Fermeture par clic extérieur et Échap : au composant appelant, comme
|
|
194
|
-
* pour le menu
|
|
194
|
+
* pour le menu, et le déclencheur porte aria-expanded. */
|
|
195
195
|
export function Popover({ className, ...reste }: Div & { className?: string }) {
|
|
196
196
|
return <div className={cx('zv-popover', className)} {...reste} />
|
|
197
197
|
}
|
|
@@ -202,9 +202,9 @@ export interface TabsProps<T extends string> {
|
|
|
202
202
|
onglets: readonly { id: T; label: ReactNode; compte?: ReactNode }[]
|
|
203
203
|
actif: T
|
|
204
204
|
onChange: (id: T) => void
|
|
205
|
-
/** « puces » (défaut) : la rangée de Seg
|
|
206
|
-
* restreignent une collection. « soulignes » : l'onglet au trait
|
|
207
|
-
* arbitré le 25/08/2026 (2 px, accent)
|
|
205
|
+
/** « puces » (défaut) : la rangée de Seg, le motif des FILTRES, qui
|
|
206
|
+
* restreignent une collection. « soulignes » : l'onglet au trait (
|
|
207
|
+
* arbitré le 25/08/2026 (2 px, accent)) pour les VUES d'un même objet
|
|
208
208
|
* (Aperçu / Sources). */
|
|
209
209
|
variante?: 'puces' | 'soulignes'
|
|
210
210
|
/** Rangée qui défile au lieu de passer à la ligne (variante puces ; la
|
|
@@ -216,7 +216,7 @@ export interface TabsProps<T extends string> {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/** Deux dessins, une seule grammaire d'état. En puces, ce composant n'ajoute
|
|
219
|
-
* que l'ARIA au-dessus de `Seg` ; en soulignés, il pose `.zv-tabs-line`
|
|
219
|
+
* que l'ARIA au-dessus de `Seg` ; en soulignés, il pose `.zv-tabs-line` :
|
|
220
220
|
* l'arbitrage longtemps en attente, tranché le 25/08/2026 (DECISIONS.md).
|
|
221
221
|
* Les deux portent le contrat clavier du rôle : la rangée est UNE étape de
|
|
222
222
|
* tabulation (roving tabindex), les flèches passent d'un onglet à l'autre
|
|
@@ -371,7 +371,7 @@ export function CitePanel({
|
|
|
371
371
|
|
|
372
372
|
export interface ScrollProps extends Div {
|
|
373
373
|
/** Barre fine, aux couleurs du système. OPT-IN : par défaut la barre native
|
|
374
|
-
* est conservée
|
|
374
|
+
* est conservée, sur macOS et iOS, l'habiller la rend permanente et elle
|
|
375
375
|
* mange une colonne. */
|
|
376
376
|
habillee?: boolean
|
|
377
377
|
className?: string
|
|
@@ -394,14 +394,14 @@ export function Scroll({ habillee, className, ...reste }: ScrollProps) {
|
|
|
394
394
|
|
|
395
395
|
export interface TabbarProps extends Div {
|
|
396
396
|
/** Le nom de la barre (« Sections ») : un <nav> anonyme de plus est une
|
|
397
|
-
* énigme au lecteur d'écran
|
|
397
|
+
* énigme au lecteur d'écran, même règle que le label de `Carousel`. */
|
|
398
398
|
label: string
|
|
399
399
|
className?: string
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
/** Barre basse de navigation, pour une application à trois ou quatre
|
|
403
403
|
* sections. N'existe que SOUS le point de rupture : au-dessus, la colonne
|
|
404
|
-
* latérale est là. CINQ entrées au maximum
|
|
404
|
+
* latérale est là. CINQ entrées au maximum : au-delà, c'est le tiroir.
|
|
405
405
|
* ⚠️ Elle est fixe : le gabarit qui la pose doit donner au contenu un
|
|
406
406
|
* padding-bottom à sa hauteur, sinon la dernière ligne se cache dessous. */
|
|
407
407
|
export function Tabbar({ label, className, ...reste }: TabbarProps) {
|
|
@@ -411,7 +411,7 @@ export function Tabbar({ label, className, ...reste }: TabbarProps) {
|
|
|
411
411
|
export interface TabbarItemProps extends A {
|
|
412
412
|
/** Section courante : pose `aria-current="page"`, que le CSS suit. */
|
|
413
413
|
courant?: boolean
|
|
414
|
-
/** L'icône au-dessus du libellé
|
|
414
|
+
/** L'icône au-dessus du libellé : un `<Icon>` du paquet. */
|
|
415
415
|
icone?: ReactNode
|
|
416
416
|
className?: string
|
|
417
417
|
children?: ReactNode
|
package/react/overlays.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
// Enrobages
|
|
3
|
+
// Enrobages : voile, tiroir, modale, notification flottante.
|
|
4
4
|
//
|
|
5
5
|
// SEUL fichier du paquet marqué « use client », avec un hook. Tout ce qui vit
|
|
6
6
|
// ici vole le regard à la page : il faut une touche Échap, un piège de focus
|
|
7
7
|
// et un verrou de défilement, et rien de cela ne s'écrit en CSS.
|
|
8
8
|
//
|
|
9
9
|
// ⚠️ Ne PAS écrire `data-open={ouvert}` en React : cela rend `data-open="false"`,
|
|
10
|
-
// que le sélecteur d'attribut du CSS considère comme PRÉSENT
|
|
10
|
+
// que le sélecteur d'attribut du CSS considère comme PRÉSENT : le panneau
|
|
11
11
|
// resterait ouvert. Ces composants emploient la classe `--open`, prévue pour ça.
|
|
12
12
|
|
|
13
13
|
import { useCallback, useEffect, useRef } from 'react'
|
|
@@ -22,7 +22,7 @@ const FOCUSABLES =
|
|
|
22
22
|
export interface UseOverlayOptions {
|
|
23
23
|
ouvert: boolean
|
|
24
24
|
onClose: () => void
|
|
25
|
-
/** Le nœud à piéger. Sans lui, pas de piège
|
|
25
|
+
/** Le nœud à piéger. Sans lui, pas de piège : mais Échap fonctionne quand
|
|
26
26
|
* même. */
|
|
27
27
|
panneau?: RefObject<HTMLElement | null>
|
|
28
28
|
/** Verrouille le défilement du document. Vrai pour une modale, vrai pour un
|
|
@@ -46,7 +46,7 @@ export function useOverlay({ ouvert, onClose, panneau, verrouille = true }: UseO
|
|
|
46
46
|
// ⚠️ Le document du PANNEAU, pas `document` : un enrobage peut vivre dans
|
|
47
47
|
// un autre document que celui du script (planche de composants rendue dans
|
|
48
48
|
// un iframe, fenêtre détachée). Écouter `document` y poserait la touche
|
|
49
|
-
// Échap sur la page hôte
|
|
49
|
+
// Échap sur la page hôte : elle fermerait la modale d'à côté et pas
|
|
50
50
|
// celle-ci. Sans panneau, on retombe sur le document courant.
|
|
51
51
|
const docu = cible?.ownerDocument ?? document
|
|
52
52
|
origine.current = docu.activeElement as HTMLElement | null
|
|
@@ -105,7 +105,7 @@ export function Scrim({ className, ...reste }: Div & { className?: string }) {
|
|
|
105
105
|
export interface DrawerProps extends Omit<Div, 'title'> {
|
|
106
106
|
ouvert: boolean
|
|
107
107
|
onClose: () => void
|
|
108
|
-
/** Nom du dialogue
|
|
108
|
+
/** Nom du dialogue : ce que le lecteur d'écran annonce à l'ouverture. */
|
|
109
109
|
label?: string
|
|
110
110
|
/** Croix de fermeture posée en absolu par-dessus la tête du panneau. */
|
|
111
111
|
fermeture?: ReactNode
|
|
@@ -114,7 +114,7 @@ export interface DrawerProps extends Omit<Div, 'title'> {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/** ⚠️ Le tiroir n'existe QUE sous le point de rupture : au-dessus, la
|
|
117
|
-
* navigation est une colonne et il n'y a rien à ouvrir. Le CSS s'en charge
|
|
117
|
+
* navigation est une colonne et il n'y a rien à ouvrir. Le CSS s'en charge :
|
|
118
118
|
* ne pas conditionner le rendu sur la largeur en JavaScript, le serveur ne
|
|
119
119
|
* la connaît pas. */
|
|
120
120
|
export function Drawer({
|
|
@@ -156,11 +156,11 @@ export interface ModalProps extends Omit<Div, 'title'> {
|
|
|
156
156
|
titre?: ReactNode
|
|
157
157
|
/** Actions de tête, à droite du titre (la croix, en général). */
|
|
158
158
|
actions?: ReactNode
|
|
159
|
-
/** Pied d'actions. Il reste visible quand le corps défile
|
|
159
|
+
/** Pied d'actions. Il reste visible quand le corps défile : c'est tout
|
|
160
160
|
* l'intérêt du plafond de hauteur. */
|
|
161
161
|
pied?: ReactNode
|
|
162
162
|
/** Modale destructive : le filet HAUT porte l'avertissement, pas un fond
|
|
163
|
-
* rouge
|
|
163
|
+
* rouge, une surface d'alerte pleine rendrait le texte pénible à lire. */
|
|
164
164
|
danger?: boolean
|
|
165
165
|
className?: string
|
|
166
166
|
children?: ReactNode
|
|
@@ -231,7 +231,7 @@ export interface ToastProps extends Div {
|
|
|
231
231
|
|
|
232
232
|
/** ⚠️ Ne remplace PAS `Alert` : une erreur de formulaire se dit à côté du
|
|
233
233
|
* champ, jamais dans un coin qui disparaît. Réserver ce composant à ce qui
|
|
234
|
-
* est vrai APRÈS COUP
|
|
234
|
+
* est vrai APRÈS COUP : « copié », « enregistré », « envoyé ». */
|
|
235
235
|
export function Toast({ ton, className, ...reste }: ToastProps) {
|
|
236
236
|
return <div className={cx('zv-toast', ton && `zv-toast--${ton}`, className)} {...reste} />
|
|
237
237
|
}
|
package/react/select-menu.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
// Sélection DESSINÉE
|
|
3
|
+
// Sélection DESSINÉE : le déclencheur a l'apparence d'un champ, et la liste
|
|
4
4
|
// est peinte par le paquet.
|
|
5
5
|
//
|
|
6
6
|
// ⚠️ POURQUOI CE COMPOSANT EXISTE, ALORS QUE `Select` (natif) EST LÀ.
|
|
7
7
|
// Un <select> natif ne peut pas être habillé : sa liste déroulée est peinte
|
|
8
8
|
// par le SYSTÈME, pas par la page. Sur un macOS en mode sombre elle sort en
|
|
9
|
-
// menu noir au milieu d'une interface claire
|
|
9
|
+
// menu noir au milieu d'une interface claire : aucun CSS ne corrige cela.
|
|
10
10
|
//
|
|
11
11
|
// ⚠️ CE QUE ÇA COÛTE, ET QUI EST PAYÉ ICI. En remplaçant le contrôle natif,
|
|
12
12
|
// on perd le clavier, l'annonce au lecteur d'écran et la liste système. Tout
|
|
@@ -39,7 +39,7 @@ export interface SelectMenuProps<T extends string = string> {
|
|
|
39
39
|
onChange: (valeur: T) => void
|
|
40
40
|
/** Affiché quand rien n'est choisi. */
|
|
41
41
|
placeholder?: string
|
|
42
|
-
/** Identifiant du déclencheur
|
|
42
|
+
/** Identifiant du déclencheur : à faire correspondre au `htmlFor` du champ. */
|
|
43
43
|
id?: string
|
|
44
44
|
/** Nom du contrôle, quand aucun intitulé visible ne le désigne. */
|
|
45
45
|
label?: string
|
package/react/surfaces.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Surfaces
|
|
1
|
+
// Surfaces : halos, sols quadrillés, voiles de fondu, bandeau manifeste,
|
|
2
2
|
// glyphes. C'est ce qui distingue une page Zevra d'une page blanche bien
|
|
3
3
|
// composée.
|
|
4
4
|
//
|
|
5
5
|
// ⚠️ TOUT est DÉCORATIF : posé en absolu derrière le contenu, sans capter le
|
|
6
6
|
// pointeur, et masqué aux lecteurs d'écran. Ces composants posent
|
|
7
|
-
// `aria-hidden` eux-mêmes
|
|
7
|
+
// `aria-hidden` eux-mêmes : ne pas y mettre de texte à lire.
|
|
8
8
|
//
|
|
9
9
|
// ⚠️ Tous supposent un parent `position: relative; overflow: hidden`. Sans le
|
|
10
10
|
// second, un halo de 1400px de large fait déborder la page.
|
|
@@ -17,7 +17,7 @@ type Div = Omit<HTMLAttributes<HTMLDivElement>, 'className'>
|
|
|
17
17
|
/* ─── Halo ambiant ─────────────────────────────────────────────────── */
|
|
18
18
|
|
|
19
19
|
/** Le violet et le cyan du spectre, très dilués. Ce n'est PAS un emploi du
|
|
20
|
-
* spectre en aplat : c'est une lumière
|
|
20
|
+
* spectre en aplat : c'est une lumière, elle ne compte pas dans les trois
|
|
21
21
|
* emplois autorisés. */
|
|
22
22
|
export function Halo({
|
|
23
23
|
place,
|
|
@@ -101,7 +101,7 @@ export function PanelNavy({ className, ...reste }: Div & { className?: string })
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/** Repeint un texte pour une surface sombre. `Band` le fait déjà pour son
|
|
104
|
-
* contenu courant
|
|
104
|
+
* contenu courant : cette classe sert aux cas isolés. */
|
|
105
105
|
export function TextOnDark({ className, ...reste }: Div & { className?: string }) {
|
|
106
106
|
return <div className={cx('zv-text-on-dark', className)} {...reste} />
|
|
107
107
|
}
|
|
@@ -143,15 +143,15 @@ export type Animation =
|
|
|
143
143
|
| 'dot-active'
|
|
144
144
|
| 'progress'
|
|
145
145
|
|
|
146
|
-
/** Les neuf animations du paquet, nommées
|
|
146
|
+
/** Les neuf animations du paquet, nommées : plus un dosage. ⚠️ Elles sont
|
|
147
147
|
* pilotées AU DÉFILEMENT et réservées à la vitrine : le chrome applicatif
|
|
148
148
|
* n'anime rien. `prefers-reduced-motion` les coupe toutes, globalement,
|
|
149
149
|
* depuis base.css.
|
|
150
150
|
*
|
|
151
151
|
* `option` échelonne les entrées d'une même rangée (`pop`, rangs 2 à 6),
|
|
152
|
-
* donne le coin d'arrivée (`converge` : tr, bl, br
|
|
152
|
+
* donne le coin d'arrivée (`converge` : tr, bl, br (le défaut vient du
|
|
153
153
|
* haut-gauche), ou DOSE l'entrée (`rise` + « doux » : douze pixels au
|
|
154
|
-
* lieu de trente-deux, pour les blocs des pages intérieures
|
|
154
|
+
* lieu de trente-deux, pour les blocs des pages intérieures) la home
|
|
155
155
|
* garde l'entrée pleine). Passée à une autre animation, elle est
|
|
156
156
|
* ignorée. */
|
|
157
157
|
export function anim(
|
|
@@ -170,10 +170,10 @@ export function anim(
|
|
|
170
170
|
export interface SocialFrameProps extends Div {
|
|
171
171
|
/** paysage 1200×630 (OpenGraph, lien LinkedIn) · carre 1080×1080 (post) ·
|
|
172
172
|
* story 1080×1920. FIGÉS en pixels : ce sont des planches qu'on CAPTURE,
|
|
173
|
-
* pas des pages
|
|
173
|
+
* pas des pages, elles ignorent le point de rupture, et c'est voulu. */
|
|
174
174
|
format?: 'paysage' | 'carre' | 'story'
|
|
175
175
|
/** La planche sombre : le manifeste en fond. Les règles du fond sombre
|
|
176
|
-
* s'appliquent
|
|
176
|
+
* s'appliquent : labels en `Mono ton="on-dark"`, jamais les neutres
|
|
177
177
|
* inversés. */
|
|
178
178
|
manifeste?: boolean
|
|
179
179
|
/** Le pied : wordmark à gauche, adresse en mono à droite. */
|
|
@@ -184,7 +184,7 @@ export interface SocialFrameProps extends Div {
|
|
|
184
184
|
|
|
185
185
|
/** Planche d'export pour les réseaux. ⚠️ La règle « jamais de faux
|
|
186
186
|
* contenu » s'y applique au premier chef : un visuel social EST une
|
|
187
|
-
* publication
|
|
187
|
+
* publication, pas de chiffre inventé, pas de citation fabriquée. */
|
|
188
188
|
export function SocialFrame({
|
|
189
189
|
format = 'paysage',
|
|
190
190
|
manifeste,
|
package/react/typography.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// Typographie
|
|
1
|
+
// Typographie : titres, corps, kickers, labels mono, filets.
|
|
2
2
|
//
|
|
3
3
|
// La HIÉRARCHIE VISUELLE et le NIVEAU SÉMANTIQUE sont deux choses : une page
|
|
4
4
|
// n'a qu'un <h1>, mais elle peut avoir plusieurs blocs à la taille du h1.
|
|
5
|
-
// D'où `as` sur les titres
|
|
5
|
+
// D'où `as` sur les titres : la taille est un choix de mise en page, la
|
|
6
6
|
// balise un choix de structure. Les confondre casse le plan du document pour
|
|
7
7
|
// un lecteur d'écran sans que rien ne se voie à l'écran.
|
|
8
8
|
|
|
@@ -59,7 +59,7 @@ export const CardTitle = (p: Omit<HeadingProps, 'taille'>) => (
|
|
|
59
59
|
<Heading taille="carte" {...p} />
|
|
60
60
|
)
|
|
61
61
|
|
|
62
|
-
/** Chiffre ou mot posé pour sa PRÉSENCE, hors de la hiérarchie des titres
|
|
62
|
+
/** Chiffre ou mot posé pour sa PRÉSENCE, hors de la hiérarchie des titres,
|
|
63
63
|
* d'où le `<div>` : un chiffre géant annoncé comme titre de section pollue
|
|
64
64
|
* le plan du document. */
|
|
65
65
|
export function Display({ className, ...reste }: Div & { className?: string }) {
|
|
@@ -131,7 +131,7 @@ export function Em({
|
|
|
131
131
|
* `AnchorHTMLAttributes` et non `HTMLAttributes` : sans quoi ils refusent
|
|
132
132
|
* `target`, `rel`, `download` et `hreflang`. Un lien sortant ne peut alors
|
|
133
133
|
* pas porter `rel="noopener"`, et un lien qui doit s'ouvrir à côté ne le
|
|
134
|
-
* peut pas non plus
|
|
134
|
+
* peut pas non plus : une app consommatrice a dû écrire la classe à la main
|
|
135
135
|
* pour cette seule raison. (0.19.0) */
|
|
136
136
|
export function Link({
|
|
137
137
|
className,
|
|
@@ -207,14 +207,14 @@ export interface MonoProps extends Span {
|
|
|
207
207
|
/** Le ton, pas la taille : `label` est le gris d'intitulé, `ink` l'encre
|
|
208
208
|
* pleine, `muted` le gris secondaire, `on-dark` la bande manifeste. */
|
|
209
209
|
ton?: 'label' | 'ink' | 'muted' | 'on-dark'
|
|
210
|
-
/** Le plus petit palier
|
|
210
|
+
/** Le plus petit palier : badge dense, compteur de coque. */
|
|
211
211
|
micro?: boolean
|
|
212
212
|
className?: string
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
/** Capitales espacées. ⚠️ Réservé aux LIBELLÉS : une date ou un nom de
|
|
216
216
|
* fichier passés en capitales espacées deviennent illisibles et s'alignent
|
|
217
|
-
* mal
|
|
217
|
+
* mal, pour ceux-là, c'est `Meta` (bas de casse, chasse tabulaire). */
|
|
218
218
|
export function Mono({ ton, micro, className, ...reste }: MonoProps) {
|
|
219
219
|
return (
|
|
220
220
|
<span
|
package/src/base.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/* Zevra UI
|
|
1
|
+
/* Zevra UI, socle : reset, document, typographie, signature
|
|
2
2
|
* Dépend de tokens.css. À charger en premier.
|
|
3
3
|
*
|
|
4
4
|
* Chaque valeur ci-dessous est relevée dans les maquettes de la passation du
|
|
5
5
|
* 23/08/2026. Quand deux planches divergent, l'arbitrage est écrit dans
|
|
6
|
-
* DECISIONS.md
|
|
6
|
+
* DECISIONS.md : jamais tranché en silence.
|
|
7
7
|
*
|
|
8
8
|
* POINT DE RUPTURE : 768px. Il ne vient PAS du relevé (les cinq maquettes sont
|
|
9
9
|
* à largeur fixe, sans une seule media query). C'est la convention `md` du
|
|
@@ -32,7 +32,7 @@ body {
|
|
|
32
32
|
/* ─── Liens ─────────────────────────────────────────────────────────────
|
|
33
33
|
* Restreint aux liens SANS classe. Les maquettes stylent `a` nu, ce qui
|
|
34
34
|
* repeint le libellé des boutons au survol (a:hover, 0-1-1, l'emporte sur
|
|
35
|
-
* .zv-btn, 0-1-0)
|
|
35
|
+
* .zv-btn, 0-1-0) : bug visible dans Main.html et signalé au relevé. Le
|
|
36
36
|
* périmètre est réduit plutôt que neutralisé par un :hover inventé. */
|
|
37
37
|
|
|
38
38
|
a:not([class]) {
|
|
@@ -55,7 +55,7 @@ a:not([class]):hover {
|
|
|
55
55
|
|
|
56
56
|
/* ─── Accessibilité ─────────────────────────────────────────────────────
|
|
57
57
|
* HORS RELEVÉ, et assumé : aucune des cinq maquettes ne déclare d'état de
|
|
58
|
-
* focus. Un système livré sans focus clavier est inutilisable au clavier
|
|
58
|
+
* focus. Un système livré sans focus clavier est inutilisable au clavier :
|
|
59
59
|
* on ne réplique pas cette lacune. Seules des valeurs existantes sont
|
|
60
60
|
* employées. */
|
|
61
61
|
|
|
@@ -64,7 +64,7 @@ a:not([class]):hover {
|
|
|
64
64
|
outline-offset: 2px;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/* Texte pour lecteurs d'écran seulement
|
|
67
|
+
/* Texte pour lecteurs d'écran seulement : la géométrie standard du motif,
|
|
68
68
|
* aucune valeur de charte. HORS RELEVÉ (25/08/2026) : ajouté avec la roue,
|
|
69
69
|
* dont le libellé d'annonce doit être un TEXTE dans le flux (un aria-label
|
|
70
70
|
* sur un span vide n'est pas annoncé par tous les lecteurs). */
|
|
@@ -161,7 +161,7 @@ a:not([class]):hover {
|
|
|
161
161
|
color: var(--accent-deep);
|
|
162
162
|
font-weight: 400;
|
|
163
163
|
/* Le composant `Em` rend un vrai <em>, que le navigateur penche par
|
|
164
|
-
* défaut
|
|
164
|
+
* défaut, et AUCUNE italique n'est chargée : Outfit n'en a pas, il en
|
|
165
165
|
* fabrique donc une oblique de synthèse. Sur un titre de 76px, ça se voit.
|
|
166
166
|
* La charte ne demande à cette classe qu'un changement de couleur et de
|
|
167
167
|
* graisse ; on remet donc la droite. (`.zv-inset em` portait déjà cette
|
|
@@ -220,7 +220,7 @@ a:not([class]):hover {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/* 12,5px : la note d'accompagnement d'un bloc ou d'une carte de
|
|
223
|
-
* documentation. Le plus petit palier de texte courant du système
|
|
223
|
+
* documentation. Le plus petit palier de texte courant du système : en
|
|
224
224
|
* dessous, on passe en label capitales. */
|
|
225
225
|
.zv-note {
|
|
226
226
|
margin: 0;
|
|
@@ -268,7 +268,7 @@ a:not([class]):hover {
|
|
|
268
268
|
|
|
269
269
|
/* Accroche produit, posée sous le nom d'un glyphe : « Votre savoir,
|
|
270
270
|
* interrogeable en une phrase ». Moins interlettrée que le label qu'elle
|
|
271
|
-
* suit (0.14em contre 0.2em)
|
|
271
|
+
* suit (0.14em contre 0.2em) : sinon la phrase se disloque. */
|
|
272
272
|
.zv-tagline {
|
|
273
273
|
font-family: var(--font-mono);
|
|
274
274
|
text-transform: uppercase;
|
|
@@ -296,7 +296,7 @@ a:not([class]):hover {
|
|
|
296
296
|
width: var(--spec-filet);
|
|
297
297
|
height: 2px;
|
|
298
298
|
background: var(--spectre);
|
|
299
|
-
/* Angle vif comme tout le reste depuis le 25/08
|
|
299
|
+
/* Angle vif comme tout le reste depuis le 25/08 : sur 2px de haut, le
|
|
300
300
|
* rayon d'1px du relevé ne se voyait de toute façon pas. */
|
|
301
301
|
border-radius: 0;
|
|
302
302
|
}
|
|
@@ -361,7 +361,7 @@ a:not([class]):hover {
|
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
/* .zv-body garde 15px : c'est déjà la taille du chapô mobile. Seule
|
|
364
|
-
* l'interligne se resserre
|
|
364
|
+
* l'interligne se resserre : et --dense reste distinct, à 13px/1.45. */
|
|
365
365
|
.zv-body {
|
|
366
366
|
line-height: 1.55;
|
|
367
367
|
}
|