@zevra/ui 0.1.0 → 0.2.1
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 +56 -0
- package/README.md +26 -0
- package/demo/app.html +233 -0
- package/demo/index.html +242 -0
- package/icons/index.json +210 -0
- package/icons/sprite.svg +36 -0
- package/package.json +11 -2
- package/src/components.css +2 -3
- package/src/data.css +742 -0
- package/src/feedback.css +566 -0
- package/src/forms.css +815 -0
- package/src/icons.css +81 -0
- package/src/index.css +5 -0
- package/src/navigation.css +814 -0
- package/src/tokens.css +57 -1
- package/tokens/index.js +27 -1
|
@@ -0,0 +1,814 @@
|
|
|
1
|
+
/* Zevra UI — navigation : le chrome de l'applicatif
|
|
2
|
+
* Colonne latérale, barre supérieure, tiroir, feuille de source citée, menu
|
|
3
|
+
* déroulant, fil d'Ariane, pagination, mécanique de défilement.
|
|
4
|
+
*
|
|
5
|
+
* DÉPEND DE :
|
|
6
|
+
* · tokens.css — neutres (--ink, --ink-2, --muted, --label, --line,
|
|
7
|
+
* --hairline, --panel, --white), --accent / --accent-soft, échelles
|
|
8
|
+
* --app-* et --mono-*, mesures --tap et --topbar-h, --shadow-menu ;
|
|
9
|
+
* · base.css — reset box-sizing, :focus-visible, style des liens sans
|
|
10
|
+
* classe, et la coupure globale prefers-reduced-motion (ce module ne la
|
|
11
|
+
* repose donc pas) ;
|
|
12
|
+
* · layout.css — l'inventaire des couches d'empilement, dont l'échelle
|
|
13
|
+
* ci-dessous est le prolongement ;
|
|
14
|
+
* · icons.css — .zv-icon (glyphes tracés) et .zv-icon-btn (bouton d'icône
|
|
15
|
+
* nu, cible --tap), réemployés ici plutôt que redéfinis.
|
|
16
|
+
* Ne repose ni le reset, ni le body, ni :root.
|
|
17
|
+
*
|
|
18
|
+
* NE REDÉFINIT RIEN de ce que les autres modules possèdent déjà :
|
|
19
|
+
* .zv-navlink et .zv-nav__* (nav de vitrine, layout.css) ne sont pas touchés —
|
|
20
|
+
* l'entrée applicative s'appelle .zv-sidelink ; le déclencheur du menu est
|
|
21
|
+
* .zv-select et l'onglet réemploie .zv-seg, tous deux dans forms.css.
|
|
22
|
+
*
|
|
23
|
+
* Ce module est un RELEVÉ de l'application Mémoire, en production, pas une
|
|
24
|
+
* proposition de DA : chaque valeur vient de src/components/app-shell.tsx,
|
|
25
|
+
* src/components/citation.tsx ou des écrans (chat, répertoire, fiche). Ce qui
|
|
26
|
+
* manque à la production — focus, états désactivés, clavier — est signalé par
|
|
27
|
+
* un ⚠️ à l'endroit exact plutôt que comblé en silence, et ce qui n'a AUCUNE
|
|
28
|
+
* source est étiqueté HORS RELEVÉ ou DÉRIVÉ, jamais présenté comme relevé.
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ SEUIL. Mémoire bascule à `lg` (1024px) ; le paquet n'a qu'un point de
|
|
31
|
+
* rupture, 768px, et c'est celui-là qui s'applique. Ce qui est relevé, ce sont
|
|
32
|
+
* les DEUX jeux de valeurs (doigt / souris), jamais la largeur où ils
|
|
33
|
+
* basculent. Tout le responsive est en fin de fichier, sous la seule forme
|
|
34
|
+
* employée par base.css et components.css : `max-width: 767px`. Les règles de
|
|
35
|
+
* base décrivent donc l'écran large, et le bloc final ramène au doigt.
|
|
36
|
+
*
|
|
37
|
+
* ─── Empilement ────────────────────────────────────────────────────────
|
|
38
|
+
* layout.css tient l'inventaire des couches du paquet et s'arrête à 50
|
|
39
|
+
* (.zv-layer-top), avec la consigne « toute nouvelle couche s'arbitre par
|
|
40
|
+
* rapport à celle-ci ». Les recouvrements applicatifs se rangent donc
|
|
41
|
+
* AU-DESSUS, sans jamais se mettre à égalité avec une couche de vitrine : un
|
|
42
|
+
* voile de tiroir et un header fixe départagés par l'ordre du DOM, ce n'est
|
|
43
|
+
* pas une échelle. Le relevé donnait voile 40, menu 40, feuille 50, tiroir 60 ;
|
|
44
|
+
* seuls les RAPPORTS comptent, et ils sont conservés — l'origine est décalée
|
|
45
|
+
* en bloc pour dégager l'inventaire de vitrine :
|
|
46
|
+
*
|
|
47
|
+
* menu 60 · voile 60 · feuille de source 70 · tiroir 80
|
|
48
|
+
*
|
|
49
|
+
* L'égalité menu/voile est conservée parce qu'elle est assumée : les deux ne
|
|
50
|
+
* coexistent jamais (ouvrir le tiroir ferme le menu). C'est autre chose qu'une
|
|
51
|
+
* égalité subie entre deux pièces qui, elles, se croisent.
|
|
52
|
+
* N'insérer aucune valeur intermédiaire sans rejuger l'échelle entière.
|
|
53
|
+
*
|
|
54
|
+
* ─── Gabarit attendu ───────────────────────────────────────────────────
|
|
55
|
+
* Toutes ces pièces supposent une racine applicative en `height: 100dvh;
|
|
56
|
+
* overflow: hidden; display: flex` — colonne sous le seuil, rangée au-dessus.
|
|
57
|
+
* Sans elle, aucune zone défilante ne tient sa hauteur et c'est la page
|
|
58
|
+
* entière qui défile, ce qui ruine la colonne comme la feuille. Cette racine
|
|
59
|
+
* relève de la mise en page, pas de ce module (cf. notes).
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/* ─── Colonne latérale ──────────────────────────────────────────────────
|
|
63
|
+
* Une colonne, un filet, rien d'autre : ni ombre, ni rayon. Une ombre ferait
|
|
64
|
+
* flotter une pièce qui ne bouge jamais, et le rayon appartient à l'action
|
|
65
|
+
* (--r-btn) ou à la carte (--r-card), pas au chrome. */
|
|
66
|
+
|
|
67
|
+
.zv-sidebar {
|
|
68
|
+
display: flex;
|
|
69
|
+
width: 224px;
|
|
70
|
+
/* Sans `flex: none`, une entrée de nav un peu longue élargit la colonne et
|
|
71
|
+
* décale le contenu : la largeur est une constante, pas un résultat. */
|
|
72
|
+
flex: none;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
/* La colonne défile pour son propre compte : une nav longue ne doit jamais
|
|
75
|
+
* entraîner le contenu, ni l'inverse. */
|
|
76
|
+
overflow-y: auto;
|
|
77
|
+
border-right: 1px solid var(--line);
|
|
78
|
+
background: var(--panel);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.zv-sidebar__head {
|
|
82
|
+
border-bottom: 1px solid var(--line);
|
|
83
|
+
padding: 20px 18px 14px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* `margin-top: auto` colle le pied en bas quand la nav est courte et le laisse
|
|
87
|
+
* suivre le contenu quand elle est longue. Un `position: sticky` aurait
|
|
88
|
+
* recouvert la dernière entrée dès que la colonne défile. */
|
|
89
|
+
.zv-sidebar__foot {
|
|
90
|
+
margin-top: auto;
|
|
91
|
+
border-top: 1px solid var(--line);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Le même balisage sert à l'aside et au panneau du tiroir. Dans le tiroir, la
|
|
95
|
+
* croix se pose en absolu par-dessus la tête : sans cette réserve, un logo
|
|
96
|
+
* client large passe dessous et devient incliquable. */
|
|
97
|
+
.zv-drawer .zv-sidebar__head > :first-child {
|
|
98
|
+
padding-right: 46px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* ─── Groupes de navigation ─────────────────────────────────────────────
|
|
102
|
+
* Nommés .zv-sidegroup et non .zv-navgroup : layout.css possède déjà .zv-nav,
|
|
103
|
+
* .zv-nav__logo, .zv-nav__links et .zv-navlink pour la nav de VITRINE. Un
|
|
104
|
+
* .zv-navgroup posé à côté se lirait comme une pièce de cette famille-là. La
|
|
105
|
+
* colonne applicative a sa propre lignée : sidebar › sidegroup › sidelink. */
|
|
106
|
+
|
|
107
|
+
.zv-sidegroup {
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
gap: 2px;
|
|
111
|
+
padding: 12px 10px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.zv-sidegroup__title {
|
|
115
|
+
padding: 2px 10px 8px;
|
|
116
|
+
font-family: var(--font-mono);
|
|
117
|
+
font-size: var(--mono);
|
|
118
|
+
font-weight: 600;
|
|
119
|
+
letter-spacing: 0.12em;
|
|
120
|
+
text-transform: uppercase;
|
|
121
|
+
color: var(--label);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* À partir du deuxième groupe, c'est le blanc au-dessus de l'intitulé qui
|
|
125
|
+
* sépare — pas un filet. La colonne en porte déjà deux (tête et pied) ; un
|
|
126
|
+
* troisième trait la transformerait en tableau. */
|
|
127
|
+
.zv-sidegroup + .zv-sidegroup .zv-sidegroup__title {
|
|
128
|
+
padding-top: 14px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ─── Entrée de barre latérale ──────────────────────────────────────────
|
|
132
|
+
* `.zv-navlink` appartient déjà à la navigation de vitrine (layout.css) :
|
|
133
|
+
* l'entrée applicative s'appelle `.zv-sidelink`, et les deux ne doivent
|
|
134
|
+
* jamais se recouvrir. */
|
|
135
|
+
|
|
136
|
+
.zv-sidelink {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: 10px;
|
|
140
|
+
padding: 8px 10px;
|
|
141
|
+
font-family: var(--font-text);
|
|
142
|
+
font-size: var(--app);
|
|
143
|
+
font-weight: 500;
|
|
144
|
+
color: var(--ink-2);
|
|
145
|
+
text-decoration: none;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* ⚠️ CONTRE-ORDRE, pas un style. base.css repeint les liens nus en accent, et
|
|
149
|
+
* l'application hôte va souvent plus loin (`a { color: accent }`,
|
|
150
|
+
* `a:hover { text-decoration: underline }`). Sans ces deux déclarations
|
|
151
|
+
* d'apparence redondante, l'entrée de nav vire au bleu souligné dès qu'elle
|
|
152
|
+
* tombe dans une page qui applique le style éditorial. */
|
|
153
|
+
.zv-sidelink:hover {
|
|
154
|
+
background: var(--hairline);
|
|
155
|
+
color: var(--ink-2);
|
|
156
|
+
text-decoration: none;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* `aria-current="page"` est ajouté au relevé : la production ne marque
|
|
160
|
+
* l'entrée courante que visuellement, ce qui la rend muette au lecteur
|
|
161
|
+
* d'écran. Les deux sélecteurs valent la même chose pour que le balisage
|
|
162
|
+
* correct ne coûte rien. */
|
|
163
|
+
.zv-sidelink--on,
|
|
164
|
+
.zv-sidelink[aria-current="page"] {
|
|
165
|
+
background: var(--accent-soft);
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
color: var(--accent);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Au survol de l'entrée courante, le fond change mais le texte reste accent :
|
|
171
|
+
* sinon la page où l'on se trouve « s'éteint » sous le pointeur. */
|
|
172
|
+
.zv-sidelink--on:hover,
|
|
173
|
+
.zv-sidelink[aria-current="page"]:hover {
|
|
174
|
+
background: var(--hairline);
|
|
175
|
+
color: var(--accent);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Le contour de base.css déborde de 2px ; dans une colonne en `overflow-y:
|
|
179
|
+
* auto` il est rogné par le bord du scroller. On le rentre plutôt que d'en
|
|
180
|
+
* redessiner un autre. */
|
|
181
|
+
.zv-sidelink:focus-visible {
|
|
182
|
+
outline-offset: -2px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Largeur fixe : le rang mono et le glyphe ⚙ du panneau admin doivent tomber
|
|
186
|
+
* sur la même colonne, sinon les libellés se décalent d'un groupe à l'autre.
|
|
187
|
+
* La production écrit 10px en dur (app-shell.tsx:160) ; c'est exactement
|
|
188
|
+
* --mono, donc le token remplace la valeur sans rien déplacer. */
|
|
189
|
+
.zv-sidelink__num {
|
|
190
|
+
width: 16px;
|
|
191
|
+
flex: none;
|
|
192
|
+
font-family: var(--font-mono);
|
|
193
|
+
font-size: var(--mono);
|
|
194
|
+
font-weight: 500;
|
|
195
|
+
color: var(--label);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* ─── Barre supérieure ──────────────────────────────────────────────────
|
|
199
|
+
* Elle remplace la colonne sous le seuil : masquée au-dessus, elle n'apparaît
|
|
200
|
+
* que dans le bloc mobile de fin de fichier. */
|
|
201
|
+
|
|
202
|
+
.zv-topbar {
|
|
203
|
+
display: none;
|
|
204
|
+
/* --topbar-h nomme la HAUTEUR DE LA BARRE, et elle seule : 52px. Le filet
|
|
205
|
+
* qui la termine est un pixel de plus, et c'est pour cela que la feuille de
|
|
206
|
+
* source commence à 53 (cf. .zv-cite-panel dans le bloc mobile). Les deux
|
|
207
|
+
* valeurs de la production sont justes et se répondent — les unifier sur 52
|
|
208
|
+
* ferait remonter la feuille par-dessus le filet.
|
|
209
|
+
* ⚠️ Nuance à connaître avant d'y toucher : base.css impose box-sizing:
|
|
210
|
+
* border-box, donc ce `border-bottom` est peint À L'INTÉRIEUR des 52px. Le
|
|
211
|
+
* modèle « 52 + 1 » n'est littéral que si le filet sort de la boîte —
|
|
212
|
+
* une ligne suffirait (`box-shadow: 0 1px 0 var(--line)` au lieu du
|
|
213
|
+
* `border-bottom`), mais c'est un écart au relevé : à arbitrer, pas à
|
|
214
|
+
* décider ici. */
|
|
215
|
+
height: var(--topbar-h);
|
|
216
|
+
flex: none;
|
|
217
|
+
align-items: center;
|
|
218
|
+
gap: 10px;
|
|
219
|
+
border-bottom: 1px solid var(--line);
|
|
220
|
+
background: var(--panel);
|
|
221
|
+
padding-inline: 16px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Le logo cède AVANT le nom : un logo client très large ne doit jamais
|
|
225
|
+
* pousser la barre hors de l'écran. */
|
|
226
|
+
.zv-topbar__logo {
|
|
227
|
+
min-width: 0;
|
|
228
|
+
overflow: hidden;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.zv-topbar__title {
|
|
232
|
+
min-width: 0;
|
|
233
|
+
flex: 1 1 0%;
|
|
234
|
+
overflow: hidden;
|
|
235
|
+
text-overflow: ellipsis;
|
|
236
|
+
white-space: nowrap;
|
|
237
|
+
font-size: var(--app-sm);
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
color: var(--ink);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Le hamburger n'est PAS un bouton propre à ce module : c'est le .zv-icon-btn
|
|
243
|
+
* d'icons.css (cible --tap, sans cadre, couleur --muted) posé dans la barre.
|
|
244
|
+
* Le balisage est donc `class="zv-icon-btn zv-topbar__burger"`, et seul le
|
|
245
|
+
* décalage optique appartient à la barre : la marge négative aligne le GLYPHE
|
|
246
|
+
* sur le bord de la barre alors que la boîte garde ses 40px de cible tactile —
|
|
247
|
+
* sans elle, il faut choisir entre l'alignement optique et le doigt.
|
|
248
|
+
* Le relevé ne donne AUCUN état au hamburger ; le survol d'icons.css peint
|
|
249
|
+
* --panel, qui est exactement le fond de cette barre — donc invisible ici.
|
|
250
|
+
* Seule la couleur du glyphe s'assombrit d'un cran, ce qui vient du composant
|
|
251
|
+
* partagé et non d'une décision de ce module. */
|
|
252
|
+
.zv-topbar__burger {
|
|
253
|
+
margin-right: -8px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* ─── Bouton de chrome (croix bordée) ───────────────────────────────────
|
|
257
|
+
* ⚠️ RENOMMÉ : ce bouton s'appelait .zv-icon-btn, nom que icons.css possède
|
|
258
|
+
* déjà pour tout autre chose (bouton nu de 40px, coins arrondis). Deux
|
|
259
|
+
* géométries sous un seul nom, c'est la dernière qui charge qui gagne : la
|
|
260
|
+
* croix bordée du chrome devient .zv-chrome-btn.
|
|
261
|
+
* Un seul emploi : la croix de fermeture du tiroir et de la feuille de source.
|
|
262
|
+
* Angles vifs — le rayon signale l'action, le chrome n'en est pas une. */
|
|
263
|
+
|
|
264
|
+
.zv-chrome-btn {
|
|
265
|
+
display: flex;
|
|
266
|
+
width: 26px;
|
|
267
|
+
height: 26px;
|
|
268
|
+
flex: none;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
border: 1px solid var(--line);
|
|
272
|
+
border-radius: 0;
|
|
273
|
+
background: var(--white);
|
|
274
|
+
/* base.css ne reset pas les boutons : sans police ni fond explicites, un
|
|
275
|
+
* <button> retombe sur ceux du navigateur et détonne. Même précaution que
|
|
276
|
+
* .zv-btn dans components.css. */
|
|
277
|
+
font-family: var(--font-text);
|
|
278
|
+
font-size: var(--app);
|
|
279
|
+
line-height: 1;
|
|
280
|
+
color: var(--muted);
|
|
281
|
+
cursor: pointer;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* ⚠️ Divergence relevée : la croix du tiroir n'a AUCUN état en production,
|
|
285
|
+
* celle de la feuille de source porte ce survol. Retenu : le survol partout,
|
|
286
|
+
* un bouton sans réaction se lit comme un décor. */
|
|
287
|
+
.zv-chrome-btn:hover {
|
|
288
|
+
background: var(--hairline);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.zv-chrome-btn:focus-visible {
|
|
292
|
+
outline-offset: -2px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* ⚠️ HORS RELEVÉ : aucun bouton de chrome n'est jamais désactivé en
|
|
296
|
+
* production. Posé parce qu'un bouton grisé sans style se lit comme un bouton
|
|
297
|
+
* actif ; aucune couleur nouvelle n'est introduite. */
|
|
298
|
+
.zv-chrome-btn[disabled] {
|
|
299
|
+
color: var(--label);
|
|
300
|
+
cursor: default;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.zv-chrome-btn[disabled]:hover {
|
|
304
|
+
background: var(--white);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* Glyphes relevés, à reproduire tels quels :
|
|
308
|
+
* · hamburger — SVG 18×12 (viewBox "0 0 18 12"),
|
|
309
|
+
* <path d="M0 1h18M0 6h18M0 11h18" stroke="currentColor" stroke-width="1.5"/> :
|
|
310
|
+
* trois traits pleine largeur, entraxe 5px, extrémités franches. Ce n'est
|
|
311
|
+
* PAS une icône 24×24 d'une bibliothèque, et une icône ronde tirée d'un
|
|
312
|
+
* jeu tiers casserait la géométrie de la barre. Il ne suit donc pas la
|
|
313
|
+
* grille de 24 de icons.css : il se pose en SVG nu, pas en .zv-icon.
|
|
314
|
+
* · fermeture — le caractère ✕ (U+2715) posé en texte, d'où le
|
|
315
|
+
* `font-size` ci-dessus qui le dimensionne. */
|
|
316
|
+
|
|
317
|
+
/* ─── Voile ─────────────────────────────────────────────────────────────
|
|
318
|
+
* Toujours l'encre de charte diluée, jamais un noir pur : la page est
|
|
319
|
+
* bleutée, un gris neutre y ferait un trou sale. Le voile n'existe que sous
|
|
320
|
+
* le seuil (cf. bloc mobile) — au-dessus, tiroir et feuille sont des colonnes,
|
|
321
|
+
* elles n'occultent rien. */
|
|
322
|
+
|
|
323
|
+
.zv-scrim {
|
|
324
|
+
display: none;
|
|
325
|
+
position: fixed;
|
|
326
|
+
inset: 0;
|
|
327
|
+
z-index: 60;
|
|
328
|
+
background: color-mix(in oklab, var(--ink) 40%, transparent);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* ─── Tiroir de navigation ──────────────────────────────────────────────*/
|
|
332
|
+
|
|
333
|
+
.zv-drawer {
|
|
334
|
+
display: none;
|
|
335
|
+
position: fixed;
|
|
336
|
+
inset: 0;
|
|
337
|
+
z-index: 80;
|
|
338
|
+
/* `visibility` plutôt que `display` : elle sort le contenu du parcours
|
|
339
|
+
* clavier ET se transitionne, ce qui retient le panneau à l'écran le temps
|
|
340
|
+
* qu'il glisse dehors. En `display: none`, il disparaîtrait à mi-course. */
|
|
341
|
+
visibility: hidden;
|
|
342
|
+
transition: visibility 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.zv-drawer[data-open] {
|
|
346
|
+
visibility: visible;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Le voile du tiroir accompagne un panneau qui glisse : il se fond. Celui de
|
|
350
|
+
* la feuille de source apparaît net, faute de mouvement à accompagner. */
|
|
351
|
+
.zv-drawer .zv-scrim {
|
|
352
|
+
opacity: 0;
|
|
353
|
+
transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.zv-drawer[data-open] .zv-scrim {
|
|
357
|
+
opacity: 1;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.zv-drawer__panel {
|
|
361
|
+
position: absolute;
|
|
362
|
+
inset-block: 0;
|
|
363
|
+
left: 0;
|
|
364
|
+
display: flex;
|
|
365
|
+
width: 288px;
|
|
366
|
+
/* Le plafond en pourcentage garde toujours une bande de page visible
|
|
367
|
+
* derrière : un tiroir qui couvre tout l'écran ne se lit plus comme un
|
|
368
|
+
* tiroir, et l'utilisateur perd l'endroit où appuyer pour sortir. */
|
|
369
|
+
max-width: 86%;
|
|
370
|
+
flex-direction: column;
|
|
371
|
+
overflow-y: auto;
|
|
372
|
+
border-right: 1px solid var(--line);
|
|
373
|
+
background: var(--panel);
|
|
374
|
+
border-radius: 0;
|
|
375
|
+
/* Aucune ombre : c'est le voile qui fait le contraste. En ajouter une
|
|
376
|
+
* doublerait la séparation et salirait le bord. */
|
|
377
|
+
box-shadow: none;
|
|
378
|
+
transform: translateX(-100%);
|
|
379
|
+
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.zv-drawer[data-open] .zv-drawer__panel {
|
|
383
|
+
transform: translateX(0);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* ⚠️ TENSION ASSUMÉE, à arbitrer. Les deux courbes ci-dessus sont celles du
|
|
387
|
+
* relevé — voile en cubic-bezier(.4,0,.2,1), panneau en ease-out, même durée.
|
|
388
|
+
* Leur asymétrie ne se voit pas, elle s'entend : le panneau arrive avant que
|
|
389
|
+
* le voile ait fini de se poser. Elles ne sont donc PAS la courbe unique
|
|
390
|
+
* --zv-easing que motion.css impose à la vitrine. Défendable (motion.css régit
|
|
391
|
+
* neuf animations de page, pas le chrome applicatif), mais c'est une divergence
|
|
392
|
+
* écrite ici plutôt qu'une variable empruntée en silence — d'autant que ce
|
|
393
|
+
* module doit rester utilisable sans motion.css.
|
|
394
|
+
* La coupure prefers-reduced-motion est déjà globale dans base.css : ne pas la
|
|
395
|
+
* reposer ici, elle neutralise ces deux transitions comme les autres. */
|
|
396
|
+
|
|
397
|
+
.zv-drawer__close {
|
|
398
|
+
position: absolute;
|
|
399
|
+
top: 7px;
|
|
400
|
+
right: 8px;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* Comportement attendu du composant, à conserver tel quel :
|
|
404
|
+
* · role="dialog" aria-modal="true" aria-label="Navigation", et aria-hidden
|
|
405
|
+
* qui reflète l'état fermé ;
|
|
406
|
+
* · clic sur le voile ou touche Échap → fermeture ;
|
|
407
|
+
* · à l'ouverture, `body { overflow: hidden }` RESTAURÉ à sa valeur
|
|
408
|
+
* précédente à la fermeture, jamais remis à '' aveuglément : c'est ce qui
|
|
409
|
+
* évite de casser une page qui gérait déjà son propre défilement ;
|
|
410
|
+
* · l'état ouvert mémorise la ROUTE d'ouverture, pas un booléen — changer de
|
|
411
|
+
* page referme alors le tiroir sans effet ni rendu en cascade. Le
|
|
412
|
+
* composant a donc besoin de la route courante, pas d'une prop `open`.
|
|
413
|
+
* ⚠️ MANQUE en production, à combler côté composant : piège de focus dans le
|
|
414
|
+
* panneau et restitution du focus au bouton d'ouverture à la fermeture. */
|
|
415
|
+
|
|
416
|
+
/* ─── Feuille de source citée ───────────────────────────────────────────
|
|
417
|
+
* ⚠️ RENOMMÉE : cette pièce s'appelait .zv-sheet. layout.css possède déjà
|
|
418
|
+
* .zv-sheet-section (section de PLANCHE de documentation, avec son __title et
|
|
419
|
+
* son __body) : côte à côte, .zv-sheet__title et .zv-sheet-section__title
|
|
420
|
+
* donnent à lire un bloc et son modificateur là où il y a deux objets sans
|
|
421
|
+
* rapport. Elle prend le nom de ce qui l'ouvre — un clic sur une citation :
|
|
422
|
+
* .zv-cite-panel. Les .zv-cite / .zv-cite-row de data.css sont la CITATION dans le fil de texte ; .zv-cite-panel est la feuille qui l'accueille — deux objets distincts, sans collision, et le nom
|
|
423
|
+
* ne se confond pas avec .zv-src (la PUCE de source, components.css).
|
|
424
|
+
*
|
|
425
|
+
* Colonne au-dessus du seuil, feuille quasi plein écran en dessous. Trois
|
|
426
|
+
* zones : tête fixe, corps défilant, pied d'actions. */
|
|
427
|
+
|
|
428
|
+
.zv-cite-panel {
|
|
429
|
+
display: flex;
|
|
430
|
+
height: 100dvh;
|
|
431
|
+
width: 392px;
|
|
432
|
+
flex: none;
|
|
433
|
+
flex-direction: column;
|
|
434
|
+
border-left: 1px solid var(--line);
|
|
435
|
+
/* Blanc, et non --panel : c'est une surface de lecture, pas du chrome. */
|
|
436
|
+
background: var(--white);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.zv-cite-panel__head {
|
|
440
|
+
display: flex;
|
|
441
|
+
/* `flex-start` et non `center` : un titre de document sur trois lignes ne
|
|
442
|
+
* doit pas faire descendre la croix au milieu du bloc. */
|
|
443
|
+
align-items: flex-start;
|
|
444
|
+
justify-content: space-between;
|
|
445
|
+
gap: 12px;
|
|
446
|
+
border-bottom: 1px solid var(--line);
|
|
447
|
+
padding: 18px 20px 14px;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.zv-cite-panel__title {
|
|
451
|
+
font-family: var(--font-display);
|
|
452
|
+
font-size: var(--app-h3);
|
|
453
|
+
line-height: 1.35;
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
/* Un nom de fichier sans espace déborderait la colonne et pousserait la
|
|
456
|
+
* croix hors de l'écran. */
|
|
457
|
+
overflow-wrap: break-word;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* Le corps est la seule zone qui défile : le fond gris l'enfonce d'un cran
|
|
461
|
+
* pour que la page citée s'y détache comme une feuille posée. */
|
|
462
|
+
.zv-cite-panel__body {
|
|
463
|
+
flex: 1 1 0%;
|
|
464
|
+
overflow-y: auto;
|
|
465
|
+
background: var(--hairline);
|
|
466
|
+
padding: 18px;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.zv-cite-panel__foot {
|
|
470
|
+
display: flex;
|
|
471
|
+
flex-direction: column;
|
|
472
|
+
gap: 8px;
|
|
473
|
+
border-top: 1px solid var(--line);
|
|
474
|
+
padding: 14px 20px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Fermeture par Échap, par le voile, par la croix, et automatiquement à chaque
|
|
478
|
+
* navigation. AUCUNE transition d'entrée ni de sortie, contrairement au
|
|
479
|
+
* tiroir : la feuille répond à un clic sur une citation, elle doit être là au
|
|
480
|
+
* moment où le regard y va.
|
|
481
|
+
* ⚠️ NON LIVRÉ : le kicker « SOURCE CITÉE » de la tête (citation.tsx:104) n'est
|
|
482
|
+
* dans aucun inventaire, et il n'est pas tranché s'il relève de la typographie
|
|
483
|
+
* (.zv-kicker, base.css) ou de ce module. Rien n'est inventé en attendant. */
|
|
484
|
+
|
|
485
|
+
/* ─── Menu déroulant ────────────────────────────────────────────────────
|
|
486
|
+
* Ancré sous son déclencheur (.zv-select, forms.css), qui DOIT porter
|
|
487
|
+
* `position: relative`, sans quoi le menu se colle au coin de la page.
|
|
488
|
+
* L'enveloppe .zv-select-anchor (forms.css) le porte déjà : c'est elle qu'on
|
|
489
|
+
* pose autour d'un déclencheur .zv-select, et non le déclencheur lui-même. */
|
|
490
|
+
|
|
491
|
+
.zv-menu {
|
|
492
|
+
position: absolute;
|
|
493
|
+
top: calc(100% + 5px);
|
|
494
|
+
left: 0;
|
|
495
|
+
z-index: 60;
|
|
496
|
+
display: flex;
|
|
497
|
+
flex-direction: column;
|
|
498
|
+
min-width: 230px;
|
|
499
|
+
border: 1px solid var(--line);
|
|
500
|
+
border-radius: 0;
|
|
501
|
+
background: var(--white);
|
|
502
|
+
padding: 5px;
|
|
503
|
+
/* Seule ombre du chrome applicatif, et elle est justifiée : un menu flotte
|
|
504
|
+
* vraiment au-dessus de la page, contrairement à la colonne ou au tiroir.
|
|
505
|
+
* --shadow-card est trop courte pour ce tirage, --shadow-panel trop indigo :
|
|
506
|
+
* d'où --shadow-menu, qui existe pour ce seul usage. */
|
|
507
|
+
box-shadow: var(--shadow-menu);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.zv-menu__item {
|
|
511
|
+
display: block;
|
|
512
|
+
/* Les quatre déclarations qui suivent existent pour que l'item tienne aussi
|
|
513
|
+
* bien en <button> qu'en <div> : sans elles, le navigateur impose sa police,
|
|
514
|
+
* son fond gris, son cadre et un texte centré. */
|
|
515
|
+
width: 100%;
|
|
516
|
+
border: 0;
|
|
517
|
+
background: none;
|
|
518
|
+
text-align: left;
|
|
519
|
+
padding: 8px 11px;
|
|
520
|
+
font-family: var(--font-text);
|
|
521
|
+
font-size: var(--app-sm);
|
|
522
|
+
font-weight: 400;
|
|
523
|
+
color: var(--ink);
|
|
524
|
+
cursor: pointer;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.zv-menu__item:hover {
|
|
528
|
+
background: var(--hairline);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.zv-menu__item:focus-visible {
|
|
532
|
+
outline-offset: -2px;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.zv-menu__item--on,
|
|
536
|
+
.zv-menu__item[aria-selected="true"] {
|
|
537
|
+
background: var(--accent-soft);
|
|
538
|
+
color: var(--accent);
|
|
539
|
+
font-weight: 600;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/* ⚠️ Relevé tel quel : le survol ÉCRASE le fond de l'item courant
|
|
543
|
+
* (accent-soft → hairline), seul le texte reste accent. Volontaire ou non,
|
|
544
|
+
* c'est ce qui tourne en production — signalé plutôt que corrigé en silence. */
|
|
545
|
+
.zv-menu__item--on:hover,
|
|
546
|
+
.zv-menu__item[aria-selected="true"]:hover {
|
|
547
|
+
background: var(--hairline);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* ⚠️ HORS RELEVÉ : aucun item désactivé n'existe en production. Même
|
|
551
|
+
* traitement que le bouton de chrome, sans couleur nouvelle. */
|
|
552
|
+
.zv-menu__item[disabled],
|
|
553
|
+
.zv-menu__item[aria-disabled="true"] {
|
|
554
|
+
color: var(--label);
|
|
555
|
+
cursor: default;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.zv-menu__item[disabled]:hover,
|
|
559
|
+
.zv-menu__item[aria-disabled="true"]:hover {
|
|
560
|
+
background: none;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/* ⚠️ MANQUENT en production et NON comblés ici, car ce sont des comportements,
|
|
564
|
+
* pas du style : navigation au clavier (flèches, Origine/Fin), fermeture au
|
|
565
|
+
* clic extérieur, fermeture à Échap. Le CSS ci-dessus est prêt à les recevoir
|
|
566
|
+
* (aria-selected, :focus-visible) ; le composant doit les écrire. */
|
|
567
|
+
|
|
568
|
+
/* ─── Fil d'Ariane ──────────────────────────────────────────────────────*/
|
|
569
|
+
|
|
570
|
+
.zv-crumbs {
|
|
571
|
+
display: block;
|
|
572
|
+
margin-bottom: 14px;
|
|
573
|
+
font-family: var(--font-mono);
|
|
574
|
+
font-size: var(--mono-lg);
|
|
575
|
+
font-weight: 500;
|
|
576
|
+
/* Le fil porte des noms de dossier entiers : il a le droit de passer à la
|
|
577
|
+
* ligne, pas celui d'élargir la page. */
|
|
578
|
+
overflow-wrap: break-word;
|
|
579
|
+
color: var(--label);
|
|
580
|
+
text-decoration: none;
|
|
581
|
+
cursor: pointer;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/* Même contre-ordre que .zv-sidelink : sans lui, le fil vire au bleu souligné
|
|
585
|
+
* dans toute page qui applique le style de lien éditorial. */
|
|
586
|
+
.zv-crumbs:hover {
|
|
587
|
+
color: var(--label);
|
|
588
|
+
text-decoration: none;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.zv-crumbs__sep {
|
|
592
|
+
margin-inline: 4px;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* Le dernier segment est plus sombre que le fil : c'est lui qu'on lit, le
|
|
596
|
+
* reste n'est qu'un chemin. Aucun état « segment courant » n'existe au relevé. */
|
|
597
|
+
.zv-crumbs__seg {
|
|
598
|
+
color: var(--ink-2);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* ─── Pagination ────────────────────────────────────────────────────────
|
|
602
|
+
* Deux formes relevées, et deux seulement : le rang centré d'un panneau
|
|
603
|
+
* (‹ p. 12 / 31 ›) et le pied de tableau (compte à gauche, « ‹ Précédent ·
|
|
604
|
+
* Suivant › » à droite).
|
|
605
|
+
*
|
|
606
|
+
* ⚠️ DOUBLON À TRANCHER, et il est tranché ici : data.css pose .zv-table-foot
|
|
607
|
+
* pour le MÊME nœud du DOM (repertoire-client.tsx:165) avec les mêmes valeurs.
|
|
608
|
+
* La pagination est de la navigation, pas de la donnée : la forme reste dans ce
|
|
609
|
+
* module, sous .zv-pager--foot, et data.css doit abandonner .zv-table-foot.
|
|
610
|
+
* .zv-pager--foot est un MODIFICATEUR : il se pose avec .zv-pager, jamais seul. */
|
|
611
|
+
|
|
612
|
+
.zv-pager {
|
|
613
|
+
display: flex;
|
|
614
|
+
justify-content: center;
|
|
615
|
+
gap: 14px;
|
|
616
|
+
/* Décolle le rang des boutons du pied de feuille, où il est immédiatement
|
|
617
|
+
* suivi de deux actions pleine largeur. */
|
|
618
|
+
padding-bottom: 2px;
|
|
619
|
+
font-family: var(--font-mono);
|
|
620
|
+
font-size: var(--mono-lg);
|
|
621
|
+
font-weight: 500;
|
|
622
|
+
color: var(--muted);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* `font: inherit` plutôt qu'une police redite : les trois éléments cliquables
|
|
626
|
+
* de la pagination sont des <button>, et sans lui le navigateur y impose sa
|
|
627
|
+
* police système. Hériter reprend exactement la mono du rang — aucune valeur
|
|
628
|
+
* nouvelle n'entre par cette porte. */
|
|
629
|
+
.zv-pager__arrow,
|
|
630
|
+
.zv-pager__rank,
|
|
631
|
+
.zv-pager__step {
|
|
632
|
+
border: 0;
|
|
633
|
+
background: none;
|
|
634
|
+
color: inherit;
|
|
635
|
+
font: inherit;
|
|
636
|
+
padding: 0;
|
|
637
|
+
cursor: pointer;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/* ⚠️ HORS RELEVÉ. La production n'a AUCUN état désactivé : les flèches sont
|
|
641
|
+
* identiques en première et en dernière page. Posé ici parce qu'une flèche
|
|
642
|
+
* qui ne fait rien et ne le dit pas se lit comme une panne. `pointer-events`
|
|
643
|
+
* n'est volontairement pas coupé : en aria-disabled, l'élément doit rester
|
|
644
|
+
* atteignable au clavier pour être annoncé. */
|
|
645
|
+
.zv-pager__arrow[disabled],
|
|
646
|
+
.zv-pager__arrow[aria-disabled="true"] {
|
|
647
|
+
color: var(--label);
|
|
648
|
+
cursor: default;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/* Pied de tableau : le compte à gauche, un SEUL élément cliquable pour les
|
|
652
|
+
* deux sens à droite — pas de numéros de page, pas de page courante. Le
|
|
653
|
+
* `padding` en raccourci annule le `padding-bottom: 2px` du rang de panneau,
|
|
654
|
+
* qui n'a pas de sens ici. */
|
|
655
|
+
.zv-pager--foot {
|
|
656
|
+
justify-content: space-between;
|
|
657
|
+
align-items: center;
|
|
658
|
+
gap: 0;
|
|
659
|
+
padding: 12px 4px;
|
|
660
|
+
color: var(--label);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* ⚠️ TROU ASSUMÉ, et il dépasse le style : il n'existe nulle part de page
|
|
664
|
+
* courante, de numéros, ni de sens séparés. Une pagination digne de ce nom
|
|
665
|
+
* (précédent et suivant distincts, désactivables, rang courant) est une
|
|
666
|
+
* DÉCISION de conception, pas un relevé — cf. notes. */
|
|
667
|
+
|
|
668
|
+
/* ─── Onglets ───────────────────────────────────────────────────────────
|
|
669
|
+
* ⚠️ AUCUNE MATIÈRE PREMIÈRE : pas un seul jeu d'onglets dans les écrans
|
|
670
|
+
* relevés. Ce module ne livre donc RIEN ici.
|
|
671
|
+
*
|
|
672
|
+
* Une rangée d'onglets se construit avec .zv-seg-group et .zv-seg
|
|
673
|
+
* (forms.css), qui portent déjà la grammaire d'un état actif. Ce module a
|
|
674
|
+
* d'abord défini une .zv-tabs — display flex, wrap, gouttière 6px — qui
|
|
675
|
+
* était la copie exacte de .zv-seg-group : deux noms pour le MÊME nœud du
|
|
676
|
+
* DOM, et c'est l'ordre de chargement qui aurait tranché. Elle est retirée.
|
|
677
|
+
*
|
|
678
|
+
* Un onglet SOULIGNÉ, lui, reste à faire : il suppose d'arbitrer la couleur
|
|
679
|
+
* et l'épaisseur du trait actif, ce qu'aucune source ne donne.
|
|
680
|
+
*/
|
|
681
|
+
|
|
682
|
+
/* ─── Défilement ────────────────────────────────────────────────────────
|
|
683
|
+
* La mécanique, elle, est relevée et livrée : `min-height: 0` ET
|
|
684
|
+
* `min-width: 0`. Sans ces deux-là, un enfant flex refuse de rétrécir en
|
|
685
|
+
* dessous de son contenu — la zone déborde, et c'est la page entière qui part
|
|
686
|
+
* en défilement horizontal. C'est la faute la plus fréquente de ce gabarit. */
|
|
687
|
+
|
|
688
|
+
.zv-scroll-y {
|
|
689
|
+
min-height: 0;
|
|
690
|
+
min-width: 0;
|
|
691
|
+
flex: 1 1 0%;
|
|
692
|
+
overflow-y: auto;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/* ⚠️ HABILLAGE DES BARRES : trou franc, vérifié deux fois. `grep -rn scrollbar`
|
|
696
|
+
* sur tout le source de Mémoire ne renvoie AUCUNE occurrence — les cinq zones
|
|
697
|
+
* défilantes utilisent la barre native du système. Il n'existe donc pas de
|
|
698
|
+
* valeur relevée pour la largeur, le pouce ou le rail, et ce module n'en
|
|
699
|
+
* applique aucune par défaut.
|
|
700
|
+
* L'habillage ci-dessous est OPT-IN et n'emploie que des mots-clés plus un
|
|
701
|
+
* token existant : aucune dimension inventée. ::-webkit-scrollbar est écarté
|
|
702
|
+
* pour deux raisons — il exigerait une largeur en pixels sortie de nulle part,
|
|
703
|
+
* et il remplace les barres flottantes de macOS/iOS par des barres permanentes
|
|
704
|
+
* qui mangent la colonne. */
|
|
705
|
+
.zv-scrollbar {
|
|
706
|
+
scrollbar-width: thin;
|
|
707
|
+
scrollbar-color: var(--line) transparent;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/* ─── Mobile ────────────────────────────────────────────────────────────
|
|
711
|
+
* Point de rupture unique du paquet. Les deux jeux de valeurs sont relevés
|
|
712
|
+
* (Mémoire les fait basculer à 1024px) ; seul le seuil est une convention.
|
|
713
|
+
* Sous ce seuil, tout ce qui se touche fait au moins --tap. */
|
|
714
|
+
|
|
715
|
+
@media (max-width: 767px) {
|
|
716
|
+
/* 224px de colonne à côté du contenu sur un écran de 375px ne laisse rien à
|
|
717
|
+
* lire : la colonne cède la place au couple barre + tiroir. */
|
|
718
|
+
.zv-sidebar {
|
|
719
|
+
display: none;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.zv-topbar {
|
|
723
|
+
display: flex;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.zv-sidelink {
|
|
727
|
+
min-height: var(--tap);
|
|
728
|
+
padding-block: 11px;
|
|
729
|
+
font-size: var(--app-lg);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.zv-chrome-btn {
|
|
733
|
+
width: var(--tap);
|
|
734
|
+
height: var(--tap);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/* DÉRIVÉ, pas relevé, et c'est une conséquence de la ligne au-dessus : la
|
|
738
|
+
* croix passe de 26 à 40px et son bord gauche remonte à 48px du bord droit,
|
|
739
|
+
* au-delà de la réserve de 46px de la tête. 40 (bouton) + 8 (son décalage)
|
|
740
|
+
* + 8 (garde) : le logo cesse de passer dessous. */
|
|
741
|
+
.zv-drawer .zv-sidebar__head > :first-child {
|
|
742
|
+
padding-right: calc(var(--tap) + 16px);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.zv-scrim {
|
|
746
|
+
display: block;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.zv-drawer {
|
|
750
|
+
display: block;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/* La feuille se cale À RAS sous la barre.
|
|
754
|
+
*
|
|
755
|
+
* La production pose top: 53px pour une barre de 52 (citation.tsx:101
|
|
756
|
+
* contre app-shell.tsx:268), en supposant que le filet s'ajoute à la
|
|
757
|
+
* hauteur. Il ne s'y ajoute pas : box-sizing: border-box le peint À
|
|
758
|
+
* L'INTÉRIEUR des 52px. Ce pixel n'empile donc rien, il ouvre une couture
|
|
759
|
+
* par laquelle le fond de page transparaît — et la feuille portant en
|
|
760
|
+
* plus son propre filet haut, le trait apparaissait doublé.
|
|
761
|
+
*
|
|
762
|
+
* Corrigé ici : la feuille commence à 52, et n'a pas de filet propre —
|
|
763
|
+
* celui de la barre les sépare déjà. C'est un écart ASSUMÉ au relevé, sur
|
|
764
|
+
* un artefact et non sur une intention. */
|
|
765
|
+
.zv-cite-panel {
|
|
766
|
+
position: fixed;
|
|
767
|
+
inset-inline: 0;
|
|
768
|
+
top: var(--topbar-h);
|
|
769
|
+
bottom: 0;
|
|
770
|
+
z-index: 70;
|
|
771
|
+
height: auto;
|
|
772
|
+
width: auto;
|
|
773
|
+
border-left: 0;
|
|
774
|
+
border-top: 0;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.zv-cite-panel__body {
|
|
778
|
+
padding: 12px;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/* Le menu s'ouvre sous un déclencheur qui peut être près du bord droit :
|
|
782
|
+
* le plafond en vw l'empêche de sortir de l'écran et d'emporter la page. */
|
|
783
|
+
.zv-menu {
|
|
784
|
+
max-width: calc(100vw - 40px);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.zv-menu__item {
|
|
788
|
+
display: flex;
|
|
789
|
+
align-items: center;
|
|
790
|
+
min-height: var(--tap);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/* Les chevrons sont des glyphes d'une dizaine de pixels : sans ce retrait,
|
|
794
|
+
* la cible reste celle du caractère, impossible à viser au doigt. */
|
|
795
|
+
.zv-pager__arrow {
|
|
796
|
+
padding: 8px;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.zv-pager__rank {
|
|
800
|
+
padding-block: 8px;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.zv-pager--foot {
|
|
804
|
+
flex-direction: column;
|
|
805
|
+
align-items: flex-start;
|
|
806
|
+
gap: 4px;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.zv-pager__step {
|
|
810
|
+
display: flex;
|
|
811
|
+
align-items: center;
|
|
812
|
+
min-height: var(--tap);
|
|
813
|
+
}
|
|
814
|
+
}
|