@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
package/src/feedback.css
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
/* Zevra UI — retours d'état : bandeaux, étiquettes, pastilles, absence,
|
|
2
|
+
* travail en cours, curseur de flux.
|
|
3
|
+
*
|
|
4
|
+
* Dépend de tokens.css et de base.css (reset, fonte de corps, focus clavier
|
|
5
|
+
* global). Indépendant des autres modules.
|
|
6
|
+
*
|
|
7
|
+
* Tokens réellement employés ici, tous vérifiés dans tokens.css :
|
|
8
|
+
* couleurs — --ink, --ink-2, --muted, --label, --line, --hairline, --white,
|
|
9
|
+
* --accent, --accent-line, --accent-soft,
|
|
10
|
+
* --ok, --ok-soft, --ok-line,
|
|
11
|
+
* --warn, --warn-soft, --warn-line, --warn-ink,
|
|
12
|
+
* --err, --err-soft, --err-line ;
|
|
13
|
+
* typo — --font-display, --font-mono, --app, --app-sm,
|
|
14
|
+
* --app-h2, --mono, --mono-sm.
|
|
15
|
+
* Aucun autre. Il n'existe PAS de --ink-3…--ink-7, pas de --line-soft, pas de
|
|
16
|
+
* --line-firm, pas d'échelles --text-* ni --mono-xs/--mono-2xs : une version
|
|
17
|
+
* antérieure de cet en-tête les déclarait en dépendance, ce qui rendait le
|
|
18
|
+
* module inauditable (rien ne disait quelle valeur portait --ink-6) et l'aurait
|
|
19
|
+
* rendu sans couleur ni taille chez le consommateur. Les valeurs relevées qui
|
|
20
|
+
* tombaient sur ces noms sont rabattues sur les paliers du paquet, et chaque
|
|
21
|
+
* rabattement qui déplace le rendu est écrit en commentaire à l'endroit exact
|
|
22
|
+
* où il est fait.
|
|
23
|
+
*
|
|
24
|
+
* Relevé sur l'application Mémoire, en production. Une différence de méthode
|
|
25
|
+
* avec les modules de vitrine : Mémoire est écrite mobile-first et bascule à
|
|
26
|
+
* `lg` (1024px), alors que la charte ne connaît qu'UN point de rupture, à
|
|
27
|
+
* 768px. Les valeurs desktop sont donc posées en base et les valeurs mobiles
|
|
28
|
+
* regroupées en fin de fichier — le rendu desktop est identique au relevé, le
|
|
29
|
+
* rendu mobile arrive 256px plus tôt. C'est délibéré : deux ruptures dans un
|
|
30
|
+
* système qui en annonce une seule est une dette qui se paie sur chaque app.
|
|
31
|
+
*
|
|
32
|
+
* ÉCHELLE DE TEXTE — la production emploie des demi-pixels (11,5 / 12,5 /
|
|
33
|
+
* 13,5) que le paquet a rationalisés en trois paliers (12 / 13 / 14). Le
|
|
34
|
+
* rabattement retenu ici conserve les ÉCARTS du relevé plutôt que la valeur
|
|
35
|
+
* absolue : 11,5 et 12,5 → --app-sm (12), 13,5 → --app (13), 14 → --app-lg.
|
|
36
|
+
* Deux tailles distinctes d'un pixel dans la source restent donc distinctes
|
|
37
|
+
* d'un pixel ; c'est ce qui fait qu'un bandeau ample reste plus grand qu'un
|
|
38
|
+
* bandeau courant, des deux côtés du seuil.
|
|
39
|
+
*
|
|
40
|
+
* Rien ici n'est interactif : pas de bouton de fermeture, pas de survol, pas
|
|
41
|
+
* de champ. Les états :hover / :focus-visible / [disabled] / [aria-invalid]
|
|
42
|
+
* n'ont donc aucune cible dans ce module — ils appartiennent aux boutons
|
|
43
|
+
* (components.css) et aux champs (forms.css). Le focus clavier global de
|
|
44
|
+
* base.css suffit au bandeau qu'on rend focusable pour l'annoncer.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/* ─── Bandeaux d'état ───────────────────────────────────────────────────
|
|
48
|
+
* Filet + fond atténué + texte du même ton : la grammaire la plus
|
|
49
|
+
* systématique du relevé, appliquée à l'identique six fois. Elle tient
|
|
50
|
+
* parce que les trois valeurs viennent d'une même famille de tokens ; la
|
|
51
|
+
* recomposer à la main avec des opacités produit six bandeaux différents.
|
|
52
|
+
*
|
|
53
|
+
* Le ton est OBLIGATOIRE : `.zv-alert` seule hérite d'un filet en
|
|
54
|
+
* currentColor, c'est-à-dire un trait d'encre autour d'un fond blanc.
|
|
55
|
+
* Toujours l'accompagner de --info / --ok / --warn / --err / --dash. */
|
|
56
|
+
|
|
57
|
+
.zv-alert {
|
|
58
|
+
border: 1px solid;
|
|
59
|
+
padding: 12px 16px;
|
|
60
|
+
/* 12,5px relevé → palier 12. La valeur mobile (13,5 → 13) est en fin de
|
|
61
|
+
* fichier : les cinq bandeaux d'authentification GROSSISSENT sous le
|
|
62
|
+
* seuil, ils ne rétrécissent pas. */
|
|
63
|
+
font-size: var(--app-sm);
|
|
64
|
+
line-height: 1.55;
|
|
65
|
+
font-weight: 500;
|
|
66
|
+
/* Un bandeau porte souvent un message d'erreur brut, non maîtrisé :
|
|
67
|
+
* nom de fichier à rallonge, identifiant, chemin. Sans coupure, il pousse
|
|
68
|
+
* la page en défilement horizontal. */
|
|
69
|
+
overflow-wrap: break-word;
|
|
70
|
+
/* Angle vif, comme au relevé.
|
|
71
|
+
* ⚠️ NON ARBITRÉ EN DA, et le module ne tranche pas à sa place : le
|
|
72
|
+
* `border-radius: 0` posé ici et sur .zv-badge est la valeur relevée, pas
|
|
73
|
+
* une « signature de la couche applicative ». Il entre en contradiction
|
|
74
|
+
* directe avec .zv-empty__action, qui renvoie au .zv-btn de
|
|
75
|
+
* components.css et donc à --r-btn (5px) : un écran vide affichera un
|
|
76
|
+
* bouton arrondi au milieu de blocs à angle vif. La règle doit monter
|
|
77
|
+
* dans DECISIONS.md ou tomber — elle ne peut pas rester implicite dans
|
|
78
|
+
* quatre modules. */
|
|
79
|
+
border-radius: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Information — le seul ton relevé en bandeau de bas de page (garanties,
|
|
83
|
+
* explication d'un écran). */
|
|
84
|
+
.zv-alert--info {
|
|
85
|
+
border-color: var(--accent-line);
|
|
86
|
+
background: var(--accent-soft);
|
|
87
|
+
color: var(--accent);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Avertissement — le texte est --warn-ink, JAMAIS --warn : l'ambre plein
|
|
91
|
+
* sur fond ambre atténué passe sous le seuil de contraste. C'est la seule
|
|
92
|
+
* famille asymétrique du système, et l'erreur la plus facile à commettre. */
|
|
93
|
+
.zv-alert--warn {
|
|
94
|
+
border-color: var(--warn-line);
|
|
95
|
+
background: var(--warn-soft);
|
|
96
|
+
color: var(--warn-ink);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Erreur — à porter avec role="alert" côté balisage, sinon le message
|
|
100
|
+
* apparaît sans être annoncé aux lecteurs d'écran : c'est précisément le
|
|
101
|
+
* seul contenu de l'écran qu'on ne peut pas se permettre de rater. */
|
|
102
|
+
.zv-alert--err {
|
|
103
|
+
border-color: var(--err-line);
|
|
104
|
+
background: var(--err-soft);
|
|
105
|
+
color: var(--err);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Succès — RELEVÉ, pleine largeur, deux fois : reset-password-card.tsx:90 et
|
|
109
|
+
* forgot-password-card.tsx:60, en `border-ok-line bg-ok-soft px-3 py-[10px]
|
|
110
|
+
* text-[13.5px]/[1.6] font-medium text-ok`. (Une version antérieure de ce
|
|
111
|
+
* commentaire le disait « extrapolé » : c'était faux, les deux écrans de
|
|
112
|
+
* réinitialisation n'avaient pas été relus. Le gabarit du relevé est celui
|
|
113
|
+
* de --sm, à composer avec.) */
|
|
114
|
+
.zv-alert--ok {
|
|
115
|
+
border-color: var(--ok-line);
|
|
116
|
+
background: var(--ok-soft);
|
|
117
|
+
color: var(--ok);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Variante « aveu d'ignorance » : ce que le système n'a PAS trouvé. Le
|
|
121
|
+
* pointillé sans fond dit qu'il n'y a rien derrière — un fond plein ferait
|
|
122
|
+
* lire une réponse là où il n'y en a pas. Avec la zone de dépôt, c'est le
|
|
123
|
+
* seul pointillé de toute la couche applicative : le garder rare, c'est ce
|
|
124
|
+
* qui lui laisse son sens.
|
|
125
|
+
*
|
|
126
|
+
* ⚠️ DEUX PERTES DE TEINTE, assumées et signalées plutôt que glissées :
|
|
127
|
+
* 1. le texte est `#6E6A61` en production (chat-client.tsx:265), un gris
|
|
128
|
+
* CHAUD — le seul du fil de réponse. Le paquet n'a pas de neutre chaud :
|
|
129
|
+
* --muted (#5a637a) est bleuté. Le rabattement est arrêté, mais il
|
|
130
|
+
* REFROIDIT ce texte, et il le range sous le même token que les gris
|
|
131
|
+
* froids du relevé (#6B7488 du pager, du bouton d'icône). Deux familles
|
|
132
|
+
* de teinte finissent donc sur --muted : c'est une fusion, pas une
|
|
133
|
+
* nuance. Le jour où la DA veut retrouver ce chaud, il faut un token
|
|
134
|
+
* dédié dans tokens.css — pas une valeur en dur ici.
|
|
135
|
+
* 2. le filet est `#C9CEDD` en production, un gris froid neutre ; --accent-line
|
|
136
|
+
* (#c9d6f5) est le seul filet du paquet à cette clarté, mais il est
|
|
137
|
+
* légèrement indigo. Écart perceptible sur un pointillé large. */
|
|
138
|
+
.zv-alert--dash {
|
|
139
|
+
border: 1px dashed var(--accent-line);
|
|
140
|
+
background: none;
|
|
141
|
+
padding: 10px 14px;
|
|
142
|
+
font-size: var(--app);
|
|
143
|
+
line-height: 1.55;
|
|
144
|
+
color: var(--muted);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Gabarit compact (`px-3 py-[9px]`) — relevé quatre fois : login-card:207,
|
|
148
|
+
* bibliotheque-client:210, branding-card:219, billing-card:164. Deux d'entre
|
|
149
|
+
* eux sont des alertes de PAGE, pas de panneau : ce gabarit n'est donc pas
|
|
150
|
+
* « le bandeau dans un cadre », c'est simplement le bandeau resserré. La
|
|
151
|
+
* carte de suppression (delete-org-card:39) n'a aucun bandeau — l'histoire
|
|
152
|
+
* qui justifiait ces valeurs dans une version antérieure était inventée, les
|
|
153
|
+
* valeurs, elles, sont bonnes. */
|
|
154
|
+
.zv-alert--sm {
|
|
155
|
+
padding: 9px 12px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Gabarit ample : le message d'incident qui REMPLACE une réponse attendue
|
|
159
|
+
* (GPU en charge). Il occupe la place du contenu manquant, d'où le corps plus
|
|
160
|
+
* grand. Source chat-client.tsx:308 : `px-4 py-[14px] text-[13.5px]/[1.6]
|
|
161
|
+
* lg:px-5 lg:py-4` — le padding change au seuil, la TAILLE non (13,5px des
|
|
162
|
+
* deux côtés). D'où l'absence de font-size dans le bloc mobile. */
|
|
163
|
+
.zv-alert--lg {
|
|
164
|
+
padding: 16px 20px;
|
|
165
|
+
font-size: var(--app);
|
|
166
|
+
line-height: 1.6;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* ⚠️ DEUX ARBITRAGES de couleur portés ici, pas glissés sous le tapis :
|
|
170
|
+
* 1. le filet vert est écrit `border-ok/30` en production (un color-mix à
|
|
171
|
+
* 30 %) alors que --ok-line existe dans le paquet et n'y sert jamais.
|
|
172
|
+
* Retenu : --ok-line. Le rendu diffère — le color-mix laissait
|
|
173
|
+
* transparaître --ok-soft, le token est un vert franc.
|
|
174
|
+
* 2. l'ambre de production est #FFF6E3 / #EBD9AE (teintes écrites en dur
|
|
175
|
+
* dans deux tables), le token du paquet #faf3e2 / #ebdcb4. Retenu : les
|
|
176
|
+
* tokens. Écart visible à l'œil sur les bandeaux et badges WARN. */
|
|
177
|
+
|
|
178
|
+
/* ─── Étiquettes d'état ─────────────────────────────────────────────────
|
|
179
|
+
* Rôle d'une entité, niveau d'un log, état d'un service, issue d'un envoi :
|
|
180
|
+
* un seul objet, trois gabarits, cinq tons. Le mono en petit corps est ce
|
|
181
|
+
* qui les distingue d'un texte — une étiquette se lit d'un coup d'œil, elle
|
|
182
|
+
* ne se lit pas en phrase.
|
|
183
|
+
*
|
|
184
|
+
* Pas de text-transform : les libellés sont écrits en capitales à la source.
|
|
185
|
+
* Le laisser au contenu évite qu'un libellé accentué (« INDEXÉ ») soit
|
|
186
|
+
* transformé différemment selon la locale du navigateur.
|
|
187
|
+
*
|
|
188
|
+
* Ne pas confondre avec .zv-pill / .zv-tag / .zv-src de components.css :
|
|
189
|
+
* ceux-là appartiennent à la vitrine (pastille pleine indigo, micro-label
|
|
190
|
+
* espacé, puce de source). Aucun nom n'est repris ici. */
|
|
191
|
+
|
|
192
|
+
.zv-badge {
|
|
193
|
+
display: inline-flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 7px;
|
|
196
|
+
/* En cellule de grille, une étiquette étirée sur toute la colonne cesse
|
|
197
|
+
* de se lire comme une étiquette : elle se cale à gauche sur sa largeur. */
|
|
198
|
+
justify-self: start;
|
|
199
|
+
border: 1px solid;
|
|
200
|
+
padding: 3px 8px;
|
|
201
|
+
font-family: var(--font-mono);
|
|
202
|
+
font-size: var(--mono); /* 10px relevé, palier exact du paquet */
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
/* Un état ne se coupe jamais en deux lignes : « EN CHARGE » sur deux
|
|
205
|
+
* lignes se lit comme deux états. */
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
border-radius: 0; /* même arbitrage non tranché que .zv-alert, voir plus haut */
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Gabarit micro : niveau de log en tête de ligne, badge de la coque. À
|
|
211
|
+
* cette taille la graisse monte et les lettres s'espacent, sinon les
|
|
212
|
+
* capitales se referment et INFO/WARN ne se distinguent plus en diagonale.
|
|
213
|
+
* 8,5px relevé → --mono-sm (9px), le plus petit palier du paquet. */
|
|
214
|
+
.zv-badge--micro {
|
|
215
|
+
padding: 2px 6px;
|
|
216
|
+
font-size: var(--mono-sm);
|
|
217
|
+
font-weight: 700;
|
|
218
|
+
letter-spacing: 0.06em;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Gabarit état de service (OK / CHARGE). ⚠️ 9,5px au relevé, rabattu sur
|
|
222
|
+
* --mono (10px) : il retombe donc sur la MÊME taille que .zv-badge, et ne
|
|
223
|
+
* s'en distingue plus que par l'interlettrage. C'est la conséquence assumée
|
|
224
|
+
* de l'échelle à trois paliers ; le gabarit est conservé pour garder un nom
|
|
225
|
+
* à cet usage, pas parce qu'il change encore de corps. */
|
|
226
|
+
.zv-badge--state {
|
|
227
|
+
padding: 3px 8px;
|
|
228
|
+
font-size: var(--mono);
|
|
229
|
+
letter-spacing: 0.06em;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Tons — triplets relevés tels quels dans les tables de teintes. */
|
|
233
|
+
|
|
234
|
+
/* Neutre : `border-line bg-hairline text-[#4A5468]` (repertoire-client:25).
|
|
235
|
+
* Le filet est bien --line, pas un filet adouci : une version antérieure y
|
|
236
|
+
* avait substitué un token intermédiaire sans le dire. #4A5468 est le neutre
|
|
237
|
+
* intermédiaire du paquet, --ink-2. */
|
|
238
|
+
.zv-badge--neutre {
|
|
239
|
+
background: var(--hairline);
|
|
240
|
+
border-color: var(--line);
|
|
241
|
+
color: var(--ink-2);
|
|
242
|
+
}
|
|
243
|
+
.zv-badge--accent {
|
|
244
|
+
background: var(--accent-soft);
|
|
245
|
+
border-color: var(--accent-line);
|
|
246
|
+
color: var(--accent);
|
|
247
|
+
}
|
|
248
|
+
.zv-badge--ok {
|
|
249
|
+
background: var(--ok-soft);
|
|
250
|
+
border-color: var(--ok-line);
|
|
251
|
+
color: var(--ok);
|
|
252
|
+
}
|
|
253
|
+
.zv-badge--warn {
|
|
254
|
+
background: var(--warn-soft);
|
|
255
|
+
border-color: var(--warn-line);
|
|
256
|
+
color: var(--warn-ink);
|
|
257
|
+
}
|
|
258
|
+
.zv-badge--err {
|
|
259
|
+
background: var(--err-soft);
|
|
260
|
+
border-color: var(--err-line);
|
|
261
|
+
color: var(--err);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* Étiquette à pastille (« RÉSEAU PRIVÉ · IA DÉDIÉE », « TOUT
|
|
265
|
+
* OPÉRATIONNEL »). La pastille reprend la couleur du texte plutôt qu'un ton
|
|
266
|
+
* réglé à part : c'est ce que fait le relevé, et cela garantit que les deux
|
|
267
|
+
* ne divergent jamais quand on change de ton.
|
|
268
|
+
* ⚠️ Un jour où l'on posera une pastille sur .zv-badge--warn, currentColor
|
|
269
|
+
* donnera --warn-ink (l'ambre sombre) et non --warn : cas absent du relevé,
|
|
270
|
+
* à trancher avec la DA plutôt qu'à corriger ici en douce. */
|
|
271
|
+
.zv-badge--dot .zv-dot {
|
|
272
|
+
background: currentColor;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* ─── Pastilles d'état ──────────────────────────────────────────────────
|
|
276
|
+
* Seule ou dans une étiquette. Ronde par défaut ; CARRÉE dans les bandeaux
|
|
277
|
+
* de provenance qui coiffent une réponse. La distinction est tenue sur cinq
|
|
278
|
+
* occurrences : ce n'est pas un oubli de `rounded-full`, c'est la marque
|
|
279
|
+
* que le point désigne une SOURCE et non un ÉTAT. Ne pas l'uniformiser. */
|
|
280
|
+
|
|
281
|
+
.zv-dot {
|
|
282
|
+
width: 6px;
|
|
283
|
+
height: 6px;
|
|
284
|
+
/* Sans flex: none la pastille s'écrase quand le libellé voisin est long —
|
|
285
|
+
* elle devient un ovale, et l'état n'est plus lisible. */
|
|
286
|
+
flex: none;
|
|
287
|
+
border-radius: 9999px;
|
|
288
|
+
/* Repli gris : un statut inconnu (nouvel état ajouté côté serveur, jamais
|
|
289
|
+
* déployé côté interface) doit rester neutre. Le teindre en vert par
|
|
290
|
+
* défaut ferait passer un échec pour un succès. */
|
|
291
|
+
background: var(--label);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Pastille de provenance : carrée. Seule dérogation au rond de tout le
|
|
295
|
+
* module. */
|
|
296
|
+
.zv-dot--square {
|
|
297
|
+
border-radius: 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Dans une étiquette de la coque ou une garantie : plus discrète. */
|
|
301
|
+
.zv-dot--sm {
|
|
302
|
+
width: 5px;
|
|
303
|
+
height: 5px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Devant un libellé de service ou une phrase de travail en cours : la
|
|
307
|
+
* pastille porte seule l'information, elle grossit d'un pixel. */
|
|
308
|
+
.zv-dot--lg {
|
|
309
|
+
width: 7px;
|
|
310
|
+
height: 7px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Correspondance statut → couleur, relevée telle quelle. Elle vit en CSS et
|
|
314
|
+
* pas dans chaque app : c'est la seule façon que « reçu » et « OCR en
|
|
315
|
+
* cours » restent du même ambre partout. */
|
|
316
|
+
.zv-dot--recu,
|
|
317
|
+
.zv-dot--ocr {
|
|
318
|
+
background: var(--warn);
|
|
319
|
+
}
|
|
320
|
+
.zv-dot--indexation {
|
|
321
|
+
background: var(--accent);
|
|
322
|
+
}
|
|
323
|
+
.zv-dot--indexe,
|
|
324
|
+
.zv-dot--ok {
|
|
325
|
+
background: var(--ok);
|
|
326
|
+
}
|
|
327
|
+
.zv-dot--erreur {
|
|
328
|
+
background: var(--err);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* ─── Statut en clair ───────────────────────────────────────────────────
|
|
332
|
+
* Pastille + libellé, sans cadre — contrairement à l'étiquette. Le cadre
|
|
333
|
+
* est réservé à ce qui classe (rôle, niveau) ; le statut, lui, se lit dans
|
|
334
|
+
* le fil de la ligne. Le message d'erreur REMPLACE le libellé quand il y en
|
|
335
|
+
* a un : l'utilisateur veut lire la cause, pas le mot « Erreur ».
|
|
336
|
+
*
|
|
337
|
+
* Source : bibliotheque-client.tsx:259, `flex min-w-0 items-center
|
|
338
|
+
* gap-[6px] text-[11.5px] font-medium text-[#3A4256] lg:min-w-[auto]`, avec
|
|
339
|
+
* un `<span className="truncate">` à l'intérieur et AUCUNE variante `lg:`
|
|
340
|
+
* sur ce span. Autrement dit : le libellé tronque, et il tronque SOUS le
|
|
341
|
+
* seuil, là où min-width vaut 0 ; au-dessus, la colonne reprend sa largeur
|
|
342
|
+
* naturelle. Le fichier est desktop-first, donc `min-width: auto` est ici et
|
|
343
|
+
* `min-width: 0` dans le bloc mobile — l'inverse exact de ce qu'une version
|
|
344
|
+
* antérieure de ce module posait, qui faisait passer le libellé à la ligne
|
|
345
|
+
* sur téléphone et décalait la pastille de 5px vers le bas. Rien de tout
|
|
346
|
+
* cela n'existe en production. */
|
|
347
|
+
|
|
348
|
+
.zv-status {
|
|
349
|
+
display: flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
gap: 6px;
|
|
352
|
+
/* Valeur initiale, écrite pour être lue : au-dessus du seuil la colonne
|
|
353
|
+
* n'est pas contrainte, le libellé la pousse — c'est le `lg:min-w-[auto]`
|
|
354
|
+
* du relevé. La troncature n'opère réellement que sous le seuil. */
|
|
355
|
+
min-width: auto;
|
|
356
|
+
font-size: var(--app-sm); /* 11,5px relevé → palier 12 */
|
|
357
|
+
font-weight: 500;
|
|
358
|
+
color: var(--ink-2); /* #3A4256 relevé → neutre intermédiaire du paquet */
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* Le libellé tronque des DEUX côtés du seuil : la classe `truncate` de la
|
|
362
|
+
* source n'a pas de variante responsive. */
|
|
363
|
+
.zv-status__label {
|
|
364
|
+
overflow: hidden;
|
|
365
|
+
text-overflow: ellipsis;
|
|
366
|
+
white-space: nowrap;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/* ─── Absence ───────────────────────────────────────────────────────────
|
|
370
|
+
* DEUX objets distincts, à ne surtout pas fusionner :
|
|
371
|
+
* — la ligne d'absence, quand un filtre ne ramène rien : la collection
|
|
372
|
+
* existe, seul le filtre est trop étroit. Elle vit DANS le cadre, à la
|
|
373
|
+
* place des lignes, pour que l'en-tête de tableau reste visible ;
|
|
374
|
+
* — l'écran d'absence, quand la collection est vide : il remplace le
|
|
375
|
+
* tableau entier, porte un titre et l'action qui la remplira.
|
|
376
|
+
* Les confondre donne soit un écran de bienvenue affiché à chaque filtre
|
|
377
|
+
* trop strict, soit une collection vide qui n'offre aucune porte de sortie. */
|
|
378
|
+
|
|
379
|
+
/* ⚠️ Les quatre lignes d'absence relevées ne s'accordent pas : `p-[26px]`
|
|
380
|
+
* (repertoire-client:159), `px-[14px] py-6` (bibliotheque-client:230),
|
|
381
|
+
* `px-[18px] py-8` (import-client:263), `py-5` seul (logs-client:234).
|
|
382
|
+
* Retenue : la seule qui soit écrite telle quelle, 26px sur les quatre
|
|
383
|
+
* côtés. Une version antérieure posait « 26px 14px », c'est-à-dire le
|
|
384
|
+
* vertical de la première recomposé avec l'horizontal de la deuxième — une
|
|
385
|
+
* paire plausible qui n'existe dans aucune source. */
|
|
386
|
+
.zv-empty {
|
|
387
|
+
padding: 26px;
|
|
388
|
+
text-align: center;
|
|
389
|
+
font-size: var(--app-sm);
|
|
390
|
+
color: var(--muted);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.zv-empty--page {
|
|
394
|
+
border: 1px solid var(--line);
|
|
395
|
+
background: var(--white);
|
|
396
|
+
padding: 48px 32px;
|
|
397
|
+
text-align: center;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* --app-h2 (20px) est le palier que tokens.css destine explicitement au
|
|
401
|
+
* « titre de fiche, d'état vide, d'interstice ». */
|
|
402
|
+
.zv-empty__title {
|
|
403
|
+
/* Posé explicitement : sur un <span>, le titre viendrait se coller au
|
|
404
|
+
* texte qui le suit au lieu de le surmonter, et l'état vide se lirait
|
|
405
|
+
* comme une seule phrase. */
|
|
406
|
+
display: block;
|
|
407
|
+
font-family: var(--font-display);
|
|
408
|
+
font-size: var(--app-h2);
|
|
409
|
+
font-weight: 300;
|
|
410
|
+
color: var(--ink);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.zv-empty__text {
|
|
414
|
+
/* La mesure bornée n'est pas décorative : centré et pleine largeur, ce
|
|
415
|
+
* paragraphe devient illisible dès 900px de cadre. */
|
|
416
|
+
margin: 8px auto 0;
|
|
417
|
+
max-width: 420px;
|
|
418
|
+
font-size: var(--app);
|
|
419
|
+
line-height: 1.6;
|
|
420
|
+
color: var(--muted);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* L'action de l'écran vide (le bouton qui remplit la collection). Elle est
|
|
424
|
+
* ici pour l'écart seul : le bouton lui-même vient de components.css — et
|
|
425
|
+
* porte donc --r-btn, voir la contradiction signalée sur .zv-alert. */
|
|
426
|
+
.zv-empty__action {
|
|
427
|
+
margin-top: 20px;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* Ni icône, ni illustration, ni « réinitialiser les filtres » : rien de tel
|
|
431
|
+
* n'existe dans les sources, et ce sont exactement les trois ajouts qu'un
|
|
432
|
+
* état vide attire. */
|
|
433
|
+
|
|
434
|
+
/* ─── Travail en cours ──────────────────────────────────────────────────
|
|
435
|
+
* En production ce n'est PAS une roue : c'est une pastille suivie d'une
|
|
436
|
+
* phrase qui dit le travail réel (« Recherche dans 4 812 documents
|
|
437
|
+
* anonymisés… »). Une roue dit « attendez » ; cette phrase dit ce qu'on
|
|
438
|
+
* attend, sur combien, et qu'il ne se passe rien d'anormal. */
|
|
439
|
+
|
|
440
|
+
.zv-loading {
|
|
441
|
+
display: flex;
|
|
442
|
+
align-items: center;
|
|
443
|
+
gap: 9px;
|
|
444
|
+
font-family: var(--font-mono);
|
|
445
|
+
font-size: var(--app-sm);
|
|
446
|
+
font-weight: 500;
|
|
447
|
+
/* #727C93 au relevé — un gris froid, rabattu sur --muted comme les autres
|
|
448
|
+
* neutres secondaires. Le token porte donc plusieurs gris du relevé : la
|
|
449
|
+
* hiérarchie interne des gris de Mémoire (#6B7488 / #727C93) n'existe plus
|
|
450
|
+
* dans le paquet, elle échouait de toute façon au seuil AA. */
|
|
451
|
+
color: var(--muted);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.zv-loading .zv-dot {
|
|
455
|
+
width: 7px;
|
|
456
|
+
height: 7px;
|
|
457
|
+
background: var(--accent);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* ⚠️ STATIQUE, et c'est un choix. Aucune animation dans la source : ni
|
|
461
|
+
* @keyframes, ni classe d'animation, la pastille ne bat pas. Lui ajouter un
|
|
462
|
+
* pouls est l'invention la plus tentante de tout le relevé — et les
|
|
463
|
+
* @keyframes du paquet (motion.css) sont réservées à la vitrine, pilotées au
|
|
464
|
+
* défilement. Ne pas confondre `.zv-dot` avec `.zv-anim-dot-active` de
|
|
465
|
+
* motion.css : ce dernier anime un point de méthode, pas un état. */
|
|
466
|
+
|
|
467
|
+
/* ─── Curseur de flux ───────────────────────────────────────────────────
|
|
468
|
+
* Bloc collé à la fin du texte en cours de diffusion, sur le dernier
|
|
469
|
+
* message seulement. Il dit « la réponse n'est pas finie » — sans lui, une
|
|
470
|
+
* réponse coupée à mi-phrase se lit comme une réponse complète et fausse. */
|
|
471
|
+
|
|
472
|
+
.zv-caret {
|
|
473
|
+
margin-left: 2px;
|
|
474
|
+
color: var(--accent);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Le glyphe ▍ (U+258D) est un CARACTÈRE posé dans le texte, pas un
|
|
478
|
+
* pseudo-élément : il suit la dernière lettre où qu'elle tombe, y compris
|
|
479
|
+
* en fin de ligne justifiée. STATIQUE là encore, aucun clignotement.
|
|
480
|
+
*
|
|
481
|
+
* Le flux interrompu, lui, n'a AUCUN style : c'est la phrase
|
|
482
|
+
* « [Flux interrompu — la réponse est incomplète.] » ajoutée au corps du
|
|
483
|
+
* message, rendue par white-space: pre-wrap. La réponse partielle est
|
|
484
|
+
* conservée, jamais effacée — on n'efface pas ce que l'utilisateur a déjà lu. */
|
|
485
|
+
|
|
486
|
+
/* ─── Retour d'attente : ce que fait Mémoire, et ce qu'elle ne fait pas ──
|
|
487
|
+
* Le retour d'attente de cette application est TEXTUEL, et c'est cohérent
|
|
488
|
+
* avec .zv-loading ci-dessus : le libellé du bouton change pendant l'action
|
|
489
|
+
* (`{busy === d.id ? 'Suppression…' : 'Supprimer'}`, bibliotheque-client:283 ;
|
|
490
|
+
* `{pending ? 'Connexion…' : 'Se connecter'}` au login), en plus d'un
|
|
491
|
+
* `disabled:cursor-wait`. Ce n'est pas du CSS : c'est un motif de balisage à
|
|
492
|
+
* reprendre, et il ne demande aucune classe.
|
|
493
|
+
*
|
|
494
|
+
* Restent DEUX actions sans aucun retour : la déconnexion et l'envoi de
|
|
495
|
+
* message (le bouton reste plein et cliquable en apparence alors que la
|
|
496
|
+
* garde existe côté script). C'est un défaut de l'application, pas une
|
|
497
|
+
* intention de charte : ne pas le répliquer dans une app neuve.
|
|
498
|
+
*
|
|
499
|
+
* ─── Manques assumés : roue, squelette, infobulle ───────────────────────
|
|
500
|
+
* AUCUNE MATIÈRE PREMIÈRE dans les cinq écrans relevés. Ni .zv-spinner, ni
|
|
501
|
+
* .zv-skeleton, ni .zv-tooltip ne sont écrits ici : les livrer supposerait
|
|
502
|
+
* d'inventer une durée, une amplitude et une géométrie que personne n'a
|
|
503
|
+
* arbitrées, et un système ment moins en affichant un trou qu'une valeur
|
|
504
|
+
* plausible. Rien n'appelle d'infobulle et rien n'en propose : le nom de
|
|
505
|
+
* document tronqué n'a même pas d'attribut title, les barres des graphiques
|
|
506
|
+
* n'affichent aucune valeur au pointeur.
|
|
507
|
+
*
|
|
508
|
+
* Contraintes à respecter le jour où la DA les tranche : pas de dégradé
|
|
509
|
+
* spectre (réservé au filet de section, à la barre de progression et aux
|
|
510
|
+
* points de méthode) — donc un squelette en --hairline, pas un balayage
|
|
511
|
+
* spectral ; et une infobulle en petit panneau blanc, filet --line, ombre
|
|
512
|
+
* --shadow-menu, texte --app-sm / --ink-2, qui est la seule combinaison déjà
|
|
513
|
+
* présente ailleurs dans le système. */
|
|
514
|
+
|
|
515
|
+
/* ─── Mobile ────────────────────────────────────────────────────────────
|
|
516
|
+
* Un seul point de rupture, 768px, comme dans tous les modules du paquet.
|
|
517
|
+
* Ce bloc ne contient QUE des bascules attestées dans la source (variantes
|
|
518
|
+
* `lg:` de Mémoire, lues en sens inverse) — pas une seule règle de confort
|
|
519
|
+
* ajoutée en chemin. */
|
|
520
|
+
|
|
521
|
+
@media (max-width: 767px) {
|
|
522
|
+
/* Les cinq bandeaux d'authentification portent `text-[13.5px]
|
|
523
|
+
* lg:text-[12.5px]` (login-card:207, reset-password-card:78 et :137,
|
|
524
|
+
* forgot-password-card:60 et :96) : le texte GROSSIT d'un palier sous le
|
|
525
|
+
* seuil. C'est le motif le mieux attesté du module ; l'oublier laissait le
|
|
526
|
+
* bandeau à sa plus petite taille précisément sur l'écran le plus étroit. */
|
|
527
|
+
.zv-alert {
|
|
528
|
+
font-size: var(--app);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Le bandeau pointillé fait l'inverse — `text-[13px]` en desktop,
|
|
532
|
+
* `text-[12.5px]` sous le seuil — et resserre sa gouttière : 16px de
|
|
533
|
+
* chaque côté sur 375px déjà bordés par le padding de page, c'est la
|
|
534
|
+
* moitié de la largeur perdue en marges. Déclaré APRÈS .zv-alert pour
|
|
535
|
+
* l'emporter à spécificité égale. */
|
|
536
|
+
.zv-alert--dash {
|
|
537
|
+
padding: 10px 12px;
|
|
538
|
+
font-size: var(--app-sm);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* Le message d'incident resserre son padding (`px-4 py-[14px]`) et GARDE
|
|
542
|
+
* sa taille : la source n'a pas de variante `lg:` sur le corps. Redéclaré
|
|
543
|
+
* ici uniquement pour n'être pas rétréci par la règle .zv-alert ci-dessus,
|
|
544
|
+
* à spécificité égale. */
|
|
545
|
+
.zv-alert--lg {
|
|
546
|
+
padding: 14px 16px;
|
|
547
|
+
font-size: var(--app);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* C'est ici, et seulement ici, que la troncature du statut opère : le
|
|
551
|
+
* `min-w-0` de la source vaut sous le seuil, et sans lui l'ellipse ne se
|
|
552
|
+
* déclenche pas dans une cellule flex ou grid — le libellé pousse la
|
|
553
|
+
* colonne. C'est la cause n° 1 de débordement horizontal des tableaux. */
|
|
554
|
+
.zv-status {
|
|
555
|
+
min-width: 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/* ⚠️ NON RELEVÉ : aucune variante `lg:` sur le padding de l'écran vide.
|
|
559
|
+
* Ce resserrement est une décision de portage, prise parce que 32px de
|
|
560
|
+
* gouttière sur 375px laissent 311px de contenu à un bloc qui porte un
|
|
561
|
+
* titre et un bouton. Si la DA la refuse, c'est cette règle qui saute —
|
|
562
|
+
* pas le reste du bloc. */
|
|
563
|
+
.zv-empty--page {
|
|
564
|
+
padding: 40px 20px;
|
|
565
|
+
}
|
|
566
|
+
}
|