@zevra/ui 0.1.0 → 0.2.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 +56 -0
- package/README.md +26 -0
- package/icons/index.json +210 -0
- package/icons/sprite.svg +36 -0
- package/package.json +9 -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
package/src/forms.css
ADDED
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
/* Zevra UI — formulaires : champ, recherche, zone de texte, sélection,
|
|
2
|
+
* case/radio, interrupteur, puces de filtre, zone de dépôt.
|
|
3
|
+
*
|
|
4
|
+
* DÉPEND de tokens.css (toutes les valeurs) et de base.css (reset
|
|
5
|
+
* box-sizing, anneau :focus-visible). RIEN D'AUTRE : ce module ne lit ni
|
|
6
|
+
* layout.css, ni components.css, ni motion.css. C'est volontaire — une app
|
|
7
|
+
* purement applicative n'a aucune raison de charger la mise en page de la
|
|
8
|
+
* vitrine.
|
|
9
|
+
*
|
|
10
|
+
* Ce qu'il NE porte PAS :
|
|
11
|
+
* · le menu déroulé par .zv-select — il appartient à navigation.css
|
|
12
|
+
* (.zv-menu) ; ce module pose l'ancre (.zv-select-anchor) et le
|
|
13
|
+
* déclencheur, rien de plus ;
|
|
14
|
+
* · le bouton d'envoi d'un formulaire — c'est .zv-btn (components.css) ;
|
|
15
|
+
* · l'alerte de bloc (role="alert") — module de retours.
|
|
16
|
+
*
|
|
17
|
+
* PROVENANCE. Relevé dans l'application Mémoire (production, mêmes tokens de
|
|
18
|
+
* charte) sur six écrans : /connexion, /chat, /repertoire, /bibliotheque,
|
|
19
|
+
* /logs, /import. Deux écrans de plus ont été rouverts APRÈS le premier
|
|
20
|
+
* relevé, parce qu'ils contiennent des motifs que les six premiers n'ont
|
|
21
|
+
* pas : /admin (add-member-form.tsx, billing-card.tsx) pour les deux seuls
|
|
22
|
+
* <select> natifs et le seul message d'erreur au niveau du champ. Restent
|
|
23
|
+
* NON relevés : /tableau-de-bord et /documents/[slug]. Chaque section dit ce
|
|
24
|
+
* qui est relevé et ce qui est dérivé ; rien n'est comblé en silence.
|
|
25
|
+
*
|
|
26
|
+
* VOCABULAIRE. Le relevé emploie huit paliers de texte dans 5,5 px et une
|
|
27
|
+
* demi-douzaine de gris intermédiaires ; tokens.css les regroupe. Les
|
|
28
|
+
* rabattements appliqués ici, une fois pour toutes :
|
|
29
|
+
* #4A5468 et #727C93 → var(--ink-2) / var(--muted)
|
|
30
|
+
* #9AA3B5 (placeholder) → var(--label)
|
|
31
|
+
* #C9CEDD (filet ferme) → var(--accent-line)
|
|
32
|
+
* #FDFDFF (survol papier) → var(--panel)
|
|
33
|
+
* 13/14/12,5/11,5 px → var(--app) / var(--app-lg) / var(--app-sm)
|
|
34
|
+
* 11/10,5/9,5/8,5 px mono → var(--mono-lg) / var(--mono) / var(--mono-sm)
|
|
35
|
+
* Deux d'entre eux font disparaître une nuance du relevé : c'est dit sur
|
|
36
|
+
* place, à l'endroit exact où la nuance tombe.
|
|
37
|
+
*
|
|
38
|
+
* POINT DE RUPTURE : 768px, comme les autres modules. Les sources basculent
|
|
39
|
+
* à 1024px (`lg` de Tailwind) : les valeurs sont reprises telles quelles,
|
|
40
|
+
* seul le seuil change.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/* ─── Groupe de champ ───────────────────────────────────────────────────
|
|
44
|
+
* Intitulé au-dessus, contrôle, puis aide ou erreur. Relevé sur les
|
|
45
|
+
* formulaires de connexion (login-card.tsx:16-18, 163-200 ;
|
|
46
|
+
* reset-password-card.tsx:102-140) et confirmé sur /admin
|
|
47
|
+
* (add-member-form.tsx). L'écart intitulé → contrôle vaut 6px et non 8 :
|
|
48
|
+
* c'est le `mt-[6px]` du champ, pas une estimation. */
|
|
49
|
+
|
|
50
|
+
.zv-field {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 6px;
|
|
54
|
+
/* Sans min-width:0, un champ placé dans une ligne flex refuse de rétrécir
|
|
55
|
+
* sous la largeur de son placeholder et fait déborder la page. */
|
|
56
|
+
min-width: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Ligne d'intitulé quand un lien l'accompagne (« Mot de passe oublié ? ») :
|
|
60
|
+
* les deux se lisent sur la même ligne de base plutôt que de s'empiler.
|
|
61
|
+
* Gouttière NULLE ici : le relevé ferme l'écart au-dessus du seuil
|
|
62
|
+
* (login-card.tsx:180, `gap-3 … lg:gap-0`) et laisse space-between faire
|
|
63
|
+
* tout le travail. Les 12px sont une valeur MOBILE, ils sont en fin de
|
|
64
|
+
* fichier. */
|
|
65
|
+
.zv-field__head {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: baseline;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
gap: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Les capitales sont LITTÉRALES dans le JSX de Mémoire. Elles passent en CSS
|
|
73
|
+
* ici : recopiées dans le contenu, elles seraient épelées lettre par lettre
|
|
74
|
+
* par un lecteur d'écran. */
|
|
75
|
+
.zv-field__label {
|
|
76
|
+
display: block;
|
|
77
|
+
font-family: var(--font-mono);
|
|
78
|
+
font-size: var(--mono);
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
letter-spacing: 0.12em;
|
|
81
|
+
text-transform: uppercase;
|
|
82
|
+
color: var(--label);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.zv-field__help {
|
|
86
|
+
font-size: var(--app-sm);
|
|
87
|
+
color: var(--muted);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Le message d'erreur AU NIVEAU DU CHAMP existe — une seule fois dans toute
|
|
91
|
+
* l'application, hors des six écrans du relevé initial
|
|
92
|
+
* (add-member-form.tsx:94). Ses valeurs propres (taille, graisse, écart)
|
|
93
|
+
* n'ont PAS été relevées : celles ci-dessous sont dérivées du reste du
|
|
94
|
+
* groupe de champ et de la teinte de l'alerte de bloc. Aucune couleur
|
|
95
|
+
* nouvelle n'est introduite. */
|
|
96
|
+
.zv-field__err {
|
|
97
|
+
font-size: var(--app-sm);
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
color: var(--err);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ─── Champ de saisie ───────────────────────────────────────────────────
|
|
103
|
+
* Référence : le champ des formulaires de connexion, seul <input> bordé de
|
|
104
|
+
* l'application (login-card.tsx:18), au motif strictement identique dans
|
|
105
|
+
* /admin. Les 10px/13px de l'inventaire venaient du CADRE de recherche, pas
|
|
106
|
+
* d'un champ : ils restent sur .zv-search. */
|
|
107
|
+
|
|
108
|
+
.zv-input {
|
|
109
|
+
width: 100%;
|
|
110
|
+
min-width: 0;
|
|
111
|
+
border: 1px solid var(--line);
|
|
112
|
+
/* Angle vif : aucun rounded-* sur un seul champ des huit écrans. Posé
|
|
113
|
+
* explicitement, sinon Safari arrondit les champs de lui-même. */
|
|
114
|
+
border-radius: 0;
|
|
115
|
+
background: var(--white);
|
|
116
|
+
padding: 9px 12px;
|
|
117
|
+
/* Un <input> n'hérite pas de la police du document : sans cette ligne, le
|
|
118
|
+
* champ repasse en police système et jure avec le reste de la page. */
|
|
119
|
+
font-family: var(--font-text);
|
|
120
|
+
font-size: var(--app);
|
|
121
|
+
color: var(--ink);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.zv-input::placeholder {
|
|
125
|
+
color: var(--label);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Relevé (`focus:border-accent-line`) : c'est :focus et non :focus-visible,
|
|
129
|
+
* la bordure réagit donc aussi à la souris. L'anneau clavier de base.css
|
|
130
|
+
* s'y AJOUTE — on ne reprend PAS le `input { outline: none }` de la prod,
|
|
131
|
+
* qui laissait l'application inutilisable au clavier. */
|
|
132
|
+
.zv-input:focus {
|
|
133
|
+
border-color: var(--accent-line);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Identifiant d'organisation : même champ, chiffres et tirets alignés
|
|
137
|
+
* (login-card.tsx:145). */
|
|
138
|
+
.zv-input--mono {
|
|
139
|
+
font-family: var(--font-mono);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* <select> NATIF, habillé comme le champ. C'est le motif de sélection
|
|
143
|
+
* accessible de l'application, relevé deux fois (add-member-form.tsx:82,
|
|
144
|
+
* billing-card.tsx:135) avec exactement les valeurs de .zv-input —
|
|
145
|
+
* `border-line bg-white px-3 py-[9px] text-[13px] text-ink`. D'où un simple
|
|
146
|
+
* modificateur : tout est déjà là.
|
|
147
|
+
*
|
|
148
|
+
* L'`appearance` n'est PAS neutralisée : la flèche, l'ouverture au clavier,
|
|
149
|
+
* la liste native et son annonce viennent du navigateur. La remplacer
|
|
150
|
+
* imposerait de redessiner tout cela, et le relevé ne le fait pas non plus.
|
|
151
|
+
* À ne pas confondre avec .zv-select, qui est un déclencheur de menu maison
|
|
152
|
+
* — voir plus bas. */
|
|
153
|
+
.zv-input--select {
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Champ nu : ni fond ni filet propres, c'est le CADRE qui l'entoure qui fait
|
|
158
|
+
* le champ (composeur du chat). Lui redonner une bordure dessinerait deux
|
|
159
|
+
* cadres imbriqués. */
|
|
160
|
+
.zv-input--bare {
|
|
161
|
+
flex: 1 1 0%;
|
|
162
|
+
border: none;
|
|
163
|
+
background: transparent;
|
|
164
|
+
padding: 2px 0;
|
|
165
|
+
font-size: var(--app-lg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ─── Recherche ─────────────────────────────────────────────────────────
|
|
169
|
+
* Cadre en flex, loupe au filet, saisie sans bordure propre. Deux relevés
|
|
170
|
+
* identiques (repertoire-client.tsx:59-78, bibliotheque-client.tsx:185-204).
|
|
171
|
+
*
|
|
172
|
+
* La loupe est un SVG dans le balisage, pas un fond CSS : viewBox 0 0 13 13,
|
|
173
|
+
* fill="none", stroke-width="1.5",
|
|
174
|
+
* <circle cx="5.5" cy="5.5" r="4"/><line x1="8.6" y1="8.6" x2="12" y2="12"/> */
|
|
175
|
+
|
|
176
|
+
.zv-search {
|
|
177
|
+
display: flex;
|
|
178
|
+
width: auto;
|
|
179
|
+
min-width: 240px;
|
|
180
|
+
flex: 1 1 0%;
|
|
181
|
+
align-items: center;
|
|
182
|
+
gap: 9px;
|
|
183
|
+
border: 1px solid var(--line);
|
|
184
|
+
background: var(--white);
|
|
185
|
+
padding-inline: 13px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* DÉRIVÉ : la bordure de focus relevée sur .zv-input, portée par le cadre.
|
|
189
|
+
*
|
|
190
|
+
* DEUX MARQUAGES, ASSUMÉS. L'anneau :focus-visible de base.css est un
|
|
191
|
+
* sélecteur universel : au clavier il se dessine AUSSI autour du champ nu,
|
|
192
|
+
* à l'intérieur du cadre, en plus de ce filet. On le garde tel quel plutôt
|
|
193
|
+
* que de trancher l'un des deux :
|
|
194
|
+
* · supprimer l'anneau (outline:none) rendrait le champ nu invisible au
|
|
195
|
+
* clavier — c'est exactement la faute de la production ;
|
|
196
|
+
* · supprimer ce filet priverait la souris de tout retour, alors que le
|
|
197
|
+
* relevé lui en donne un sur les champs bordés.
|
|
198
|
+
* La redondance coûte moins cher que l'une ou l'autre perte. */
|
|
199
|
+
.zv-search:focus-within {
|
|
200
|
+
border-color: var(--accent-line);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.zv-search__icon {
|
|
204
|
+
flex: none;
|
|
205
|
+
width: 13px;
|
|
206
|
+
height: 13px;
|
|
207
|
+
/* Même gris que le placeholder : la loupe est une invite, pas une donnée. */
|
|
208
|
+
stroke: var(--label);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.zv-search__input {
|
|
212
|
+
min-width: 0;
|
|
213
|
+
flex: 1 1 0%;
|
|
214
|
+
border: none;
|
|
215
|
+
background: transparent;
|
|
216
|
+
padding-block: 10px;
|
|
217
|
+
font-family: var(--font-text);
|
|
218
|
+
font-size: var(--app);
|
|
219
|
+
color: var(--ink);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.zv-search__input::placeholder {
|
|
223
|
+
color: var(--label);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ─── Zone de texte ─────────────────────────────────────────────────────
|
|
227
|
+
* DÉCISION, PAS UN RELEVÉ : il n'existe AUCUN <textarea> dans l'application,
|
|
228
|
+
* sur aucun des huit écrans ouverts. Le composeur du chat est un <input> où
|
|
229
|
+
* Entrée envoie (chat-client.tsx:365). Le bloc ci-dessous est dérivé mot à
|
|
230
|
+
* mot de .zv-input ; seules min-height et resize s'y ajoutent — sans elles
|
|
231
|
+
* le champ se rend sur une ligne et le composant n'existe pas. min-height
|
|
232
|
+
* est la SEULE valeur de ce module sans aucune source, dans aucun écran.
|
|
233
|
+
*
|
|
234
|
+
* Ne PAS remplacer l'input du composeur par ce bloc : ce serait un
|
|
235
|
+
* changement de comportement (Entrée n'enverrait plus). */
|
|
236
|
+
|
|
237
|
+
.zv-textarea {
|
|
238
|
+
width: 100%;
|
|
239
|
+
min-width: 0;
|
|
240
|
+
min-height: 96px; /* sans source — voir ci-dessus */
|
|
241
|
+
border: 1px solid var(--line);
|
|
242
|
+
border-radius: 0;
|
|
243
|
+
background: var(--white);
|
|
244
|
+
padding: 9px 12px;
|
|
245
|
+
font-family: var(--font-text);
|
|
246
|
+
font-size: var(--app);
|
|
247
|
+
/* 1.6 : l'interligne du corps de texte du paquet (base.css, .zv-body). */
|
|
248
|
+
line-height: 1.6;
|
|
249
|
+
color: var(--ink);
|
|
250
|
+
/* Vertical seul : en libre, l'utilisateur élargit le champ hors de sa
|
|
251
|
+
* colonne et casse la mise en page. */
|
|
252
|
+
resize: vertical;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.zv-textarea::placeholder {
|
|
256
|
+
color: var(--label);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.zv-textarea:focus {
|
|
260
|
+
border-color: var(--accent-line);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* ─── Déclencheur de sélection (menu maison) ────────────────────────────
|
|
264
|
+
* « PÉRIMÈTRE : Toute la base ▾ » (chat-client.tsx:168-175). Ce n'est pas un
|
|
265
|
+
* <select> : c'est un bouton qui ouvre .zv-menu (navigation.css). Pour un
|
|
266
|
+
* vrai choix dans une liste, préférer .zv-input--select, qui est natif.
|
|
267
|
+
*
|
|
268
|
+
* ANCRAGE — arbitrage à consigner dans DECISIONS.md, les deux modules ne
|
|
269
|
+
* disent pas la même chose. Le relevé donne raison à celui-ci : c'est
|
|
270
|
+
* l'ENVELOPPE qui porte position:relative (`relative inline-block
|
|
271
|
+
* max-w-full`), pas le déclencheur. Ancrer le menu sur .zv-select
|
|
272
|
+
* fonctionnerait par accident, mais casserait son `max-width` calculé sur
|
|
273
|
+
* la fenêtre. D'où .zv-select-anchor, posée ici, à côté de son
|
|
274
|
+
* déclencheur. */
|
|
275
|
+
|
|
276
|
+
.zv-select-anchor {
|
|
277
|
+
position: relative;
|
|
278
|
+
display: inline-block;
|
|
279
|
+
max-width: 100%;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.zv-select {
|
|
283
|
+
display: inline-flex;
|
|
284
|
+
align-items: center;
|
|
285
|
+
gap: 7px;
|
|
286
|
+
border: 1px solid var(--line);
|
|
287
|
+
background: var(--white);
|
|
288
|
+
padding: 6px 11px;
|
|
289
|
+
font-family: var(--font-mono);
|
|
290
|
+
font-size: var(--mono);
|
|
291
|
+
font-weight: 600;
|
|
292
|
+
letter-spacing: 0.1em;
|
|
293
|
+
text-transform: uppercase;
|
|
294
|
+
/* La valeur courante peut être longue : elle sort du cadre plutôt que de
|
|
295
|
+
* casser la pastille en deux lignes. */
|
|
296
|
+
white-space: nowrap;
|
|
297
|
+
color: var(--ink-2);
|
|
298
|
+
cursor: pointer;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.zv-select:hover {
|
|
302
|
+
border-color: var(--accent-line);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* La valeur choisie, seule partie mise en avant du déclencheur. */
|
|
306
|
+
.zv-select strong {
|
|
307
|
+
font-weight: 700;
|
|
308
|
+
color: var(--accent);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* ▾ (U+25BE) posé dans le balisage. */
|
|
312
|
+
.zv-select__chevron {
|
|
313
|
+
font-size: var(--mono-sm);
|
|
314
|
+
color: var(--label);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* ─── Case à cocher et bouton radio ─────────────────────────────────────
|
|
318
|
+
* DÉRIVÉS INTÉGRALEMENT, ET ILS DOIVENT ÊTRE LUS COMME TELS : il n'existe
|
|
319
|
+
* zéro <input type="checkbox"> et zéro <input type="radio"> dans toute
|
|
320
|
+
* l'application — la sélection s'y fait toujours par .zv-seg ou .zv-switch.
|
|
321
|
+
* Le bloc ci-dessous réemploie la grammaire déjà en production (carré au
|
|
322
|
+
* filet --line sur --white, coché en aplat --accent, rond des pastilles)
|
|
323
|
+
* sans introduire de valeur de charte nouvelle ; ses DIMENSIONS, elles
|
|
324
|
+
* (16px, taille du glyphe, épaisseur de l'anneau intérieur), n'ont aucune
|
|
325
|
+
* source. Le jour où un vrai écran en pose, c'est lui qui tranche.
|
|
326
|
+
*
|
|
327
|
+
* ⚠️ .zv-check est DÉJÀ PRIS par la pastille de validation
|
|
328
|
+
* (components.css:482) — d'où .zv-check-box.
|
|
329
|
+
*
|
|
330
|
+
* Contrôles natifs volontairement conservés (appearance:none seulement) : le
|
|
331
|
+
* clavier, :focus-visible, [disabled] et l'annonce au lecteur d'écran
|
|
332
|
+
* viennent alors du navigateur, pas d'un <div> à réoutiller. */
|
|
333
|
+
|
|
334
|
+
.zv-check-box,
|
|
335
|
+
.zv-radio {
|
|
336
|
+
appearance: none;
|
|
337
|
+
flex: none;
|
|
338
|
+
width: 16px;
|
|
339
|
+
height: 16px;
|
|
340
|
+
margin: 0;
|
|
341
|
+
border: 1px solid var(--line);
|
|
342
|
+
border-radius: 0;
|
|
343
|
+
background: var(--white);
|
|
344
|
+
cursor: pointer;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.zv-radio {
|
|
348
|
+
border-radius: 9999px;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.zv-check-box:hover,
|
|
352
|
+
.zv-radio:hover {
|
|
353
|
+
border-color: var(--accent-line);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* Le blanc du glyphe est écrit en clair (%23fff) : une data-URI ne lit pas
|
|
357
|
+
* les variables CSS. Valeur strictement égale à --white. */
|
|
358
|
+
.zv-check-box:checked {
|
|
359
|
+
border-color: var(--accent);
|
|
360
|
+
background-color: var(--accent);
|
|
361
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='2.5,6.5 5,9 9.5,3.5'/%3E%3C/svg%3E");
|
|
362
|
+
background-position: center;
|
|
363
|
+
background-repeat: no-repeat;
|
|
364
|
+
background-size: 12px 12px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Le point du radio est creusé dans l'aplat par un anneau blanc interne :
|
|
368
|
+
* un pseudo-élément sur un <input> n'est pas fiable d'un moteur à l'autre.
|
|
369
|
+
* Ce box-shadow est une géométrie, pas une ombre de charte — d'où l'absence
|
|
370
|
+
* de token. */
|
|
371
|
+
.zv-radio:checked {
|
|
372
|
+
border-color: var(--accent);
|
|
373
|
+
background-color: var(--accent);
|
|
374
|
+
box-shadow: inset 0 0 0 4px var(--white);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* ─── Interrupteur ──────────────────────────────────────────────────────
|
|
378
|
+
* Ligne de réglage des dossiers (logs-client.tsx:267-293). Piste
|
|
379
|
+
* RECTANGULAIRE, bouton rond : relevé tel quel, ne pas arrondir la piste.
|
|
380
|
+
*
|
|
381
|
+
* ⚠️ RABATTEMENT VISIBLE : la piste éteinte était un gris ferme (#C9CEDD),
|
|
382
|
+
* rabattu sur --accent-line, qui est bleuté. L'état éteint prend donc une
|
|
383
|
+
* teinte indigo très pâle. La distinction éteint/allumé tient quand même —
|
|
384
|
+
* elle est portée par l'aplat plein et par le libellé accolé, pas par cette
|
|
385
|
+
* nuance-là. */
|
|
386
|
+
|
|
387
|
+
.zv-switch {
|
|
388
|
+
position: relative;
|
|
389
|
+
display: inline-block;
|
|
390
|
+
width: 28px;
|
|
391
|
+
height: 16px;
|
|
392
|
+
flex: none;
|
|
393
|
+
background: var(--accent-line);
|
|
394
|
+
/* La courbe est celle du paquet (motion.css, `--zv-easing`), recopiée en
|
|
395
|
+
* clair : forms.css ne dépend pas de motion.css et n'a pas à en dépendre
|
|
396
|
+
* pour une transition de couleur. Ce n'est PAS `ease` — le défaut d'un
|
|
397
|
+
* framework tiers n'a rien à faire dans le système. */
|
|
398
|
+
transition: background-color 150ms cubic-bezier(0.2, 0.7, 0.2, 1);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.zv-switch--on {
|
|
402
|
+
background: var(--accent);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.zv-switch__knob {
|
|
406
|
+
position: absolute;
|
|
407
|
+
inset-block-start: 2px;
|
|
408
|
+
inset-inline-start: 2px;
|
|
409
|
+
width: 12px;
|
|
410
|
+
height: 12px;
|
|
411
|
+
border-radius: 9999px;
|
|
412
|
+
background: var(--white);
|
|
413
|
+
transition: inset-inline-start 150ms cubic-bezier(0.2, 0.7, 0.2, 1);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.zv-switch--on .zv-switch__knob {
|
|
417
|
+
inset-inline-start: 14px;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Cible tactile portée par une enveloppe : agrandir la piste elle-même
|
|
421
|
+
* décalerait toute la ligne de réglage (cf. mobile en fin de fichier). */
|
|
422
|
+
.zv-switch-hit {
|
|
423
|
+
display: flex;
|
|
424
|
+
/* La source place la gouttière de 7px sur une enveloppe PARENTE, et
|
|
425
|
+
* n'entoure que la piste de sa zone tactile (logs-client.tsx:267-273).
|
|
426
|
+
* Elle est ramenée ici : cette classe enveloppe le contrôle ET son
|
|
427
|
+
* libellé, ce qui rend le texte cliquable — un <label> qui n'entoure que
|
|
428
|
+
* l'interrupteur laisse une cible de 28px là où il en faut 40. Sans la
|
|
429
|
+
* gouttière, le libellé vient coller à la piste. */
|
|
430
|
+
align-items: center;
|
|
431
|
+
gap: 7px;
|
|
432
|
+
cursor: pointer;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* Le libellé accolé change avec l'état : c'est lui qui porte le sens, la
|
|
436
|
+
* piste seule ne dit pas ce qui est activé. */
|
|
437
|
+
.zv-switch__text {
|
|
438
|
+
font-family: var(--font-mono);
|
|
439
|
+
font-size: var(--mono);
|
|
440
|
+
font-weight: 600;
|
|
441
|
+
color: var(--label);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.zv-switch__text--on {
|
|
445
|
+
color: var(--accent);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* ─── Puces de filtre ───────────────────────────────────────────────────
|
|
449
|
+
* Le seul mécanisme de filtrage de l'application (bibliotheque-client.tsx:
|
|
450
|
+
* 143-180, repertoire-client.tsx:79-94, logs-client.tsx:193-207).
|
|
451
|
+
*
|
|
452
|
+
* ⚠️ LA SOURCE DIVERGE, ET C'EST TRANCHÉ ICI :
|
|
453
|
+
* · gouttière du groupe — bibliothèque 5px, répertoire et logs 6px. On
|
|
454
|
+
* retient 6px : deux écrans sur trois, et c'est la valeur de .zv-tabs
|
|
455
|
+
* (navigation.css), qui héberge les mêmes puces. Deux conteneurs des
|
|
456
|
+
* mêmes puces avec deux écarts différents, c'est un défaut, pas une
|
|
457
|
+
* nuance.
|
|
458
|
+
* · taille et densité — bibliothèque descend à 11px/4-9px au-dessus du
|
|
459
|
+
* seuil, le répertoire reste à 11px/8-12px partout, en graisse 600. On
|
|
460
|
+
* retient la puce DENSE de la bibliothèque au-dessus du seuil, et la
|
|
461
|
+
* graisse 500 (la 600 est réservée à l'état sélectionné, sinon
|
|
462
|
+
* sélectionné et non sélectionné ne se distinguent plus que par la
|
|
463
|
+
* couleur).
|
|
464
|
+
* · corps — 11 et 12px se rabattent tous les deux sur --app-sm (12px) :
|
|
465
|
+
* l'écart d'un pixel entre les deux écrans disparaît, et la variante
|
|
466
|
+
* mobile de la bibliothèque avec lui. C'est le prix de l'échelle
|
|
467
|
+
* rationalisée, assumé.
|
|
468
|
+
*
|
|
469
|
+
* ⚠️ L'état sélectionné n'est porté par AUCUN aria-pressed en production :
|
|
470
|
+
* il est invisible pour un lecteur d'écran. Le paquet impose <button
|
|
471
|
+
* aria-pressed="true|false"> — et le style suit l'attribut autant que la
|
|
472
|
+
* classe, pour que le balisage correct ne coûte rien. */
|
|
473
|
+
|
|
474
|
+
.zv-seg-group {
|
|
475
|
+
display: flex;
|
|
476
|
+
flex-wrap: wrap;
|
|
477
|
+
gap: 6px;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/* ⚠️ HORS RELEVÉ. Les quatre rangées de puces du dépôt passent TOUTES à la
|
|
481
|
+
* ligne ; le motif « nowrap + défilement » n'apparaît que sur des CADRES
|
|
482
|
+
* (tableau, bande de trente jours), jamais sur une rangée de puces.
|
|
483
|
+
* Cette variante n'existe que parce que la règle du projet interdit tout
|
|
484
|
+
* défilement horizontal de PAGE : une rangée qu'on refuse d'enrouler doit
|
|
485
|
+
* alors déborder dans son propre cadre. À poser explicitement, jamais par
|
|
486
|
+
* défaut. */
|
|
487
|
+
.zv-seg-group--rail {
|
|
488
|
+
flex-wrap: nowrap;
|
|
489
|
+
overflow-x: auto;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.zv-seg {
|
|
493
|
+
border: 1px solid var(--line);
|
|
494
|
+
border-radius: 0;
|
|
495
|
+
background: var(--white);
|
|
496
|
+
padding: 4px 9px;
|
|
497
|
+
font-family: var(--font-text);
|
|
498
|
+
font-size: var(--app-sm);
|
|
499
|
+
font-weight: 500;
|
|
500
|
+
white-space: nowrap;
|
|
501
|
+
color: var(--ink-2);
|
|
502
|
+
cursor: pointer;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* La rangée de puces vit souvent dans un rail qui défile horizontalement
|
|
506
|
+
* (.zv-tabs--rail, navigation.css). L'anneau de base.css y serait rogné aux
|
|
507
|
+
* deux bords. On ne le REDÉFINIT pas — on déplace seulement son tracé à
|
|
508
|
+
* l'intérieur de la puce, où rien ne le coupe. */
|
|
509
|
+
.zv-seg:focus-visible {
|
|
510
|
+
outline-offset: -2px;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/* Sélection DOUCE (bibliothèque), la seule livrée par défaut : elle
|
|
514
|
+
* n'ajoute pas un second aplat indigo à l'écran, là où la charte n'autorise
|
|
515
|
+
* qu'un seul plein par page. */
|
|
516
|
+
.zv-seg--on,
|
|
517
|
+
.zv-seg[aria-pressed="true"] {
|
|
518
|
+
border-color: var(--accent-line);
|
|
519
|
+
background: var(--accent-soft);
|
|
520
|
+
font-weight: 600;
|
|
521
|
+
color: var(--accent);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/* Sélection PLEINE (répertoire, logs) : OPT-IN, à poser explicitement, et
|
|
525
|
+
* BORNÉE — un seul groupe plein par page, jamais sur une page qui porte
|
|
526
|
+
* déjà un bouton plein. Une page de filtres à plusieurs groupes (la
|
|
527
|
+
* bibliothèque en a deux) afficherait sinon autant d'aplats indigo que de
|
|
528
|
+
* groupes, simultanément : c'est le seul mécanisme du paquet capable de
|
|
529
|
+
* multiplier le plein sans le dire. */
|
|
530
|
+
.zv-seg--on-solid,
|
|
531
|
+
.zv-seg--on-solid[aria-pressed="true"] {
|
|
532
|
+
border-color: var(--accent);
|
|
533
|
+
background: var(--accent);
|
|
534
|
+
color: var(--white);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.zv-seg__count {
|
|
538
|
+
margin-inline-start: 6px;
|
|
539
|
+
font-family: var(--font-mono);
|
|
540
|
+
font-size: var(--mono);
|
|
541
|
+
color: var(--label);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/* Niveaux de log : libellés de longueur inégale (INFO, WARN, ERROR) centrés
|
|
545
|
+
* pour que la rangée ne danse pas d'une puce à l'autre. */
|
|
546
|
+
.zv-seg--mono {
|
|
547
|
+
justify-content: center;
|
|
548
|
+
padding: 4px 8px;
|
|
549
|
+
font-family: var(--font-mono);
|
|
550
|
+
font-size: var(--mono-sm);
|
|
551
|
+
font-weight: 600;
|
|
552
|
+
letter-spacing: 0.06em;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* ─── Zone de dépôt ─────────────────────────────────────────────────────
|
|
556
|
+
* Elle tient lieu d'action principale de l'écran d'import : il n'y a aucun
|
|
557
|
+
* bouton plein sur cette page (import-client.tsx:144-202).
|
|
558
|
+
*
|
|
559
|
+
* ⚠️ En production c'est un <div onClick> sans role ni tabIndex : la page
|
|
560
|
+
* d'import est inatteignable au clavier. Le paquet impose role="button" +
|
|
561
|
+
* tabindex="0" (l'anneau de base.css suit alors tout seul).
|
|
562
|
+
*
|
|
563
|
+
* L'icône est un SVG du balisage (import-client.tsx:159-166) : viewBox
|
|
564
|
+
* 0 0 30 30, fill="none", stroke="var(--accent)", stroke-width="1.5",
|
|
565
|
+
* stroke-linecap="round" — et PAS de stroke-linejoin, le tracé n'a qu'un
|
|
566
|
+
* angle franc et la source ne le pose pas :
|
|
567
|
+
* <line x1="15" y1="4" x2="15" y2="18"/>
|
|
568
|
+
* <polyline points="9.5,12.5 15,18 20.5,12.5"/>
|
|
569
|
+
* <path d="M5 21 v3 a2 2 0 0 0 2 2 h16 a2 2 0 0 0 2-2 v-3"/> */
|
|
570
|
+
|
|
571
|
+
.zv-drop {
|
|
572
|
+
display: flex;
|
|
573
|
+
flex-direction: column;
|
|
574
|
+
align-items: center;
|
|
575
|
+
gap: 10px;
|
|
576
|
+
/* 1,5px : l'épaisseur du pointillé est celle du tracé de l'icône, les deux
|
|
577
|
+
* se répondent. */
|
|
578
|
+
border: 1.5px dashed var(--accent-line);
|
|
579
|
+
background: var(--white);
|
|
580
|
+
padding: 38px 24px;
|
|
581
|
+
cursor: pointer;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/* Survol et survol de dépôt rendus à l'identique : la zone répond pareil que
|
|
585
|
+
* le fichier soit traîné ou non, sinon le retour visuel se contredit.
|
|
586
|
+
*
|
|
587
|
+
* ⚠️ RABATTEMENT VISIBLE : le fond de survol du relevé (#FDFDFF) tombe sur
|
|
588
|
+
* --panel, à peine distinct du --white de repos. Le fond ne SIGNALE donc
|
|
589
|
+
* plus rien — le retour visuel repose entièrement sur le filet qui passe à
|
|
590
|
+
* --accent. Ne pas le supprimer en croyant que le fond compense. */
|
|
591
|
+
.zv-drop:hover,
|
|
592
|
+
.zv-drop.is-dragging {
|
|
593
|
+
border-color: var(--accent);
|
|
594
|
+
background: var(--panel);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/* Stockage indisponible : la zone reste visible mais inerte — la masquer
|
|
598
|
+
* ferait croire à une page cassée. */
|
|
599
|
+
.zv-drop--disabled {
|
|
600
|
+
cursor: not-allowed;
|
|
601
|
+
border-color: var(--line);
|
|
602
|
+
background: var(--panel);
|
|
603
|
+
opacity: 0.6;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.zv-drop--disabled:hover {
|
|
607
|
+
border-color: var(--line);
|
|
608
|
+
background: var(--panel);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* 15px : c'est une phrase, pas un libellé d'interface — d'où le palier de
|
|
612
|
+
* corps de texte (--body) plutôt que l'échelle applicative, qui s'arrête à
|
|
613
|
+
* 14px. */
|
|
614
|
+
.zv-drop__title {
|
|
615
|
+
font-size: var(--body);
|
|
616
|
+
font-weight: 500;
|
|
617
|
+
color: var(--ink);
|
|
618
|
+
text-align: center;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.zv-drop__hint {
|
|
622
|
+
font-size: var(--app-sm);
|
|
623
|
+
color: var(--muted);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.zv-drop__hint a,
|
|
627
|
+
.zv-drop__hint strong {
|
|
628
|
+
font-weight: 600;
|
|
629
|
+
color: var(--accent);
|
|
630
|
+
text-decoration: underline;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Sous le seuil il n'y a pas de glisser-déposer : le lien souligné cède la
|
|
634
|
+
* place à une vraie cible tactile. LES DEUX NŒUDS SONT POSÉS EN PERMANENCE
|
|
635
|
+
* DANS LE BALISAGE et c'est CE fichier qui les échange, au seul seuil du
|
|
636
|
+
* paquet. Ne pas y ajouter .zv-only-desktop / .zv-only-mobile (layout.css) :
|
|
637
|
+
* les deux mécanismes se disputeraient la même déclaration `display`, et le
|
|
638
|
+
* module tirerait la mise en page de la vitrine derrière lui. */
|
|
639
|
+
.zv-drop__browse {
|
|
640
|
+
display: none;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.zv-drop__spec {
|
|
644
|
+
font-family: var(--font-mono);
|
|
645
|
+
font-size: var(--mono);
|
|
646
|
+
font-weight: 500;
|
|
647
|
+
color: var(--label);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/* Le vrai <input type="file"> est retiré du flux, la zone entière lui sert
|
|
651
|
+
* de déclencheur. Nommé en élément de .zv-drop, et non `.zv-file` : le
|
|
652
|
+
* paquet a par ailleurs une vignette de format (.zv-filetype, data.css) et
|
|
653
|
+
* les deux se liraient comme un seul bloc.
|
|
654
|
+
*
|
|
655
|
+
* Conséquence du retrait du flux : l'input n'est PAS focusable — d'où le
|
|
656
|
+
* role/tabindex exigés sur .zv-drop ci-dessus. Remettre value='' après
|
|
657
|
+
* chaque sélection, sinon redéposer le même fichier ne déclenche rien. */
|
|
658
|
+
.zv-drop__input {
|
|
659
|
+
display: none;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/* ─── États transverses ─────────────────────────────────────────────────
|
|
663
|
+
* HORS RELEVÉ pour l'essentiel, et assumé : la production ne désactive aucun
|
|
664
|
+
* champ (seul le bouton d'envoi passe en `cursor-wait; opacity:.7` pendant
|
|
665
|
+
* la requête) et ne signale l'erreur au niveau du champ qu'à un seul
|
|
666
|
+
* endroit. Un système livré sans ces deux états oblige chaque app à les
|
|
667
|
+
* réinventer, chacune à sa façon. Les valeurs sont empruntées, sans
|
|
668
|
+
* exception, à ce qui existe déjà : le traitement désactivé de
|
|
669
|
+
* .zv-drop--disabled et le filet de l'alerte d'erreur. */
|
|
670
|
+
|
|
671
|
+
.zv-input[disabled],
|
|
672
|
+
.zv-textarea[disabled],
|
|
673
|
+
.zv-select[disabled],
|
|
674
|
+
.zv-seg[disabled],
|
|
675
|
+
.zv-check-box[disabled],
|
|
676
|
+
.zv-radio[disabled] {
|
|
677
|
+
cursor: not-allowed;
|
|
678
|
+
border-color: var(--line);
|
|
679
|
+
background: var(--panel);
|
|
680
|
+
opacity: 0.6;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* Le survol ne doit pas rattraper un contrôle désactivé : sans ces règles,
|
|
684
|
+
* .zv-seg:hover et .zv-select:hover repeignent le filet et le contrôle a
|
|
685
|
+
* l'air actif. */
|
|
686
|
+
.zv-select[disabled]:hover,
|
|
687
|
+
.zv-check-box[disabled]:hover,
|
|
688
|
+
.zv-radio[disabled]:hover {
|
|
689
|
+
border-color: var(--line);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/* Champ en erreur : seul le filet change. Un fond rouge sur un champ à
|
|
693
|
+
* remplir rendrait la saisie illisible — l'aplat --err-soft reste réservé au
|
|
694
|
+
* bandeau d'alerte. Le sélecteur suit l'attribut autant que la classe :
|
|
695
|
+
* aria-invalid est ce qui rend l'erreur audible, il ne doit rien coûter. */
|
|
696
|
+
.zv-field--err .zv-input,
|
|
697
|
+
.zv-field--err .zv-textarea,
|
|
698
|
+
.zv-field--err .zv-select,
|
|
699
|
+
.zv-input[aria-invalid="true"],
|
|
700
|
+
.zv-textarea[aria-invalid="true"] {
|
|
701
|
+
border-color: var(--err-line);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* Le focus l'emporte sur l'erreur : pendant la correction, l'utilisateur doit
|
|
705
|
+
* voir où il est, pas ce qui était faux. */
|
|
706
|
+
.zv-field--err .zv-input:focus,
|
|
707
|
+
.zv-field--err .zv-textarea:focus,
|
|
708
|
+
.zv-input[aria-invalid="true"]:focus,
|
|
709
|
+
.zv-textarea[aria-invalid="true"]:focus {
|
|
710
|
+
border-color: var(--accent-line);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* ─── Mobile ────────────────────────────────────────────────────────────
|
|
714
|
+
* Valeurs relevées aux mêmes endroits, sous le seuil des sources (1024px),
|
|
715
|
+
* ramenées ici au seuil unique du paquet.
|
|
716
|
+
*
|
|
717
|
+
* NOTE : le micro-libellé du groupe de champ ne bouge PAS ici. Le relevé le
|
|
718
|
+
* grossit d'un pixel sous le seuil (9,5 → 10,5) ; les deux valeurs tombent
|
|
719
|
+
* sur --mono. La nuance n'existe plus, et une règle qui ne change rien vaut
|
|
720
|
+
* moins qu'une ligne qui dit pourquoi. */
|
|
721
|
+
|
|
722
|
+
@media (max-width: 767px) {
|
|
723
|
+
/* Le lien qui accompagne l'intitulé devient une cible tactile : la ligne
|
|
724
|
+
* de base ne tient plus quand les deux éléments font 40px de haut, et il
|
|
725
|
+
* leur faut alors une gouttière (gap-3), là où space-between suffisait. */
|
|
726
|
+
.zv-field__head {
|
|
727
|
+
align-items: center;
|
|
728
|
+
gap: 12px;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* 44px, pas --tap (40px) : c'est la valeur des formulaires de connexion et
|
|
732
|
+
* du pavé d'import, les deux seuls endroits où l'on saisit au doigt.
|
|
733
|
+
* 16px : en dessous, Safari iOS zoome à la mise au point du champ et
|
|
734
|
+
* décale toute la page — ce n'est pas un choix typographique, aucun token
|
|
735
|
+
* n'a à porter cette valeur. */
|
|
736
|
+
.zv-input,
|
|
737
|
+
.zv-textarea {
|
|
738
|
+
min-height: 44px;
|
|
739
|
+
font-size: 16px;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/* Les <select> de /admin, eux, sont relevés à 40px (`min-h-10 lg:min-h-0`)
|
|
743
|
+
* et non à 44 : un autre écran, une autre main. */
|
|
744
|
+
.zv-input--select {
|
|
745
|
+
min-height: var(--tap);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.zv-input--bare {
|
|
749
|
+
min-height: var(--tap);
|
|
750
|
+
font-size: var(--app-lg);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/* Le cadre passe pleine largeur. Il n'est PAS figé (`flex: none`) : le
|
|
754
|
+
* relevé lui laisse le `0 1 auto` par défaut, il peut donc encore se
|
|
755
|
+
* comprimer si la ligne qui l'accueille l'exige. */
|
|
756
|
+
.zv-search {
|
|
757
|
+
width: 100%;
|
|
758
|
+
min-width: 0;
|
|
759
|
+
flex: 0 1 auto;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.zv-search__input {
|
|
763
|
+
padding-block: 12px;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/* Le déclencheur peut passer à la ligne : sa valeur courante est un nom de
|
|
767
|
+
* dossier libre, qui déborderait sinon de l'écran. */
|
|
768
|
+
.zv-select {
|
|
769
|
+
min-height: var(--tap);
|
|
770
|
+
flex-wrap: wrap;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* La puce se desserre pour devenir une cible tactile. Le corps, lui, ne
|
|
774
|
+
* change plus : 11 et 12px se rabattent sur le même palier (voir la
|
|
775
|
+
* section « Puces de filtre »). */
|
|
776
|
+
.zv-seg {
|
|
777
|
+
display: flex;
|
|
778
|
+
align-items: center;
|
|
779
|
+
min-height: var(--tap);
|
|
780
|
+
padding: 8px 12px;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/* Cible de 40px sans gonfler la ligne de réglage : la marge négative
|
|
784
|
+
* annule exactement le padding ajouté. */
|
|
785
|
+
.zv-switch-hit {
|
|
786
|
+
margin-block: -12px;
|
|
787
|
+
padding-block: 12px;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.zv-drop {
|
|
791
|
+
padding: 32px 16px;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/* Pas de glisser-déposer au doigt : le lien souligné est remplacé par une
|
|
795
|
+
* cible pleine largeur. Les deux nœuds appartiennent à ce module, l'échange
|
|
796
|
+
* se fait ici et nulle part ailleurs. */
|
|
797
|
+
.zv-drop__hint {
|
|
798
|
+
display: none;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.zv-drop__browse {
|
|
802
|
+
display: flex;
|
|
803
|
+
width: 100%;
|
|
804
|
+
max-width: 280px;
|
|
805
|
+
min-height: 44px;
|
|
806
|
+
align-items: center;
|
|
807
|
+
justify-content: center;
|
|
808
|
+
border: 1px solid var(--accent-line);
|
|
809
|
+
background: var(--accent-soft);
|
|
810
|
+
padding-inline: 16px;
|
|
811
|
+
font-size: var(--app);
|
|
812
|
+
font-weight: 600;
|
|
813
|
+
color: var(--accent);
|
|
814
|
+
}
|
|
815
|
+
}
|