@zevra/ui 0.17.0 → 0.21.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 +300 -0
- package/dist/catalogue.d.ts.map +1 -1
- package/dist/catalogue.js +76 -2
- package/dist/catalogue.js.map +1 -1
- package/dist/content.d.ts +127 -5
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +101 -5
- package/dist/content.js.map +1 -1
- package/dist/css-inline.d.ts.map +1 -1
- package/dist/css-inline.js +1 -1
- package/dist/css-inline.js.map +1 -1
- package/dist/forms.d.ts +22 -0
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +18 -0
- package/dist/forms.js.map +1 -1
- package/dist/icon-data.d.ts +1 -1
- package/dist/icon-data.d.ts.map +1 -1
- package/dist/icon-data.js +30 -0
- package/dist/icon-data.js.map +1 -1
- package/dist/layout.d.ts +11 -5
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js.map +1 -1
- package/dist/typography.d.ts +8 -3
- package/dist/typography.d.ts.map +1 -1
- package/dist/typography.js +6 -0
- package/dist/typography.js.map +1 -1
- package/icons/index.json +90 -0
- package/icons/sprite.svg +15 -0
- package/package.json +1 -1
- package/react/catalogue.tsx +109 -3
- package/react/content.tsx +320 -9
- package/react/css-inline.ts +1 -1
- package/react/forms.tsx +53 -0
- package/react/icon-data.ts +45 -0
- package/react/layout.tsx +11 -4
- package/react/typography.tsx +13 -3
- package/src/base.css +7 -0
- package/src/components.css +344 -11
- package/src/forms.css +65 -2
- package/src/icons.css +20 -0
- package/src/layout.css +39 -1
- package/src/navigation.css +20 -0
package/src/components.css
CHANGED
|
@@ -41,10 +41,34 @@
|
|
|
41
41
|
box-shadow 160ms cubic-bezier(0.2, 0.7, 0.2, 1);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
/* ─── Le plein ne s'applique QUE si aucune variante n'est posée ────────
|
|
45
|
+
*
|
|
46
|
+
* `zv-btn`, `zv-btn--ghost`, `zv-btn--ink` et `zv-btn--danger` sont quatre
|
|
47
|
+
* classes AUTONOMES et mutuellement exclusives : le socle commun ci-dessus
|
|
48
|
+
* les groupe dans un même sélecteur, ce qui leur donne exactement l'air
|
|
49
|
+
* d'une base et de ses modificateurs. `Button` n'en rend jamais qu'une —
|
|
50
|
+
* mais une app qui écrit ses classes à la main lit du BEM et compose les
|
|
51
|
+
* deux. C'est arrivé (mcp-factory, 0.19.0), et le résultat était vicieux :
|
|
52
|
+
* `.zv-btn:hover` (0,2,0) l'emportait sur `.zv-btn--ghost` (0,1,0), d'où un
|
|
53
|
+
* fond indigo sous un texte encre — illisible, et seulement AU SURVOL, donc
|
|
54
|
+
* invisible en relecture comme en capture.
|
|
55
|
+
*
|
|
56
|
+
* Le `:not(:where(…))` dit ce que le système voulait déjà dire : ces quatre
|
|
57
|
+
* classes s'excluent. `:where()` a une spécificité NULLE — la garde ne
|
|
58
|
+
* change donc aucun arbitrage existant, et une app qui surcharge `.zv-btn`
|
|
59
|
+
* continue de gagner exactement comme avant.
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ À reporter sur toute règle future qui peint le bouton plein. */
|
|
62
|
+
|
|
63
|
+
.zv-btn:not(:where(.zv-btn--ghost, .zv-btn--ink, .zv-btn--danger)) {
|
|
64
|
+
/* Le sommet du dégradé. RELEVÉ dans Main.html à #2b2bb4, mais DÉRIVÉ de
|
|
65
|
+
* --accent depuis la 0.19.0 : la déclaration étant portée par le bouton
|
|
66
|
+
* lui-même, une redéfinition de marque héritée d'un conteneur ne
|
|
67
|
+
* l'atteignait pas — un cabinet en marque blanche obtenait un dégradé
|
|
68
|
+
* allant de l'indigo Zevra à SA couleur. Pour la charte par défaut,
|
|
69
|
+
* l'écart avec la valeur relevée est d'un point de bleu, invisible.
|
|
70
|
+
* Les états de survol le dérivent ensuite par color-mix. */
|
|
71
|
+
--zv-btn-haut: var(--accent);
|
|
48
72
|
background: linear-gradient(180deg, var(--zv-btn-haut) 0%, var(--accent-deep) 100%);
|
|
49
73
|
color: var(--white);
|
|
50
74
|
/* Le liseré interne fait tout le galbe du bouton : il est dans Main.html
|
|
@@ -88,7 +112,7 @@
|
|
|
88
112
|
* chose.
|
|
89
113
|
* Déclarés AVANT [disabled], qui doit l'emporter à spécificité égale. */
|
|
90
114
|
|
|
91
|
-
.zv-btn:hover {
|
|
115
|
+
.zv-btn:not(:where(.zv-btn--ghost, .zv-btn--ink, .zv-btn--danger)):hover {
|
|
92
116
|
background: linear-gradient(
|
|
93
117
|
180deg,
|
|
94
118
|
color-mix(in oklab, var(--zv-btn-haut) 86%, var(--ink)) 0%,
|
|
@@ -96,7 +120,7 @@
|
|
|
96
120
|
);
|
|
97
121
|
}
|
|
98
122
|
|
|
99
|
-
.zv-btn:active {
|
|
123
|
+
.zv-btn:not(:where(.zv-btn--ghost, .zv-btn--ink, .zv-btn--danger)):active {
|
|
100
124
|
background: linear-gradient(
|
|
101
125
|
180deg,
|
|
102
126
|
color-mix(in oklab, var(--zv-btn-haut) 72%, var(--ink)) 0%,
|
|
@@ -177,8 +201,8 @@
|
|
|
177
201
|
* désactivé OU occupé (dont le clic est avalé par la façade) : sans ces
|
|
178
202
|
* règles, les états ci-dessus repeignent le fond et le bouton a l'air
|
|
179
203
|
* actif. Même précaution que les contrôles désactivés de forms.css. */
|
|
180
|
-
.zv-btn[disabled]:is(:hover, :active),
|
|
181
|
-
.zv-btn[aria-busy='true']:is(:hover, :active) {
|
|
204
|
+
.zv-btn:not(:where(.zv-btn--ghost, .zv-btn--ink, .zv-btn--danger))[disabled]:is(:hover, :active),
|
|
205
|
+
.zv-btn:not(:where(.zv-btn--ghost, .zv-btn--ink, .zv-btn--danger))[aria-busy='true']:is(:hover, :active) {
|
|
182
206
|
background: linear-gradient(180deg, var(--zv-btn-haut) 0%, var(--accent-deep) 100%);
|
|
183
207
|
}
|
|
184
208
|
|
|
@@ -612,8 +636,23 @@
|
|
|
612
636
|
/* ─── FAQ ───────────────────────────────────────────────────────────────
|
|
613
637
|
* Cinq questions par page au maximum. */
|
|
614
638
|
|
|
639
|
+
/* Le filet, comme `.zv-fold` : sans lui, la FAQ est un aplat blanc posé sur
|
|
640
|
+
* le papier, sans début ni fin. Toutes les autres surfaces blanches de la
|
|
641
|
+
* vitrine — carte, encart, tarif — en portent un ; celle-ci faisait tache. */
|
|
615
642
|
.zv-faq {
|
|
616
643
|
background: var(--white);
|
|
644
|
+
border: 1px solid var(--line);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/* Chaque question est un `<details>` : le repli est natif, sans JavaScript.
|
|
648
|
+
* Le filet sépare les questions ENTRE elles ; la première n'en porte pas, il
|
|
649
|
+
* tomberait sur celui du bloc et ferait un trait double. */
|
|
650
|
+
.zv-faq__item {
|
|
651
|
+
border-top: 1px solid var(--hairline);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.zv-faq__item:first-child {
|
|
655
|
+
border-top: 0;
|
|
617
656
|
}
|
|
618
657
|
|
|
619
658
|
.zv-faq__q {
|
|
@@ -622,22 +661,54 @@
|
|
|
622
661
|
align-items: center;
|
|
623
662
|
gap: 16px;
|
|
624
663
|
padding: 18px 24px;
|
|
625
|
-
border-bottom: 1px solid var(--hairline);
|
|
626
664
|
font-size: 14px;
|
|
627
665
|
font-weight: 600;
|
|
628
666
|
cursor: pointer;
|
|
667
|
+
/* Le marqueur natif du `<summary>` tombe : le chevron du composant fait le
|
|
668
|
+
* même travail sans imposer le triangle du navigateur. Même traitement que
|
|
669
|
+
* `.zv-fold__titre`. */
|
|
670
|
+
list-style: none;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.zv-faq__q::-webkit-details-marker {
|
|
674
|
+
display: none;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/* Une FAQ de vitrine veut ses questions dans le PLAN DU DOCUMENT : c'est ce
|
|
678
|
+
* que lisent les moteurs, et ce que parcourt un lecteur d'écran qui saute de
|
|
679
|
+
* titre en titre. On accepte donc un `<h2>`/`<h3>`/`<h4>` comme question,
|
|
680
|
+
* et on lui retire ce que le navigateur lui met — taille, graisse, marges —
|
|
681
|
+
* pour qu'il se rende comme le reste des questions. Le niveau reste au
|
|
682
|
+
* balisage, l'apparence au système. */
|
|
683
|
+
.zv-faq__q > :is(h2, h3, h4) {
|
|
684
|
+
margin: 0;
|
|
685
|
+
font: inherit;
|
|
686
|
+
color: inherit;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.zv-faq__q:hover {
|
|
690
|
+
background: var(--panel);
|
|
629
691
|
}
|
|
630
692
|
|
|
631
|
-
|
|
693
|
+
/* Le chevron porte sa classe : cibler tout `svg` du titre attraperait aussi
|
|
694
|
+
* une icône posée dans le libellé, qui se verrait rétrécie et retournée. */
|
|
695
|
+
.zv-faq__chevron {
|
|
632
696
|
width: 14px;
|
|
633
697
|
height: 14px;
|
|
634
698
|
flex: none;
|
|
635
699
|
color: var(--accent);
|
|
700
|
+
transition: rotate 160ms cubic-bezier(0.2, 0.7, 0.2, 1);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.zv-faq__item[open] .zv-faq__chevron {
|
|
704
|
+
rotate: 180deg;
|
|
636
705
|
}
|
|
637
706
|
|
|
638
707
|
.zv-faq__a {
|
|
639
|
-
|
|
708
|
+
margin: 0;
|
|
709
|
+
padding: 0 24px 18px;
|
|
640
710
|
font-size: 13px;
|
|
711
|
+
line-height: 1.6;
|
|
641
712
|
color: var(--muted);
|
|
642
713
|
}
|
|
643
714
|
|
|
@@ -911,6 +982,13 @@
|
|
|
911
982
|
|
|
912
983
|
|
|
913
984
|
@media (max-width: 767px) {
|
|
985
|
+
|
|
986
|
+
/* Le `min-width: 260px` de la valeur ne tient pas sous le seuil : dans une
|
|
987
|
+
* carte de 215px — un écran de 320px — elle débordait de 45px. La CLÉ
|
|
988
|
+
* était déjà libérée (flex-basis 100%), il manquait la valeur. */
|
|
989
|
+
.zv-rows__val {
|
|
990
|
+
min-width: 0;
|
|
991
|
+
}
|
|
914
992
|
.zv-btn--sm {
|
|
915
993
|
height: 36px;
|
|
916
994
|
padding: 0 14px;
|
|
@@ -1029,3 +1107,258 @@
|
|
|
1029
1107
|
.zv-lockup--marque-seule .zv-lockup__word {
|
|
1030
1108
|
display: none;
|
|
1031
1109
|
}
|
|
1110
|
+
|
|
1111
|
+
/* ─── Cadre média ───────────────────────────────────────────────────────
|
|
1112
|
+
* HORS RELEVÉ (27/08/2026, 0.18.0). Aucune des dix planches ne montre
|
|
1113
|
+
* d'image de contenu — elles sont composées, pas photographiées. Le manque
|
|
1114
|
+
* était pourtant total : pas un `aspect-ratio`, pas un `object-fit` dans le
|
|
1115
|
+
* paquet, et quatre réécritures du même cadre chez un seul consommateur.
|
|
1116
|
+
*
|
|
1117
|
+
* ⚠️ Le rapport vit sur le CADRE, jamais sur l'image. Posé sur un enfant de
|
|
1118
|
+
* grille aligné au centre, il ne se résout pas et la colonne s'effondre à
|
|
1119
|
+
* zéro — c'est un correctif de production, pas une précaution théorique.
|
|
1120
|
+
*
|
|
1121
|
+
* Le fond est posé et non laissé transparent : une image qui tarde, échoue
|
|
1122
|
+
* ou porte de la transparence laisserait voir la surface du dessous, soit un
|
|
1123
|
+
* trou blanc sur une carte, soit un trou sombre sur une bande. */
|
|
1124
|
+
|
|
1125
|
+
.zv-media {
|
|
1126
|
+
position: relative;
|
|
1127
|
+
display: block;
|
|
1128
|
+
width: 100%;
|
|
1129
|
+
overflow: hidden;
|
|
1130
|
+
background: var(--hairline);
|
|
1131
|
+
/* Angle vif comme le reste ; aucune ombre, l'image est dans le flux. */
|
|
1132
|
+
border-radius: 0;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/* Les quatre rapports du système. Un cinquième se demande, il ne s'invente
|
|
1136
|
+
* pas : c'est ainsi qu'une bibliothèque finit avec dix-sept rapports dont
|
|
1137
|
+
* trois servent. */
|
|
1138
|
+
.zv-media--16-9 {
|
|
1139
|
+
aspect-ratio: 16 / 9;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
.zv-media--4-3 {
|
|
1143
|
+
aspect-ratio: 4 / 3;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
.zv-media--1-1 {
|
|
1147
|
+
aspect-ratio: 1 / 1;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.zv-media--3-2 {
|
|
1151
|
+
aspect-ratio: 3 / 2;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/* L'image REMPLIT le cadre : c'est le seul comportement qui le garantisse
|
|
1155
|
+
* plein quelle que soit la source, et une grille dont les couvertures ne
|
|
1156
|
+
* s'alignent pas se voit aussitôt. En absolu, pour que le cadre garde la
|
|
1157
|
+
* main sur sa hauteur. */
|
|
1158
|
+
.zv-media__img {
|
|
1159
|
+
position: absolute;
|
|
1160
|
+
inset: 0;
|
|
1161
|
+
width: 100%;
|
|
1162
|
+
height: 100%;
|
|
1163
|
+
object-fit: cover;
|
|
1164
|
+
display: block;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/* Le cas inverse — un logo, un schéma, une pièce qu'on ne peut pas rogner.
|
|
1168
|
+
* Le papier passe derrière : il restera du vide, autant qu'il soit choisi. */
|
|
1169
|
+
.zv-media--contient {
|
|
1170
|
+
background: var(--paper);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
.zv-media--contient .zv-media__img {
|
|
1174
|
+
object-fit: contain;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/* Le coin d'un cadre : durée d'une vidéo, nombre de pages, mention de
|
|
1178
|
+
* réserve. Encre diluée et non un gris opaque — le fond d'image est
|
|
1179
|
+
* imprévisible, et l'encre de charte s'y pose sans jurer. */
|
|
1180
|
+
.zv-media__coin {
|
|
1181
|
+
position: absolute;
|
|
1182
|
+
right: 8px;
|
|
1183
|
+
bottom: 8px;
|
|
1184
|
+
z-index: 1;
|
|
1185
|
+
display: inline-flex;
|
|
1186
|
+
align-items: center;
|
|
1187
|
+
gap: 4px;
|
|
1188
|
+
padding: 3px 7px;
|
|
1189
|
+
background: color-mix(in srgb, var(--ink) 72%, transparent);
|
|
1190
|
+
color: var(--white);
|
|
1191
|
+
font-family: var(--font-mono);
|
|
1192
|
+
font-size: var(--mono);
|
|
1193
|
+
font-weight: 500;
|
|
1194
|
+
letter-spacing: 0.08em;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
.zv-media__coin--gauche {
|
|
1198
|
+
right: auto;
|
|
1199
|
+
left: 8px;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.zv-media__coin--haut {
|
|
1203
|
+
bottom: auto;
|
|
1204
|
+
top: 8px;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/* Voile de lecture : un titre posé SUR une image n'est lisible qu'au-dessus
|
|
1208
|
+
* d'un dégradé. Il part du bas, là où le texte se pose, et n'assombrit pas
|
|
1209
|
+
* le sujet. Il ne capte pas le pointeur — le cadre reste cliquable. */
|
|
1210
|
+
.zv-media__voile {
|
|
1211
|
+
position: absolute;
|
|
1212
|
+
inset: 0;
|
|
1213
|
+
z-index: 1;
|
|
1214
|
+
pointer-events: none;
|
|
1215
|
+
background: linear-gradient(
|
|
1216
|
+
180deg,
|
|
1217
|
+
transparent 40%,
|
|
1218
|
+
color-mix(in srgb, var(--ink) 78%, transparent) 100%
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/* ─── Page légale ───────────────────────────────────────────────────────
|
|
1223
|
+
* HORS RELEVÉ (27/08/2026, voir DECISIONS.md). La forme d'un document
|
|
1224
|
+
* contractuel — CGVU, mentions légales, DPA. Le TEXTE, lui, n'est pas ici :
|
|
1225
|
+
* il vit dans @zevra/legal, versionné et archivé. Ce module ne porte que la
|
|
1226
|
+
* mise en page ; c'est ce qui permet au même texte de s'afficher sur la
|
|
1227
|
+
* vitrine et dans une application sans exister en deux exemplaires.
|
|
1228
|
+
*
|
|
1229
|
+
* Presque tout se compose de pièces existantes : le sommaire est .zv-rail,
|
|
1230
|
+
* les définitions .zv-rows, les tableaux .zv-table, les encarts .zv-callout.
|
|
1231
|
+
* Ne restent ici que la colonne de lecture et ce que les listes de texte
|
|
1232
|
+
* courant n'avaient jamais eu. */
|
|
1233
|
+
|
|
1234
|
+
.zv-legal {
|
|
1235
|
+
display: flex;
|
|
1236
|
+
flex-direction: column;
|
|
1237
|
+
gap: 48px;
|
|
1238
|
+
/* La MESURE, et c'est la seule règle qui compte vraiment pour un contrat :
|
|
1239
|
+
* au-delà d'environ 80 signes par ligne, l'œil perd le début de la ligne
|
|
1240
|
+
* suivante — sur un texte qu'on lit déjà à reculons, c'est rédhibitoire. */
|
|
1241
|
+
max-width: 72ch;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.zv-legal__section {
|
|
1245
|
+
display: flex;
|
|
1246
|
+
flex-direction: column;
|
|
1247
|
+
gap: 14px;
|
|
1248
|
+
/* Un lien vers un article précis ne doit pas déposer son titre sous la
|
|
1249
|
+
* barre fixe. Sans cette réserve, cliquer « Art. 7 » dans le sommaire
|
|
1250
|
+
* affiche le corps de l'article et cache son intitulé. */
|
|
1251
|
+
scroll-margin-top: 88px;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/* Les listes du texte courant. Le paquet n'en avait aucune : ses listes
|
|
1255
|
+
* étaient toutes des objets (étapes, sommaires numérotés, rangées à filets),
|
|
1256
|
+
* jamais l'énumération d'une clause. */
|
|
1257
|
+
.zv-legal__liste {
|
|
1258
|
+
margin: 0;
|
|
1259
|
+
padding-left: 22px;
|
|
1260
|
+
display: flex;
|
|
1261
|
+
flex-direction: column;
|
|
1262
|
+
gap: 8px;
|
|
1263
|
+
font-size: var(--body);
|
|
1264
|
+
line-height: 1.6;
|
|
1265
|
+
color: var(--muted);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
.zv-legal__liste li::marker {
|
|
1269
|
+
color: var(--accent-line);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/* Le renvoi vers un autre document contractuel, dans le fil d'une clause :
|
|
1273
|
+
* souligné, parce qu'à l'intérieur d'un paragraphe la couleur seule ne
|
|
1274
|
+
* suffit pas à annoncer qu'on change de contrat. */
|
|
1275
|
+
.zv-legal a {
|
|
1276
|
+
color: var(--accent);
|
|
1277
|
+
text-decoration: underline;
|
|
1278
|
+
text-underline-offset: 2px;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/* Le tableau d'un contrat (durées de conservation, sous-traitants). Il n'
|
|
1282
|
+
* emprunte PAS .zv-table de data.css : celui-là est une surface de travail
|
|
1283
|
+
* dont les rangées se survolent et se cliquent, ce qu'un tableau de contrat
|
|
1284
|
+
* ne fait jamais. Filets, chiffres alignés, et rien de plus. */
|
|
1285
|
+
.zv-legal__table {
|
|
1286
|
+
width: 100%;
|
|
1287
|
+
border-collapse: collapse;
|
|
1288
|
+
font-size: var(--app);
|
|
1289
|
+
line-height: 1.55;
|
|
1290
|
+
color: var(--ink-2);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
.zv-legal__table th {
|
|
1294
|
+
border-bottom: 1px solid var(--line);
|
|
1295
|
+
padding: 8px 12px 8px 0;
|
|
1296
|
+
text-align: left;
|
|
1297
|
+
font-family: var(--font-mono);
|
|
1298
|
+
font-size: var(--mono);
|
|
1299
|
+
font-weight: 600;
|
|
1300
|
+
letter-spacing: 0.14em;
|
|
1301
|
+
text-transform: uppercase;
|
|
1302
|
+
color: var(--label);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.zv-legal__table td {
|
|
1306
|
+
border-bottom: 1px solid var(--hairline);
|
|
1307
|
+
padding: 10px 12px 10px 0;
|
|
1308
|
+
vertical-align: top;
|
|
1309
|
+
font-variant-numeric: tabular-nums;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/* L'en-tête : titre, date d'entrée en vigueur, identifiant de version.
|
|
1313
|
+
* ⚠️ La VERSION est affichée, et ce n'est pas décoratif : c'est ce que
|
|
1314
|
+
* l'utilisateur cite quand il conteste, et ce que le support lui demande. */
|
|
1315
|
+
.zv-legal__tete {
|
|
1316
|
+
display: flex;
|
|
1317
|
+
flex-direction: column;
|
|
1318
|
+
gap: 10px;
|
|
1319
|
+
border-bottom: 1px solid var(--line);
|
|
1320
|
+
padding-bottom: 24px;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
@media (max-width: 767px) {
|
|
1324
|
+
.zv-legal {
|
|
1325
|
+
gap: 36px;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.zv-legal__section {
|
|
1329
|
+
scroll-margin-top: calc(var(--topbar-h) + 12px);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/* Encart d'accompagnement d'une clause : la reformulation en langage courant
|
|
1334
|
+
* (« En clair ») ou le point à ne pas manquer. Il COMMENTE la clause et ne
|
|
1335
|
+
* la remplace pas — d'où une surface d'accompagnement, et non l'aplat d'une
|
|
1336
|
+
* alerte, qui annoncerait un incident.
|
|
1337
|
+
* Il vit ici et non dans feedback.css : une page légale de vitrine n'a pas à
|
|
1338
|
+
* charger la couche applicative pour afficher une glose. */
|
|
1339
|
+
.zv-legal__encart {
|
|
1340
|
+
display: flex;
|
|
1341
|
+
flex-direction: column;
|
|
1342
|
+
gap: 6px;
|
|
1343
|
+
border-left: 2px solid var(--accent-line);
|
|
1344
|
+
background: var(--panel);
|
|
1345
|
+
padding: 14px 18px;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
.zv-legal__encart--important {
|
|
1349
|
+
border-left-color: var(--warn);
|
|
1350
|
+
background: var(--warn-soft);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
.zv-legal__encart-label {
|
|
1354
|
+
font-family: var(--font-mono);
|
|
1355
|
+
font-size: var(--mono);
|
|
1356
|
+
font-weight: 700;
|
|
1357
|
+
letter-spacing: 0.14em;
|
|
1358
|
+
text-transform: uppercase;
|
|
1359
|
+
color: var(--label);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
.zv-legal__encart--important .zv-legal__encart-label {
|
|
1363
|
+
color: var(--warn-ink);
|
|
1364
|
+
}
|
package/src/forms.css
CHANGED
|
@@ -1001,9 +1001,15 @@
|
|
|
1001
1001
|
min-height: var(--tap);
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
1004
|
-
.
|
|
1004
|
+
/* Le champ nu et celui d'un cadre à affixe. 16px et non --app-lg (14px) :
|
|
1005
|
+
* la règle des 16px ci-dessus vaut pour TOUT champ qu'on saisit au doigt,
|
|
1006
|
+
* et ces deux-là — le composeur, le sous-domaine d'organisation — sont
|
|
1007
|
+
* parmi les plus saisis d'une application. Sous 16, Safari iOS zoome à la
|
|
1008
|
+
* mise au point et décale la page. */
|
|
1009
|
+
.zv-input--bare,
|
|
1010
|
+
.zv-affix .zv-input--bare {
|
|
1005
1011
|
min-height: var(--tap);
|
|
1006
|
-
font-size:
|
|
1012
|
+
font-size: 16px;
|
|
1007
1013
|
}
|
|
1008
1014
|
|
|
1009
1015
|
/* Le cadre passe pleine largeur. Il n'est PAS figé (`flex: none`) : le
|
|
@@ -1015,8 +1021,11 @@
|
|
|
1015
1021
|
flex: 0 1 auto;
|
|
1016
1022
|
}
|
|
1017
1023
|
|
|
1024
|
+
/* Le champ de recherche n'était visé par aucune des deux règles : il
|
|
1025
|
+
* gardait --app (13px), donc il zoomait lui aussi. */
|
|
1018
1026
|
.zv-search__input {
|
|
1019
1027
|
padding-block: 12px;
|
|
1028
|
+
font-size: 16px;
|
|
1020
1029
|
}
|
|
1021
1030
|
|
|
1022
1031
|
/* Le déclencheur peut passer à la ligne : sa valeur courante est un nom de
|
|
@@ -1075,3 +1084,57 @@
|
|
|
1075
1084
|
color: var(--accent);
|
|
1076
1085
|
}
|
|
1077
1086
|
}
|
|
1087
|
+
|
|
1088
|
+
/* ─── Champ mot de passe ────────────────────────────────────────────────
|
|
1089
|
+
* HORS RELEVÉ (27/08/2026, 0.18.0). Aucune des planches ne montre d'écran de
|
|
1090
|
+
* connexion, et le système n'avait donc rien pour ce champ — alors que tous
|
|
1091
|
+
* les produits en ont un, et que la saisie masquée est le seul endroit de
|
|
1092
|
+
* l'interface où l'on tape sans voir ce qu'on écrit.
|
|
1093
|
+
*
|
|
1094
|
+
* Bâti sur `.zv-affix` plutôt qu'en cadre parallèle : le champ nu, le cadre
|
|
1095
|
+
* qui l'entoure et son anneau de mise au point sont déjà là.
|
|
1096
|
+
*
|
|
1097
|
+
* ⚠️ Le bouton est un vrai <button type="button"> : posé sur un <span>, il
|
|
1098
|
+
* n'est ni atteignable au clavier ni annoncé, et il envoie le formulaire dès
|
|
1099
|
+
* qu'on oublie le type. Son libellé change avec l'état — « Afficher » /
|
|
1100
|
+
* « Masquer » — parce qu'une icône seule ne dit pas ce qui va se passer. */
|
|
1101
|
+
|
|
1102
|
+
.zv-affix__btn {
|
|
1103
|
+
display: inline-flex;
|
|
1104
|
+
align-items: center;
|
|
1105
|
+
justify-content: center;
|
|
1106
|
+
flex: none;
|
|
1107
|
+
/* Cible tactile obtenue par la taille, pas par du remplissage : un bouton
|
|
1108
|
+
* de 16 px entouré de marge reste un bouton de 16 px pour le doigt. */
|
|
1109
|
+
width: 32px;
|
|
1110
|
+
height: 32px;
|
|
1111
|
+
margin-right: -6px;
|
|
1112
|
+
padding: 0;
|
|
1113
|
+
border: 0;
|
|
1114
|
+
border-radius: 0;
|
|
1115
|
+
background: none;
|
|
1116
|
+
color: var(--muted);
|
|
1117
|
+
cursor: pointer;
|
|
1118
|
+
transition: color 140ms cubic-bezier(0.2, 0.7, 0.2, 1);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.zv-affix__btn:hover {
|
|
1122
|
+
color: var(--ink);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
.zv-affix__btn[aria-pressed='true'] {
|
|
1126
|
+
color: var(--accent);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/* Le contour déborderait du cadre à affixe, qui le rogne. On le rentre. */
|
|
1130
|
+
.zv-affix__btn:focus-visible {
|
|
1131
|
+
outline-offset: -2px;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/* Les gestionnaires de mot de passe des navigateurs posent leur propre œil
|
|
1135
|
+
* dans le champ : deux boutons côte à côte pour la même chose. On masque le
|
|
1136
|
+
* leur, le nôtre étant accessible et cohérent avec la charte. */
|
|
1137
|
+
.zv-affix input[type='password']::-ms-reveal,
|
|
1138
|
+
.zv-affix input[type='password']::-ms-clear {
|
|
1139
|
+
display: none;
|
|
1140
|
+
}
|
package/src/icons.css
CHANGED
|
@@ -81,6 +81,26 @@
|
|
|
81
81
|
color: var(--ink);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/* Désactivé. C'était la SEULE classe interactive du module sans cet état :
|
|
85
|
+
* .zv-btn, .zv-chrome-btn, .zv-menu__item, .zv-pager__arrow, .zv-range et
|
|
86
|
+
* les champs en ont tous un. Sans lui, un bouton fermé se rendait comme un
|
|
87
|
+
* bouton actif — curseur main compris — et s'allumait au survol comme s'il
|
|
88
|
+
* allait agir.
|
|
89
|
+
* ⚠️ Le survol est neutralisé EXPLICITEMENT : `:hover` s'applique encore à
|
|
90
|
+
* un bouton désactivé, et l'application ne peut pas le rattraper depuis une
|
|
91
|
+
* couche d'utilitaires — ce module n'est pas layered. */
|
|
92
|
+
.zv-icon-btn[disabled],
|
|
93
|
+
.zv-icon-btn[aria-disabled='true'] {
|
|
94
|
+
opacity: 0.5;
|
|
95
|
+
cursor: not-allowed;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.zv-icon-btn[disabled]:hover,
|
|
99
|
+
.zv-icon-btn[aria-disabled='true']:hover {
|
|
100
|
+
background: transparent;
|
|
101
|
+
color: var(--muted);
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
/* Icône décorative posée à côté d'un libellé qui la nomme déjà : elle est
|
|
85
105
|
* atténuée pour que le mot reste le point d'entrée du regard. */
|
|
86
106
|
.zv-icon--quiet {
|
package/src/layout.css
CHANGED
|
@@ -395,13 +395,51 @@
|
|
|
395
395
|
padding-top: 22px;
|
|
396
396
|
padding-bottom: 12px;
|
|
397
397
|
z-index: 40;
|
|
398
|
+
/* Le voile est PLEIN derrière la barre, et il se dégrade EN DESSOUS d'elle
|
|
399
|
+
* (::after). Il était auparavant dégradé sur toute sa hauteur : à sa base
|
|
400
|
+
* il ne restait donc presque rien, et un titre de 38 ou 56px qui passait
|
|
401
|
+
* dessous se retrouvait COUPÉ EN DEUX — moitié voilée, moitié nette, avec
|
|
402
|
+
* les entrées de nav posées sur ses lettres. Deux textes superposés, aucun
|
|
403
|
+
* lisible. Signalé en recette sur mcp-factory.
|
|
404
|
+
* Le dégradé n'est pas perdu : il vit sous la barre, là où il adoucit le
|
|
405
|
+
* passage sans se disputer la place avec la navigation. */
|
|
406
|
+
background: color-mix(in srgb, var(--paper) 92%, transparent);
|
|
407
|
+
/* ⚠️ LE PRÉFIXÉ D'ABORD. Lightning CSS — le compilateur de Tailwind v4,
|
|
408
|
+
* donc celui de toutes nos applications Next — SUPPRIME `backdrop-filter`
|
|
409
|
+
* non préfixé quand il précède la forme `-webkit-`. Or Firefox ne connaît
|
|
410
|
+
* QUE la forme non préfixée : la barre y perdait son flou. Reproduit avec
|
|
411
|
+
* la chaîne exacte des apps, dans les deux ordres. L'ordre est donc une
|
|
412
|
+
* contrainte, pas un style.
|
|
413
|
+
*
|
|
414
|
+
* Le voile, lui, reste en `color-mix` : le compilateur le traite
|
|
415
|
+
* CORRECTEMENT — il émet un repli opaque puis la vraie valeur sous
|
|
416
|
+
* `@supports`, ce qu'on lit dans les bundles livrés. Une note antérieure
|
|
417
|
+
* affirmait qu'il le « réduisait » ; elle prenait le repli pour le
|
|
418
|
+
* résultat. Ne pas « corriger » vers `rgb(from …)` : cette syntaxe sort du
|
|
419
|
+
* compilateur NUE, sans repli, et un navigateur qui l'ignore n'a alors
|
|
420
|
+
* plus AUCUN fond sous une barre fixe — 3,5 % d'usage qui voyaient le
|
|
421
|
+
* voile correctement et ne verraient plus rien. */
|
|
422
|
+
-webkit-backdrop-filter: blur(6px);
|
|
423
|
+
backdrop-filter: blur(6px);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* La frange de dégradé, posée SOUS la barre. `pointer-events: none` : elle
|
|
427
|
+
* couvre le haut du contenu, elle ne doit rien intercepter. */
|
|
428
|
+
.zv-header::after {
|
|
429
|
+
content: '';
|
|
430
|
+
position: absolute;
|
|
431
|
+
top: 100%;
|
|
432
|
+
left: 0;
|
|
433
|
+
right: 0;
|
|
434
|
+
height: 24px;
|
|
398
435
|
background: linear-gradient(
|
|
399
436
|
180deg,
|
|
400
437
|
color-mix(in srgb, var(--paper) 92%, transparent),
|
|
401
438
|
color-mix(in srgb, var(--paper) 0%, transparent)
|
|
402
439
|
);
|
|
403
|
-
backdrop-filter: blur(6px);
|
|
404
440
|
-webkit-backdrop-filter: blur(6px);
|
|
441
|
+
backdrop-filter: blur(6px);
|
|
442
|
+
pointer-events: none;
|
|
405
443
|
}
|
|
406
444
|
|
|
407
445
|
.zv-header:has(> .zv-nav) {
|
package/src/navigation.css
CHANGED
|
@@ -832,6 +832,15 @@
|
|
|
832
832
|
* Sous ce seuil, tout ce qui se touche fait au moins --tap. */
|
|
833
833
|
|
|
834
834
|
@media (max-width: 767px) {
|
|
835
|
+
|
|
836
|
+
/* Le fil ENTIER est un seul lien, et il n'avait aucune hauteur de cible :
|
|
837
|
+
* à 11px de texte il passe sous le minimum tactile. On déploie la cible
|
|
838
|
+
* sans toucher au corps. */
|
|
839
|
+
.zv-crumbs {
|
|
840
|
+
display: inline-flex;
|
|
841
|
+
align-items: center;
|
|
842
|
+
min-height: var(--tap);
|
|
843
|
+
}
|
|
835
844
|
/* 224px de colonne à côté du contenu sur un écran de 375px ne laisse rien à
|
|
836
845
|
* lire : la colonne cède la place au couple barre + tiroir. */
|
|
837
846
|
.zv-sidebar {
|
|
@@ -982,6 +991,17 @@
|
|
|
982
991
|
padding: 8px;
|
|
983
992
|
}
|
|
984
993
|
|
|
994
|
+
/* La cible tactile, que le padding seul ne donne pas : un chevron de dix
|
|
995
|
+
* pixels entouré de huit reste sous --tap. `.zv-pager__step` la portait
|
|
996
|
+
* déjà ; ses deux voisines l'attendaient. */
|
|
997
|
+
.zv-pager__arrow,
|
|
998
|
+
.zv-pager__rank {
|
|
999
|
+
display: inline-flex;
|
|
1000
|
+
align-items: center;
|
|
1001
|
+
justify-content: center;
|
|
1002
|
+
min-height: var(--tap);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
985
1005
|
.zv-pager__rank {
|
|
986
1006
|
padding-block: 8px;
|
|
987
1007
|
}
|