@zevra/ui 0.2.0 → 0.3.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 +26 -0
- package/README.md +10 -2
- package/demo/app.html +276 -0
- package/demo/index.html +258 -0
- package/package.json +6 -3
- package/src/index.css +1 -0
- package/src/layout.css +76 -0
- package/src/navigation.css +0 -107
- package/src/overlays.css +305 -0
package/DECISIONS.md
CHANGED
|
@@ -173,6 +173,32 @@ n'importe quelle police.
|
|
|
173
173
|
`--muted`. La nuance de température disparaît ; elle n'était reconductible
|
|
174
174
|
nulle part ailleurs.
|
|
175
175
|
|
|
176
|
+
### Enrobages — hors relevé, assumés
|
|
177
|
+
|
|
178
|
+
Trois composants n'existent dans aucune source et sont pourtant livrés, car
|
|
179
|
+
leur absence rend le système inutilisable :
|
|
180
|
+
|
|
181
|
+
**Le menu mobile de la vitrine.** Les cinq planches vitrine sont des rendus
|
|
182
|
+
figés à 1440px : aucune n'a jamais eu de menu mobile. Sans `.zv-nav__burger`
|
|
183
|
+
et le tiroir qu'il ouvre, une page Zevra sur téléphone n'a aucun moyen
|
|
184
|
+
d'atteindre sa navigation. Le tracé du burger est dérivé ; les mesures
|
|
185
|
+
(cible de 40px, panneau de 288px) viennent du tiroir applicatif, lui relevé.
|
|
186
|
+
|
|
187
|
+
**La modale.** Mémoire n'en emploie aucune. Celle-ci est dérivée de la carte
|
|
188
|
+
et du tiroir, sans une valeur nouvelle. Elle existe parce que la règle du
|
|
189
|
+
projet en impose la forme — panneau plafonné à 92vh qui défile en son sein —
|
|
190
|
+
et qu'à défaut chaque app en réinventerait une.
|
|
191
|
+
|
|
192
|
+
**La notification flottante.** Mémoire signale tout en place, par une alerte
|
|
193
|
+
de bloc. Celle-ci est l'alerte détachée et posée en coin. Elle ne remplace
|
|
194
|
+
pas `.zv-alert` : une erreur de formulaire se dit à côté du champ, jamais
|
|
195
|
+
dans un coin qui disparaît.
|
|
196
|
+
|
|
197
|
+
Ces trois-là vivent dans `overlays.css`, module commun aux deux couches.
|
|
198
|
+
Auparavant, le voile et le tiroir étaient dans `navigation.css` : une landing
|
|
199
|
+
devait charger la barre latérale, la pagination et les tableaux pour obtenir
|
|
200
|
+
un bouton burger.
|
|
201
|
+
|
|
176
202
|
### Ce que le relevé ne donnait pas
|
|
177
203
|
|
|
178
204
|
Aucun `<textarea>`, aucune case à cocher, aucun bouton radio n'existe dans
|
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ pnpm add @zevra/ui
|
|
|
32
32
|
@import "@zevra/ui/surfaces.css"; /* halos, sols, voiles, manifeste */
|
|
33
33
|
@import "@zevra/ui/motion.css"; /* les neuf animations autorisées */
|
|
34
34
|
@import "@zevra/ui/icons.css"; /* dimensionnement des icônes */
|
|
35
|
+
@import "@zevra/ui/overlays.css"; /* voile, tiroir, modale, notification */
|
|
35
36
|
@import "@zevra/ui/forms.css"; /* champs, selects, interrupteurs, dépôt */
|
|
36
37
|
@import "@zevra/ui/feedback.css"; /* alertes, badges, états, chargement */
|
|
37
38
|
@import "@zevra/ui/navigation.css"; /* menus, barre, fil d'Ariane, pagination */
|
|
@@ -60,8 +61,15 @@ La **couche applicative** — `forms`, `feedback`, `navigation`, `data`, `icons`
|
|
|
60
61
|
compose une interface qui s'opère : champs, alertes, menus, tableaux. Elle
|
|
61
62
|
est plus dense, et dispose de son échelle propre (`--app`, `--mono`).
|
|
62
63
|
|
|
63
|
-
Une app n'a aucune raison de charger les deux.
|
|
64
|
-
`base.css`
|
|
64
|
+
Une app n'a aucune raison de charger les deux. Sont communs : `tokens.css`,
|
|
65
|
+
`base.css`, `icons.css` et **`overlays.css`** — une page vitrine a besoin
|
|
66
|
+
d'un tiroir pour son menu mobile autant qu'une application pour sa
|
|
67
|
+
navigation.
|
|
68
|
+
|
|
69
|
+
**Le menu mobile n'est pas optionnel.** Sous 768px, `.zv-nav__links` doit
|
|
70
|
+
être masqué (`zv-only-desktop`) et remplacé par `.zv-nav__burger`, qui ouvre
|
|
71
|
+
un `.zv-drawer`. Sans lui, la navigation devient inatteignable sur
|
|
72
|
+
téléphone.
|
|
65
73
|
|
|
66
74
|
Le tableau est **à base de grille**, pas de `<table>` : le gabarit de
|
|
67
75
|
colonnes se pose en `--zv-cols` sur le conteneur, et sous 768px la rangée
|
package/demo/app.html
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Zevra UI — couche applicative</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
8
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Outfit:wght@200;300;400;500;600&family=Public+Sans:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap">
|
|
9
|
+
<link rel="stylesheet" href="../src/index.css">
|
|
10
|
+
<style>
|
|
11
|
+
/* Propre à la planche — n'appartient pas au système. */
|
|
12
|
+
body { padding: 0; }
|
|
13
|
+
.d-wrap { max-width: 1180px; margin: 0 auto; padding: 32px 24px 80px; }
|
|
14
|
+
.d-sec { margin-top: 44px; }
|
|
15
|
+
.d-sec > h2 { margin: 0 0 4px; }
|
|
16
|
+
.d-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; margin-top: 16px; }
|
|
17
|
+
.d-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin-top: 16px; }
|
|
18
|
+
.d-box { border: 1px dashed var(--accent-line); padding: 14px; background: var(--white); }
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
|
|
23
|
+
<div class="d-wrap">
|
|
24
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">Couche applicative</span></span>
|
|
25
|
+
<h1 class="zv-h2 zv-h2--sm" style="margin-top:18px">Ce qu'une interface exige <span class="zv-em">et qu'une vitrine ignore.</span></h1>
|
|
26
|
+
<p class="zv-note" style="margin-top:10px">Chaque composant relevé dans Mémoire, posé une fois. Les cadres pointillés isolent un composant de son voisin.</p>
|
|
27
|
+
|
|
28
|
+
<!-- FORMULAIRES -->
|
|
29
|
+
<section class="d-sec">
|
|
30
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">01 · Formulaires</span></span>
|
|
31
|
+
<div class="d-grid">
|
|
32
|
+
<div class="d-box">
|
|
33
|
+
<label class="zv-field">
|
|
34
|
+
<span class="zv-field__head">
|
|
35
|
+
<span class="zv-field__label">Adresse e-mail</span>
|
|
36
|
+
<a class="zv-link" href="#" style="font-size:var(--app-sm)">Aide</a>
|
|
37
|
+
</span>
|
|
38
|
+
<input class="zv-input" type="email" placeholder="prenom@cabinet.fr">
|
|
39
|
+
<span class="zv-field__help">Utilisée pour la connexion et les alertes.</span>
|
|
40
|
+
</label>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="d-box">
|
|
43
|
+
<label class="zv-field zv-field--err">
|
|
44
|
+
<span class="zv-field__label">Identifiant d'organisation</span>
|
|
45
|
+
<input class="zv-input zv-input--mono" value="karila-2024" aria-invalid="true">
|
|
46
|
+
<span class="zv-field__err">Cet identifiant est déjà pris.</span>
|
|
47
|
+
</label>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="d-box">
|
|
50
|
+
<label class="zv-field">
|
|
51
|
+
<span class="zv-field__label">Rôle</span>
|
|
52
|
+
<select class="zv-input zv-input--select">
|
|
53
|
+
<option>Membre</option><option>Administrateur</option><option>Lecture seule</option>
|
|
54
|
+
</select>
|
|
55
|
+
</label>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="d-box">
|
|
58
|
+
<label class="zv-field">
|
|
59
|
+
<span class="zv-field__label">Note interne</span>
|
|
60
|
+
<textarea class="zv-textarea" placeholder="Contexte du dossier…"></textarea>
|
|
61
|
+
</label>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="d-row">
|
|
66
|
+
<span class="zv-search"><svg class="zv-search__icon" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><circle cx="5.5" cy="5.5" r="4"/><line x1="8.6" y1="8.6" x2="12" y2="12"/></svg><input class="zv-search__input" placeholder="Rechercher un document…"></span>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="d-row">
|
|
70
|
+
<span class="zv-seg-group">
|
|
71
|
+
<button class="zv-seg zv-seg--on">Tous <span class="zv-seg__count">128</span></button>
|
|
72
|
+
<button class="zv-seg">Consultations <span class="zv-seg__count">41</span></button>
|
|
73
|
+
<button class="zv-seg">Actes <span class="zv-seg__count">63</span></button>
|
|
74
|
+
<button class="zv-seg zv-seg--mono">PDF</button>
|
|
75
|
+
</span>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="d-row">
|
|
79
|
+
<span class="zv-select-anchor">
|
|
80
|
+
<button class="zv-select">Périmètre : <strong>Toute la base</strong><span class="zv-select__chevron">▾</span></button>
|
|
81
|
+
</span>
|
|
82
|
+
<label class="zv-switch-hit"><span class="zv-switch zv-switch--on"><span class="zv-switch__knob"></span></span><span class="zv-switch__text zv-switch__text--on">Indexation automatique</span></label>
|
|
83
|
+
<label class="zv-switch-hit"><span class="zv-switch"><span class="zv-switch__knob"></span></span><span class="zv-switch__text">Notifications</span></label>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="d-row">
|
|
87
|
+
<label class="zv-switch-hit"><input class="zv-check-box" type="checkbox" checked><span class="zv-switch__text">J'accepte les conditions</span></label>
|
|
88
|
+
<label class="zv-switch-hit"><input class="zv-radio" type="radio" name="r" checked><span class="zv-switch__text">Mensuel</span></label>
|
|
89
|
+
<label class="zv-switch-hit"><input class="zv-radio" type="radio" name="r"><span class="zv-switch__text">Annuel</span></label>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="zv-drop" style="margin-top:16px">
|
|
93
|
+
<span class="zv-drop__title">Déposer des documents</span>
|
|
94
|
+
<span class="zv-drop__hint">Glissez vos fichiers ici, ou <span class="zv-drop__browse">parcourez votre disque</span>.</span>
|
|
95
|
+
<span class="zv-drop__spec">PDF, DOCX — 40 Mo maximum</span>
|
|
96
|
+
</div>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
99
|
+
<!-- RETOURS -->
|
|
100
|
+
<section class="d-sec">
|
|
101
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">02 · Retours</span></span>
|
|
102
|
+
<div class="d-row" style="flex-direction:column;align-items:stretch">
|
|
103
|
+
<div class="zv-alert zv-alert--info">L'indexation reprendra automatiquement à la reconnexion.</div>
|
|
104
|
+
<div class="zv-alert zv-alert--ok">Douze documents importés, tous indexés.</div>
|
|
105
|
+
<div class="zv-alert zv-alert--warn">Trois documents scannés attendent la reconnaissance de texte.</div>
|
|
106
|
+
<div class="zv-alert zv-alert--err">Impossible de joindre le service d'indexation. Réessayez dans un instant.</div>
|
|
107
|
+
<div class="zv-alert zv-alert--dash">Réponse construite à partir de vos documents, sans recours à un modèle externe.</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="d-row">
|
|
111
|
+
<span class="zv-badge zv-badge--ok">Indexé</span>
|
|
112
|
+
<span class="zv-badge zv-badge--warn">OCR en cours</span>
|
|
113
|
+
<span class="zv-badge zv-badge--err">Échec</span>
|
|
114
|
+
<span class="zv-badge zv-badge--accent">Nouveau</span>
|
|
115
|
+
<span class="zv-badge zv-badge--neutre">Brouillon</span>
|
|
116
|
+
<span class="zv-badge zv-badge--micro">v2</span>
|
|
117
|
+
<span class="zv-badge zv-badge--dot"><span class="zv-dot zv-dot--ok"></span>En ligne</span>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div class="d-row">
|
|
121
|
+
<span class="zv-status"><span class="zv-dot zv-dot--indexe"></span><span class="zv-status__label">Indexé</span></span>
|
|
122
|
+
<span class="zv-status"><span class="zv-dot zv-dot--indexation"></span><span class="zv-status__label">Indexation…</span></span>
|
|
123
|
+
<span class="zv-status"><span class="zv-dot zv-dot--ocr"></span><span class="zv-status__label">Reconnaissance de texte</span></span>
|
|
124
|
+
<span class="zv-status"><span class="zv-dot zv-dot--erreur"></span><span class="zv-status__label">Erreur</span></span>
|
|
125
|
+
<span class="zv-status"><span class="zv-dot zv-dot--recu"></span><span class="zv-status__label">Reçu</span></span>
|
|
126
|
+
<span class="zv-loading">Chargement…</span>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div class="zv-empty" style="margin-top:16px">
|
|
130
|
+
<div class="zv-empty__title">Aucun document</div>
|
|
131
|
+
<div class="zv-empty__text">Déposez vos premiers actes pour que Mémoire puisse les interroger.</div>
|
|
132
|
+
<a class="zv-btn zv-btn--sm zv-empty__action" href="#">Importer</a>
|
|
133
|
+
</div>
|
|
134
|
+
</section>
|
|
135
|
+
|
|
136
|
+
<!-- NAVIGATION -->
|
|
137
|
+
<section class="d-sec">
|
|
138
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">03 · Navigation</span></span>
|
|
139
|
+
|
|
140
|
+
<div class="d-box" style="margin-top:16px;padding:0">
|
|
141
|
+
<div class="zv-topbar">
|
|
142
|
+
<button class="zv-topbar__burger zv-icon-btn" aria-label="Ouvrir le menu"><svg class="zv-icon"><use href="../icons/sprite.svg#zv-menu"/></svg></button>
|
|
143
|
+
<span class="zv-topbar__title">Bibliothèque</span>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div class="d-grid">
|
|
148
|
+
<div class="d-box" style="padding:0">
|
|
149
|
+
<nav class="zv-sidebar" style="height:auto">
|
|
150
|
+
<div class="zv-sidebar__head"><span class="zv-mono zv-mono--micro">Cabinet Karila</span></div>
|
|
151
|
+
<div class="zv-sidegroup">
|
|
152
|
+
<span class="zv-sidegroup__title">Travail</span>
|
|
153
|
+
<a class="zv-sidelink zv-sidelink--on" href="#"><svg class="zv-icon zv-icon--sm"><use href="../icons/sprite.svg#zv-recherche"/></svg>Recherche</a>
|
|
154
|
+
<a class="zv-sidelink" href="#"><svg class="zv-icon zv-icon--sm"><use href="../icons/sprite.svg#zv-dossier"/></svg>Bibliothèque <span class="zv-sidelink__num">128</span></a>
|
|
155
|
+
<a class="zv-sidelink" href="#"><svg class="zv-icon zv-icon--sm"><use href="../icons/sprite.svg#zv-equipe"/></svg>Répertoire</a>
|
|
156
|
+
</div>
|
|
157
|
+
</nav>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="d-box">
|
|
160
|
+
<span class="zv-crumbs"><a class="zv-crumbs__seg" href="#">Bibliothèque</a><span class="zv-crumbs__sep">/</span><a class="zv-crumbs__seg" href="#">Actes</a><span class="zv-crumbs__sep">/</span><span class="zv-crumbs__seg">Cession de parts</span></span>
|
|
161
|
+
<div class="zv-menu" style="position:static;margin-top:16px">
|
|
162
|
+
<button class="zv-menu__item zv-menu__item--on">Toute la base</button>
|
|
163
|
+
<button class="zv-menu__item">Consultations</button>
|
|
164
|
+
<button class="zv-menu__item">Actes signés</button>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="zv-pager" style="margin-top:16px">
|
|
167
|
+
<button class="zv-pager__arrow" aria-label="Page précédente">‹</button>
|
|
168
|
+
<span class="zv-pager__rank">1 – 25 sur 128</span>
|
|
169
|
+
<button class="zv-pager__arrow" aria-label="Page suivante">›</button>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</section>
|
|
174
|
+
|
|
175
|
+
<!-- DONNÉES -->
|
|
176
|
+
<section class="d-sec">
|
|
177
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">04 · Données</span></span>
|
|
178
|
+
|
|
179
|
+
<!-- Le tableau est à base de GRILLE, pas de <table> : c'est ainsi qu'il
|
|
180
|
+
est relevé, et le gabarit de colonnes se pose sur le conteneur. -->
|
|
181
|
+
<div class="zv-table" style="margin-top:16px;--zv-cols:minmax(180px,1.4fr) 108px 132px 84px 104px">
|
|
182
|
+
<div class="zv-table__head">
|
|
183
|
+
<span>Document</span>
|
|
184
|
+
<span>Nature</span>
|
|
185
|
+
<span>Statut</span>
|
|
186
|
+
<span>Taille</span>
|
|
187
|
+
<span>Indexé le</span>
|
|
188
|
+
</div>
|
|
189
|
+
<div class="zv-row">
|
|
190
|
+
<span class="zv-cell zv-cell--name"><span class="zv-filetype zv-filetype--pdf">PDF</span><span class="zv-trunc">Cession de parts — SCI Beaumont</span></span>
|
|
191
|
+
<span class="zv-cell">Acte</span>
|
|
192
|
+
<span class="zv-cell"><span class="zv-status"><span class="zv-dot zv-dot--indexe"></span><span class="zv-status__label">Indexé</span></span></span>
|
|
193
|
+
<span class="zv-cell zv-cell--num"><span class="zv-cell__label">Taille</span>2,4 Mo</span>
|
|
194
|
+
<span class="zv-cell zv-cell--num">12/08/2026</span>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="zv-row">
|
|
197
|
+
<span class="zv-cell zv-cell--name"><span class="zv-filetype zv-filetype--docx">DOCX</span><span class="zv-trunc">Consultation — garantie autonome</span></span>
|
|
198
|
+
<span class="zv-cell">Consultation</span>
|
|
199
|
+
<span class="zv-cell"><span class="zv-status"><span class="zv-dot zv-dot--ocr"></span><span class="zv-status__label">OCR</span></span></span>
|
|
200
|
+
<span class="zv-cell zv-cell--num"><span class="zv-cell__label">Taille</span>840 Ko</span>
|
|
201
|
+
<span class="zv-cell zv-cell--num">11/08/2026</span>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="zv-pager zv-pager--foot">
|
|
204
|
+
<button class="zv-pager__arrow" aria-label="Page précédente">‹</button>
|
|
205
|
+
<span class="zv-pager__rank">1 – 25 sur 128</span>
|
|
206
|
+
<button class="zv-pager__arrow" aria-label="Page suivante">›</button>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div class="d-grid">
|
|
211
|
+
<div class="d-box">
|
|
212
|
+
<div class="zv-kv zv-kv--grid">
|
|
213
|
+
<div class="zv-kv__row"><span class="zv-kv__key">Nature</span><span class="zv-kv__val">Acte de cession</span></div>
|
|
214
|
+
<div class="zv-kv__row"><span class="zv-kv__key">Pages</span><span class="zv-kv__val">14</span></div>
|
|
215
|
+
<div class="zv-kv__row"><span class="zv-kv__key">Signé le</span><span class="zv-kv__val">04/07/2026</span></div>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
<div class="d-box">
|
|
219
|
+
<div class="zv-list">
|
|
220
|
+
<div class="zv-list__row"><span class="zv-avatar zv-avatar--accent">MK</span><span class="zv-list__main"><span class="zv-list__name">Maître Karila</span><span class="zv-meta">Associé · 4 dossiers</span></span></div>
|
|
221
|
+
<div class="zv-list__row"><span class="zv-avatar zv-avatar--soft">CL</span><span class="zv-list__main"><span class="zv-list__name">Clesame</span><span class="zv-meta zv-meta--quiet">Client · 12 documents</span></span></div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
<div class="d-box">
|
|
225
|
+
<span class="zv-chip-row"><span class="zv-chip">Garantie</span><span class="zv-chip zv-chip--outline">Cession</span><span class="zv-chip zv-chip--mono">40 %</span></span>
|
|
226
|
+
<p class="zv-cite-row" style="margin-top:14px"><span class="zv-cite">p. 12</span> Garantie autonome plafonnée à 40 % du prix de cession.</p>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</section>
|
|
230
|
+
|
|
231
|
+
<!-- ENROBAGES -->
|
|
232
|
+
<section class="d-sec">
|
|
233
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">05 · Enrobages</span></span>
|
|
234
|
+
<p class="zv-note" style="margin-top:10px">Tout ce qui se pose par-dessus la page. La modale et la notification sont dérivées, hors relevé — voir DECISIONS.md.</p>
|
|
235
|
+
|
|
236
|
+
<div class="d-row">
|
|
237
|
+
<button class="zv-btn zv-btn--sm" onclick="document.getElementById('m1').toggleAttribute('data-open')">Ouvrir une modale</button>
|
|
238
|
+
<button class="zv-btn--ghost zv-btn--sm" onclick="document.getElementById('m2').toggleAttribute('data-open')">Modale destructive</button>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div class="d-row" style="align-items:stretch;flex-direction:column">
|
|
242
|
+
<span class="zv-toast">Lien copié dans le presse-papier.</span>
|
|
243
|
+
<span class="zv-toast zv-toast--ok">Douze documents indexés.</span>
|
|
244
|
+
<span class="zv-toast zv-toast--err">L'envoi a échoué. Réessayez.</span>
|
|
245
|
+
</div>
|
|
246
|
+
</section>
|
|
247
|
+
|
|
248
|
+
<div class="zv-modal" id="m1">
|
|
249
|
+
<span class="zv-scrim" onclick="document.getElementById('m1').removeAttribute('data-open')"></span>
|
|
250
|
+
<div class="zv-modal__panel">
|
|
251
|
+
<div class="zv-modal__head">
|
|
252
|
+
<h3 class="zv-modal__title">Partager ce dossier</h3>
|
|
253
|
+
<button class="zv-icon-btn" aria-label="Fermer" onclick="document.getElementById('m1').removeAttribute('data-open')"><svg class="zv-icon"><use href="../icons/sprite.svg#zv-fermer"/></svg></button>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="zv-modal__body">Les personnes invitées accèdent aux documents de ce dossier, jamais au reste de la base.</div>
|
|
256
|
+
<div class="zv-modal__foot">
|
|
257
|
+
<button class="zv-btn--ghost zv-btn--sm" onclick="document.getElementById('m1').removeAttribute('data-open')">Annuler</button>
|
|
258
|
+
<button class="zv-btn zv-btn--sm">Inviter</button>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div class="zv-modal zv-modal--danger" id="m2">
|
|
264
|
+
<span class="zv-scrim" onclick="document.getElementById('m2').removeAttribute('data-open')"></span>
|
|
265
|
+
<div class="zv-modal__panel">
|
|
266
|
+
<div class="zv-modal__head"><h3 class="zv-modal__title">Supprimer ce document ?</h3></div>
|
|
267
|
+
<div class="zv-modal__body">Cette action retire le document de la base et de l'index. Elle est définitive.</div>
|
|
268
|
+
<div class="zv-modal__foot">
|
|
269
|
+
<button class="zv-btn--ghost zv-btn--sm" onclick="document.getElementById('m2').removeAttribute('data-open')">Annuler</button>
|
|
270
|
+
<button class="zv-btn--ink zv-btn--sm">Supprimer</button>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</body>
|
|
276
|
+
</html>
|
package/demo/index.html
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Zevra UI — planche de vérification</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
8
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Outfit:wght@200;300;400;500;600&family=Public+Sans:wght@400;600&family=Space+Grotesk:wght@500;600;700&display=swap">
|
|
9
|
+
<link rel="stylesheet" href="../src/index.css">
|
|
10
|
+
<style>
|
|
11
|
+
/* Propre à la planche : elle n'appartient pas au système. */
|
|
12
|
+
.demo-sec { padding-top: 72px; }
|
|
13
|
+
.demo-sec > h2 { margin: 0 0 6px; }
|
|
14
|
+
.demo-row { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; margin-top: 20px; }
|
|
15
|
+
.demo-row > * { min-width: 240px; }
|
|
16
|
+
.demo-note { margin-top: 4px; }
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<div class="zv-page">
|
|
21
|
+
|
|
22
|
+
<!-- Chrome -->
|
|
23
|
+
<nav class="zv-nav zv-gutter">
|
|
24
|
+
<span class="zv-display" style="font-size:21px;font-weight:600;letter-spacing:0.22em;text-transform:uppercase">Zevra</span>
|
|
25
|
+
<button class="zv-nav__burger" aria-label="Ouvrir le menu" onclick="document.getElementById('menu').toggleAttribute('data-open')">
|
|
26
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h16"/></svg>
|
|
27
|
+
</button>
|
|
28
|
+
<span class="zv-nav__links zv-only-desktop">
|
|
29
|
+
<a class="zv-navlink" href="#">Produits</a>
|
|
30
|
+
<a class="zv-navlink" href="#">Méthode</a>
|
|
31
|
+
<a class="zv-navlink zv-navlink--quiet" href="#">Se connecter</a>
|
|
32
|
+
<a class="zv-btn zv-btn--sm" href="#">Réserver 30 min</a>
|
|
33
|
+
</span>
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<!-- Tiroir de navigation mobile -->
|
|
37
|
+
<div class="zv-drawer" id="menu">
|
|
38
|
+
<span class="zv-scrim" onclick="document.getElementById('menu').removeAttribute('data-open')"></span>
|
|
39
|
+
<nav class="zv-drawer__panel">
|
|
40
|
+
<span class="zv-nav-drawer">
|
|
41
|
+
<a class="zv-nav-drawer__link zv-nav-drawer__link--on" href="#">Produits</a>
|
|
42
|
+
<a class="zv-nav-drawer__link" href="#">Méthode</a>
|
|
43
|
+
<a class="zv-nav-drawer__link" href="#">Sécurité</a>
|
|
44
|
+
<a class="zv-nav-drawer__link" href="#">Tarifs</a>
|
|
45
|
+
<a class="zv-btn zv-btn--block" href="#" style="margin-top:12px">Réserver 30 min</a>
|
|
46
|
+
</span>
|
|
47
|
+
</nav>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Hero -->
|
|
51
|
+
<header class="zv-hero zv-gutter--wide">
|
|
52
|
+
<span class="zv-halo zv-halo--hero zv-anim-breathe" aria-hidden="true"></span>
|
|
53
|
+
<span class="zv-kicker zv-kicker--center">
|
|
54
|
+
<span class="zv-spec" aria-hidden="true"></span>
|
|
55
|
+
<span class="zv-mono">Planche de vérification</span>
|
|
56
|
+
</span>
|
|
57
|
+
<h1 class="zv-h1">Un système pour des outils <span class="zv-em">qui refusent de louer leur intelligence.</span></h1>
|
|
58
|
+
<p class="zv-lead">Cette page n'existe que pour regarder le paquet fonctionner : chaque composant y est posé une fois, dans son contexte.</p>
|
|
59
|
+
<span class="zv-tagline">Votre savoir, interrogeable en une phrase</span>
|
|
60
|
+
<span class="zv-btn-row">
|
|
61
|
+
<a class="zv-btn" href="#">Action principale</a>
|
|
62
|
+
<a class="zv-btn--ghost" href="#">Action secondaire</a>
|
|
63
|
+
</span>
|
|
64
|
+
</header>
|
|
65
|
+
|
|
66
|
+
<!-- Typographie -->
|
|
67
|
+
<section class="zv-gutter demo-sec">
|
|
68
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">01 · Typographie</span></span>
|
|
69
|
+
<div class="zv-grid-12" style="margin-top:28px">
|
|
70
|
+
<div class="zv-col-8">
|
|
71
|
+
<h2 class="zv-h2">Titre de section <span class="zv-em">en deux temps.</span></h2>
|
|
72
|
+
<p class="zv-lead demo-note">Chapô — 18px, gris, interligne 1.6.</p>
|
|
73
|
+
<p class="zv-body demo-note">Corps de texte à 15px. Un <strong>mot fort</strong> passe en encre.</p>
|
|
74
|
+
<p class="zv-body zv-body--dense demo-note">Variante dense à 13px, pour les légendes de carte.</p>
|
|
75
|
+
<p class="zv-rule demo-note">Texte de règle à 14px, celui des planches de documentation.</p>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="zv-col-4">
|
|
78
|
+
<p class="zv-body zv-body--lg">Colonne d'accompagnement à 16px, alignée sur le bas du titre.</p>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<!-- Boutons et badges -->
|
|
84
|
+
<section class="zv-gutter demo-sec">
|
|
85
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">02 · Boutons et badges</span></span>
|
|
86
|
+
<div class="demo-row">
|
|
87
|
+
<a class="zv-btn" href="#">Bouton plein</a>
|
|
88
|
+
<a class="zv-btn--ghost" href="#">Fantôme</a>
|
|
89
|
+
<a class="zv-btn--ink" href="#">Encre</a>
|
|
90
|
+
<a class="zv-btn zv-btn--sm" href="#">Compact</a>
|
|
91
|
+
<button class="zv-btn" disabled>Désactivé</button>
|
|
92
|
+
<a class="zv-link-arrow" href="#">Lien fléché
|
|
93
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M13 6l6 6-6 6"/></svg>
|
|
94
|
+
</a>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="demo-row">
|
|
97
|
+
<span class="zv-pill">Pièce 01 · Connaissance</span>
|
|
98
|
+
<span class="zv-src-row">
|
|
99
|
+
<span class="zv-src">BOSS</span><span class="zv-src">Judilibre</span><span class="zv-src">KALI</span><span class="zv-src zv-src--quiet">+ 11</span>
|
|
100
|
+
</span>
|
|
101
|
+
<span class="zv-tag zv-tag--ok">Validé</span>
|
|
102
|
+
<span class="zv-tag zv-tag--err">Interdit</span>
|
|
103
|
+
</div>
|
|
104
|
+
<p class="zv-ent-text demo-row" style="display:block">Par acte du <span class="zv-ent">DATE_1</span>, la société <span class="zv-ent">SOCIÉTÉ_1</span> a cédé à <span class="zv-ent">PERSONNE_1</span> l'intégralité des parts.</p>
|
|
105
|
+
<div class="zv-progress" style="margin-top:24px"><span class="zv-progress__fill" style="width:38%"></span></div>
|
|
106
|
+
</section>
|
|
107
|
+
|
|
108
|
+
<!-- Cartes -->
|
|
109
|
+
<section class="zv-gutter demo-sec">
|
|
110
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">03 · Cartes et encarts</span></span>
|
|
111
|
+
<div class="zv-grid-3" style="margin-top:28px">
|
|
112
|
+
<article class="zv-card zv-card--pad-lg" style="display:flex;flex-direction:column;gap:18px;min-height:300px">
|
|
113
|
+
<span class="zv-mono zv-mono--label">Recherche</span>
|
|
114
|
+
<h3 class="zv-card-title">Une carte produit, avec sa preuve en bas.</h3>
|
|
115
|
+
<div class="zv-inset zv-inset--bottom">Réponse sourcée sur <span class="zv-inset__key">14 décisions</span>.</div>
|
|
116
|
+
</article>
|
|
117
|
+
<article class="zv-card zv-card--pad-lg" style="display:flex;flex-direction:column;gap:18px;min-height:300px">
|
|
118
|
+
<span class="zv-mono zv-mono--label">Rédaction</span>
|
|
119
|
+
<h3 class="zv-card-title">Encart de document, plus aéré.</h3>
|
|
120
|
+
<div class="zv-inset zv-inset--quiet zv-inset--bottom">Extrait de l'acte, restitué tel quel dans le fil du dossier.</div>
|
|
121
|
+
</article>
|
|
122
|
+
<article class="zv-card zv-card--pad-lg" style="display:flex;flex-direction:column;gap:18px;min-height:300px">
|
|
123
|
+
<span class="zv-mono zv-mono--label">Mesure</span>
|
|
124
|
+
<h3 class="zv-card-title">Statistiques de pied de carte.</h3>
|
|
125
|
+
<div class="zv-ministat zv-inset--bottom">
|
|
126
|
+
<div class="zv-ministat__item"><span class="zv-mono zv-mono--micro">Sources</span><span class="zv-ministat__value">1 240</span></div>
|
|
127
|
+
<div class="zv-ministat__item"><span class="zv-mono zv-mono--micro">Délai</span><span class="zv-ministat__value">1,8 s</span></div>
|
|
128
|
+
</div>
|
|
129
|
+
</article>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="demo-row" style="align-items:stretch">
|
|
132
|
+
<div class="zv-card zv-card--ok zv-card--pad" style="flex:1"><span class="zv-tag zv-tag--ok">Oui</span><p class="zv-body zv-body--dense" style="margin-top:8px">Carte d'état validé.</p></div>
|
|
133
|
+
<div class="zv-card zv-card--err zv-card--pad" style="flex:1"><span class="zv-tag zv-tag--err">Non</span><p class="zv-body zv-body--dense" style="margin-top:8px">Carte d'état refusé.</p></div>
|
|
134
|
+
<div class="zv-stat-card"><span class="zv-mono zv-mono--micro">Cabinets</span><span class="zv-stat-card__value">38</span><span class="zv-body zv-body--dense">équipés cette année</span></div>
|
|
135
|
+
</div>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
<!-- Surfaces -->
|
|
139
|
+
<section class="demo-sec">
|
|
140
|
+
<div class="zv-gutter">
|
|
141
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">04 · Surfaces</span></span>
|
|
142
|
+
</div>
|
|
143
|
+
<div style="position:relative;overflow:hidden;height:280px;margin-top:24px">
|
|
144
|
+
<span class="zv-grid-floor" aria-hidden="true"></span>
|
|
145
|
+
<span class="zv-fade" aria-hidden="true"></span>
|
|
146
|
+
<div class="zv-glass zv-gutter" style="position:relative;max-width:520px;margin:48px auto 0;padding-top:28px;padding-bottom:28px">
|
|
147
|
+
<span class="zv-mono zv-mono--label">Carte verre</span>
|
|
148
|
+
<p class="zv-body" style="margin-top:10px">Posée devant un sol quadrillé, avec son voile de fondu.</p>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</section>
|
|
152
|
+
|
|
153
|
+
<!-- Manifeste -->
|
|
154
|
+
<section class="zv-band" style="margin-top:64px">
|
|
155
|
+
<span class="zv-band__glow" aria-hidden="true"></span>
|
|
156
|
+
<div class="zv-band__content zv-gutter">
|
|
157
|
+
<span class="zv-kicker"><span class="zv-spec zv-spec--on-dark" aria-hidden="true"></span><span class="zv-mono zv-mono--on-dark">05 · Manifeste</span></span>
|
|
158
|
+
<h2 class="zv-h2" style="margin-top:24px;max-width:900px">La seule surface sombre du système, <span class="zv-em zv-em--on-dark">et elle ne s'obtient pas en inversant les neutres.</span></h2>
|
|
159
|
+
<p class="zv-body" style="margin-top:20px;max-width:560px">Les labels passent en indigo clair, les filets en blanc à 18 %.</p>
|
|
160
|
+
<div class="zv-stats" style="margin-top:40px">
|
|
161
|
+
<div class="zv-stat"><span class="zv-stat__value">5</span><span class="zv-mono zv-mono--micro zv-mono--on-dark">produits</span></div>
|
|
162
|
+
<div class="zv-stat"><span class="zv-stat__value">40</span><span class="zv-mono zv-mono--micro zv-mono--on-dark">logos</span></div>
|
|
163
|
+
<div class="zv-stat"><span class="zv-stat__value">32</span><span class="zv-mono zv-mono--micro zv-mono--on-dark">tokens</span></div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</section>
|
|
167
|
+
|
|
168
|
+
<!-- Méthode -->
|
|
169
|
+
<section class="zv-gutter demo-sec">
|
|
170
|
+
<span class="zv-kicker"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">06 · Méthode</span></span>
|
|
171
|
+
<div class="zv-steps" style="margin-top:36px">
|
|
172
|
+
<span class="zv-steps__rail" aria-hidden="true"></span>
|
|
173
|
+
<span class="zv-steps__progress" style="width:38%" aria-hidden="true"></span>
|
|
174
|
+
<div class="zv-step"><span class="zv-step__dot" aria-hidden="true"></span><span class="zv-mono zv-mono--label">Étape 01</span><h3 class="zv-card-title">Relever</h3><p class="zv-rule">Les valeurs viennent des maquettes, pas de l'intuition.</p></div>
|
|
175
|
+
<div class="zv-step"><span class="zv-step__dot zv-step__dot--violet" aria-hidden="true"></span><span class="zv-mono zv-mono--label">Étape 02</span><h3 class="zv-card-title">Arbitrer</h3><p class="zv-rule">Chaque désaccord est écrit, jamais tranché en silence.</p></div>
|
|
176
|
+
<div class="zv-step"><span class="zv-step__dot zv-step__dot--cyan" aria-hidden="true"></span><span class="zv-mono zv-mono--label">Étape 03</span><h3 class="zv-card-title">Verrouiller</h3><p class="zv-rule">Un test échoue si les deux sources divergent.</p></div>
|
|
177
|
+
</div>
|
|
178
|
+
</section>
|
|
179
|
+
|
|
180
|
+
<!-- Blocs de landing -->
|
|
181
|
+
<section class="zv-gutter demo-sec">
|
|
182
|
+
<span class="zv-kicker"><span class="zv-spec zv-anim-filet" aria-hidden="true"></span><span class="zv-mono">07 · Blocs de landing</span></span>
|
|
183
|
+
<h2 class="zv-h2 zv-h2--sm zv-anim-rise" style="margin-top:20px">Un titre de landing, <span class="zv-em">d'un cran plus court.</span></h2>
|
|
184
|
+
<div class="zv-grid-3" style="margin-top:28px;align-items:start">
|
|
185
|
+
|
|
186
|
+
<div class="zv-price zv-price--featured">
|
|
187
|
+
<span class="zv-pill">Early adopter</span>
|
|
188
|
+
<span class="zv-price__amount">1 250 € <span class="zv-price__unit">HT · 1re année</span></span>
|
|
189
|
+
<span class="zv-price__note">Puis 2 900 € HT plein tarif.</span>
|
|
190
|
+
<span class="zv-note" style="display:flex;align-items:center;gap:8px;margin-top:4px">
|
|
191
|
+
<span class="zv-check" aria-hidden="true"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></span>
|
|
192
|
+
Mise en service incluse
|
|
193
|
+
</span>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="zv-quote">
|
|
197
|
+
<span class="zv-spec zv-spec--on-dark" aria-hidden="true"></span>
|
|
198
|
+
<span class="zv-quote__text">« [Citation client à recueillir.] »</span>
|
|
199
|
+
<span class="zv-mono zv-mono--micro zv-mono--on-dark">[Nom, cabinet]</span>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div class="zv-ui-preview">
|
|
203
|
+
<span class="zv-ui-preview__q">
|
|
204
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>
|
|
205
|
+
Quelle clause de garantie depuis 2021 ?
|
|
206
|
+
</span>
|
|
207
|
+
<span class="zv-divider" aria-hidden="true"></span>
|
|
208
|
+
<span class="zv-ui-preview__a">Garantie autonome plafonnée à 40 %… <span class="zv-ui-preview__count">3 sources</span></span>
|
|
209
|
+
<span class="zv-src-row"><span class="zv-src">Consultation</span><span class="zv-src">Protocole</span></span>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<div class="zv-card" style="margin-top:24px;overflow:hidden">
|
|
215
|
+
<div class="zv-faq">
|
|
216
|
+
<div class="zv-faq__q">Où sont hébergées nos données ?
|
|
217
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="zv-faq__a">En France, sur infrastructure OVHcloud, dans un réseau privé cloisonné.</div>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div class="zv-card" style="margin-top:24px">
|
|
224
|
+
<div class="zv-proof-bar">
|
|
225
|
+
<span class="zv-mono zv-mono--micro">Ils bâtissent avec nous</span>
|
|
226
|
+
<span class="zv-proof-bar__names"><span>Karila</span><span>Orange</span><span>Clesame</span><span>Beside Avocats</span><span>Dairia</span><span>BL-NK</span></span>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</section>
|
|
230
|
+
|
|
231
|
+
<!-- CTA -->
|
|
232
|
+
<section class="zv-cta zv-gutter--wide">
|
|
233
|
+
<span class="zv-halo zv-halo--cta" aria-hidden="true"></span>
|
|
234
|
+
<span class="zv-kicker zv-kicker--center"><span class="zv-spec" aria-hidden="true"></span><span class="zv-mono">Fin de planche</span></span>
|
|
235
|
+
<h2 class="zv-h2 zv-h2--cta">Le titre de CTA prend <span class="zv-em">un cran de plus.</span></h2>
|
|
236
|
+
<a class="zv-btn" href="#">Action finale</a>
|
|
237
|
+
</section>
|
|
238
|
+
|
|
239
|
+
<footer class="zv-footer zv-inset-x">
|
|
240
|
+
<div class="zv-footer__brand">
|
|
241
|
+
<span class="zv-spec zv-spec--sm" aria-hidden="true"></span>
|
|
242
|
+
<p class="zv-body zv-body--dense">Zevra UI — design system « Encre & Papier ».</p>
|
|
243
|
+
<span class="zv-mono zv-mono--micro">Planche de vérification, hors paquet</span>
|
|
244
|
+
</div>
|
|
245
|
+
<div class="zv-footer__cols">
|
|
246
|
+
<div><span class="zv-mono zv-mono--micro zv-mono--ink" style="display:block">Modules</span><a class="zv-link" href="#">tokens</a><br><a class="zv-link" href="#">base</a><br><a class="zv-link" href="#">components</a></div>
|
|
247
|
+
<div><span class="zv-mono zv-mono--micro zv-mono--ink" style="display:block">Suite</span><a class="zv-link" href="#">layout</a><br><a class="zv-link" href="#">surfaces</a></div>
|
|
248
|
+
</div>
|
|
249
|
+
</footer>
|
|
250
|
+
|
|
251
|
+
<div class="zv-footer-mini">
|
|
252
|
+
<span class="zv-mono zv-mono--micro">Zevra · Legaltech souveraine · France</span>
|
|
253
|
+
<span class="zv-spec" style="width:32px" aria-hidden="true"></span>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
</div>
|
|
257
|
+
</body>
|
|
258
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zevra/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Design system Zevra « Encre & Papier » — tokens, CSS des composants et logos officiels.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -29,19 +29,22 @@
|
|
|
29
29
|
"./layout.css": "./src/layout.css",
|
|
30
30
|
"./surfaces.css": "./src/surfaces.css",
|
|
31
31
|
"./motion.css": "./src/motion.css",
|
|
32
|
-
"./logos/*": "./logos/*",
|
|
33
32
|
"./icons.css": "./src/icons.css",
|
|
33
|
+
"./overlays.css": "./src/overlays.css",
|
|
34
34
|
"./forms.css": "./src/forms.css",
|
|
35
35
|
"./feedback.css": "./src/feedback.css",
|
|
36
36
|
"./navigation.css": "./src/navigation.css",
|
|
37
37
|
"./data.css": "./src/data.css",
|
|
38
|
-
"./
|
|
38
|
+
"./logos/*": "./logos/*",
|
|
39
|
+
"./icons/*": "./icons/*",
|
|
40
|
+
"./demo/*": "./demo/*"
|
|
39
41
|
},
|
|
40
42
|
"files": [
|
|
41
43
|
"src",
|
|
42
44
|
"tokens",
|
|
43
45
|
"logos",
|
|
44
46
|
"icons",
|
|
47
|
+
"demo",
|
|
45
48
|
"README.md",
|
|
46
49
|
"DECISIONS.md"
|
|
47
50
|
],
|
package/src/index.css
CHANGED
package/src/layout.css
CHANGED
|
@@ -195,6 +195,77 @@
|
|
|
195
195
|
text-decoration: none;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/* Bouton burger de la barre vitrine.
|
|
199
|
+
*
|
|
200
|
+
* ⚠️ HORS RELEVÉ des planches vitrine, qui sont toutes des rendus figés à
|
|
201
|
+
* 1440px et n'ont donc jamais eu de menu mobile. Mobile.html en montre un,
|
|
202
|
+
* mais sans en donner le tracé.
|
|
203
|
+
*
|
|
204
|
+
* Il est ici parce qu'une page sans lui n'a AUCUN moyen d'atteindre sa
|
|
205
|
+
* navigation sur téléphone : sous le seuil, .zv-nav__links doit être masqué
|
|
206
|
+
* (.zv-only-desktop) et rien ne le remplace. Le tiroir qu'il ouvre vit dans
|
|
207
|
+
* overlays.css.
|
|
208
|
+
*
|
|
209
|
+
* Il n'apparaît que sous le seuil : au-dessus, les liens sont là. */
|
|
210
|
+
.zv-nav__burger {
|
|
211
|
+
display: none;
|
|
212
|
+
align-items: center;
|
|
213
|
+
justify-content: center;
|
|
214
|
+
/* 40px de cible tactile, obtenus par la taille et non par du padding : un
|
|
215
|
+
* bouton de 24px entouré de marge reste un bouton de 24px pour le doigt. */
|
|
216
|
+
width: 40px;
|
|
217
|
+
height: 40px;
|
|
218
|
+
/* Les marges négatives ramènent le bouton à l'alignement optique de la
|
|
219
|
+
* barre : sans elles, sa zone tactile décale le logo et le bord droit. */
|
|
220
|
+
margin: -8px -8px -8px 0;
|
|
221
|
+
padding: 0;
|
|
222
|
+
border: 0;
|
|
223
|
+
background: transparent;
|
|
224
|
+
color: var(--ink);
|
|
225
|
+
cursor: pointer;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.zv-nav__burger svg {
|
|
229
|
+
width: 20px;
|
|
230
|
+
height: 20px;
|
|
231
|
+
fill: none;
|
|
232
|
+
stroke: currentColor;
|
|
233
|
+
stroke-width: 2;
|
|
234
|
+
stroke-linecap: round;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Le tiroir de la vitrine : liens empilés, respiration généreuse. Le panneau
|
|
238
|
+
* lui-même vient d'overlays.css ; ceci n'habille que son contenu. */
|
|
239
|
+
.zv-nav-drawer {
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
gap: 4px;
|
|
243
|
+
padding: 20px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.zv-nav-drawer__link {
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
/* Chaque entrée fait au moins une cible tactible pleine : une liste de
|
|
250
|
+
* liens serrés est la première cause de faux appuis sur téléphone. */
|
|
251
|
+
min-height: var(--tap);
|
|
252
|
+
padding: 0 12px;
|
|
253
|
+
border-radius: var(--r-panel);
|
|
254
|
+
font-size: 15px;
|
|
255
|
+
font-weight: 500;
|
|
256
|
+
color: var(--ink);
|
|
257
|
+
text-decoration: none;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.zv-nav-drawer__link:hover {
|
|
261
|
+
background: var(--panel);
|
|
262
|
+
color: var(--accent);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.zv-nav-drawer__link--on {
|
|
266
|
+
color: var(--accent);
|
|
267
|
+
}
|
|
268
|
+
|
|
198
269
|
.zv-navlink--quiet {
|
|
199
270
|
color: var(--label);
|
|
200
271
|
}
|
|
@@ -473,6 +544,11 @@
|
|
|
473
544
|
padding-top: 20px;
|
|
474
545
|
}
|
|
475
546
|
|
|
547
|
+
/* Le burger n'existe que là où les liens disparaissent. */
|
|
548
|
+
.zv-nav__burger {
|
|
549
|
+
display: inline-flex;
|
|
550
|
+
}
|
|
551
|
+
|
|
476
552
|
.zv-nav__logo {
|
|
477
553
|
height: 30px;
|
|
478
554
|
}
|
package/src/navigation.css
CHANGED
|
@@ -314,105 +314,6 @@
|
|
|
314
314
|
* · fermeture — le caractère ✕ (U+2715) posé en texte, d'où le
|
|
315
315
|
* `font-size` ci-dessus qui le dimensionne. */
|
|
316
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
317
|
/* ─── Feuille de source citée ───────────────────────────────────────────
|
|
417
318
|
* ⚠️ RENOMMÉE : cette pièce s'appelait .zv-sheet. layout.css possède déjà
|
|
418
319
|
* .zv-sheet-section (section de PLANCHE de documentation, avec son __title et
|
|
@@ -742,14 +643,6 @@
|
|
|
742
643
|
padding-right: calc(var(--tap) + 16px);
|
|
743
644
|
}
|
|
744
645
|
|
|
745
|
-
.zv-scrim {
|
|
746
|
-
display: block;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
.zv-drawer {
|
|
750
|
-
display: block;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
646
|
/* La feuille se cale À RAS sous la barre.
|
|
754
647
|
*
|
|
755
648
|
* La production pose top: 53px pour une barre de 52 (citation.tsx:101
|
package/src/overlays.css
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/* Zevra UI — enrobages : voile, tiroir, modale, notification
|
|
2
|
+
* Dépend de tokens.css et base.css.
|
|
3
|
+
*
|
|
4
|
+
* Ce module est COMMUN aux deux couches. Une page vitrine a besoin d'un
|
|
5
|
+
* tiroir pour son menu mobile ; une application en a besoin pour sa
|
|
6
|
+
* navigation. Les laisser dans navigation.css obligeait une landing à
|
|
7
|
+
* charger la barre latérale, la pagination et les tableaux pour obtenir un
|
|
8
|
+
* bouton burger — c'est ce qui a motivé cette séparation.
|
|
9
|
+
*
|
|
10
|
+
* Tout ce qui vit ici se pose PAR-DESSUS la page et lui vole le regard. D'où
|
|
11
|
+
* une règle commune : rien ne s'ouvre sans un moyen évident d'en sortir, et
|
|
12
|
+
* l'échappement au clavier est du ressort du gabarit, que le CSS ne peut pas
|
|
13
|
+
* assurer seul.
|
|
14
|
+
*
|
|
15
|
+
* Empilement, dans l'ordre : voile 60, tiroir 80, modale 90, notification 100.
|
|
16
|
+
* Les couches inférieures (2, 5, 40, 50) sont dans layout.css.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* ─── Voile ─────────────────────────────────────────────────────────────
|
|
20
|
+
* Toujours l'encre de charte diluée, jamais un noir pur : la page est
|
|
21
|
+
* bleutée, un gris neutre y ferait un trou sale. Le voile n'existe que sous
|
|
22
|
+
* le seuil (cf. bloc mobile) — au-dessus, tiroir et feuille sont des colonnes,
|
|
23
|
+
* elles n'occultent rien. */
|
|
24
|
+
|
|
25
|
+
.zv-scrim {
|
|
26
|
+
display: none;
|
|
27
|
+
position: fixed;
|
|
28
|
+
inset: 0;
|
|
29
|
+
z-index: 60;
|
|
30
|
+
background: color-mix(in oklab, var(--ink) 40%, transparent);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ─── Tiroir de navigation ──────────────────────────────────────────────*/
|
|
34
|
+
|
|
35
|
+
.zv-drawer {
|
|
36
|
+
display: none;
|
|
37
|
+
position: fixed;
|
|
38
|
+
inset: 0;
|
|
39
|
+
z-index: 80;
|
|
40
|
+
/* `visibility` plutôt que `display` : elle sort le contenu du parcours
|
|
41
|
+
* clavier ET se transitionne, ce qui retient le panneau à l'écran le temps
|
|
42
|
+
* qu'il glisse dehors. En `display: none`, il disparaîtrait à mi-course. */
|
|
43
|
+
visibility: hidden;
|
|
44
|
+
transition: visibility 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.zv-drawer[data-open] {
|
|
48
|
+
visibility: visible;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Le voile du tiroir accompagne un panneau qui glisse : il se fond. Celui de
|
|
52
|
+
* la feuille de source apparaît net, faute de mouvement à accompagner.
|
|
53
|
+
*
|
|
54
|
+
* Et il redescend au sol : son z-index de 60 est la couche qu'il occupe
|
|
55
|
+
* quand il est SEUL sur la page. À l'intérieur d'un enrobage, qui ouvre son
|
|
56
|
+
* propre contexte d'empilement, ce 60 l'emportait sur le panneau et voilait
|
|
57
|
+
* le tiroir par-dessus — il paraissait délavé, derrière son propre écran. */
|
|
58
|
+
.zv-drawer .zv-scrim {
|
|
59
|
+
z-index: 0;
|
|
60
|
+
opacity: 0;
|
|
61
|
+
transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.zv-drawer[data-open] .zv-scrim {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.zv-drawer__panel {
|
|
69
|
+
position: absolute;
|
|
70
|
+
/* Le panneau passe DEVANT son voile. .zv-drawer ouvre un contexte
|
|
71
|
+
* d'empilement, dans lequel le z-index 60 du voile l'emporte sur un
|
|
72
|
+
* panneau resté en `auto` : le tiroir s'affichait alors délavé, comme s'il
|
|
73
|
+
* était derrière son propre écran. */
|
|
74
|
+
z-index: 1;
|
|
75
|
+
inset-block: 0;
|
|
76
|
+
left: 0;
|
|
77
|
+
display: flex;
|
|
78
|
+
width: 288px;
|
|
79
|
+
/* Le plafond en pourcentage garde toujours une bande de page visible
|
|
80
|
+
* derrière : un tiroir qui couvre tout l'écran ne se lit plus comme un
|
|
81
|
+
* tiroir, et l'utilisateur perd l'endroit où appuyer pour sortir. */
|
|
82
|
+
max-width: 86%;
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
overflow-y: auto;
|
|
85
|
+
border-right: 1px solid var(--line);
|
|
86
|
+
background: var(--panel);
|
|
87
|
+
border-radius: 0;
|
|
88
|
+
/* Aucune ombre : c'est le voile qui fait le contraste. En ajouter une
|
|
89
|
+
* doublerait la séparation et salirait le bord. */
|
|
90
|
+
box-shadow: none;
|
|
91
|
+
transform: translateX(-100%);
|
|
92
|
+
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.zv-drawer[data-open] .zv-drawer__panel {
|
|
96
|
+
transform: translateX(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ⚠️ TENSION ASSUMÉE, à arbitrer. Les deux courbes ci-dessus sont celles du
|
|
100
|
+
* relevé — voile en cubic-bezier(.4,0,.2,1), panneau en ease-out, même durée.
|
|
101
|
+
* Leur asymétrie ne se voit pas, elle s'entend : le panneau arrive avant que
|
|
102
|
+
* le voile ait fini de se poser. Elles ne sont donc PAS la courbe unique
|
|
103
|
+
* --zv-easing que motion.css impose à la vitrine. Défendable (motion.css régit
|
|
104
|
+
* neuf animations de page, pas le chrome applicatif), mais c'est une divergence
|
|
105
|
+
* écrite ici plutôt qu'une variable empruntée en silence — d'autant que ce
|
|
106
|
+
* module doit rester utilisable sans motion.css.
|
|
107
|
+
* La coupure prefers-reduced-motion est déjà globale dans base.css : ne pas la
|
|
108
|
+
* reposer ici, elle neutralise ces deux transitions comme les autres. */
|
|
109
|
+
|
|
110
|
+
.zv-drawer__close {
|
|
111
|
+
position: absolute;
|
|
112
|
+
top: 7px;
|
|
113
|
+
right: 8px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Comportement attendu du composant, à conserver tel quel :
|
|
117
|
+
* · role="dialog" aria-modal="true" aria-label="Navigation", et aria-hidden
|
|
118
|
+
* qui reflète l'état fermé ;
|
|
119
|
+
* · clic sur le voile ou touche Échap → fermeture ;
|
|
120
|
+
* · à l'ouverture, `body { overflow: hidden }` RESTAURÉ à sa valeur
|
|
121
|
+
* précédente à la fermeture, jamais remis à '' aveuglément : c'est ce qui
|
|
122
|
+
* évite de casser une page qui gérait déjà son propre défilement ;
|
|
123
|
+
* · l'état ouvert mémorise la ROUTE d'ouverture, pas un booléen — changer de
|
|
124
|
+
* page referme alors le tiroir sans effet ni rendu en cascade. Le
|
|
125
|
+
* composant a donc besoin de la route courante, pas d'une prop `open`.
|
|
126
|
+
* ⚠️ MANQUE en production, à combler côté composant : piège de focus dans le
|
|
127
|
+
* panneau et restitution du focus au bouton d'ouverture à la fermeture. */
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/* ─── Modale ────────────────────────────────────────────────────────────
|
|
131
|
+
* ⚠️ HORS RELEVÉ. Aucune modale dans les sources de Mémoire — l'application
|
|
132
|
+
* n'en emploie pas. Celle-ci est DÉRIVÉE de la carte et du tiroir, sans
|
|
133
|
+
* introduire une seule valeur nouvelle : elle réemploie --r-card,
|
|
134
|
+
* --shadow-panel et le voile ci-dessus.
|
|
135
|
+
*
|
|
136
|
+
* Elle existe parce que la règle du projet en impose la forme (panneau
|
|
137
|
+
* plafonné à 92vh, qui défile en son sein) et qu'à défaut chaque app en
|
|
138
|
+
* réinventerait une.
|
|
139
|
+
*
|
|
140
|
+
* Employer <dialog> plutôt que ce balisage quand c'est possible : le
|
|
141
|
+
* navigateur y apporte le piège de focus et la fermeture par Échap, que le
|
|
142
|
+
* CSS ne sait pas faire. */
|
|
143
|
+
|
|
144
|
+
.zv-modal {
|
|
145
|
+
display: none;
|
|
146
|
+
position: fixed;
|
|
147
|
+
inset: 0;
|
|
148
|
+
z-index: 90;
|
|
149
|
+
align-items: center;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
padding: 16px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.zv-modal[data-open] {
|
|
155
|
+
display: flex;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Le voile d'une modale se montre à TOUTES les largeurs, et passe sous son
|
|
159
|
+
* panneau.
|
|
160
|
+
*
|
|
161
|
+
* .zv-scrim est masqué par défaut et ne s'affiche que sous le seuil : c'est
|
|
162
|
+
* juste pour le tiroir, qui n'existe qu'en mobile. La modale, elle, existe
|
|
163
|
+
* partout — sans cette règle, elle s'ouvrait en desktop sans rien occulter.
|
|
164
|
+
* Le z-index revient à 0 pour la même raison que sur le tiroir. */
|
|
165
|
+
.zv-modal .zv-scrim {
|
|
166
|
+
z-index: 0;
|
|
167
|
+
display: block;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.zv-modal__panel {
|
|
171
|
+
position: relative;
|
|
172
|
+
z-index: 1;
|
|
173
|
+
display: flex;
|
|
174
|
+
width: 100%;
|
|
175
|
+
max-width: 520px;
|
|
176
|
+
/* Le plafond ET le défilement interne : sans les deux, une modale au
|
|
177
|
+
* contenu long pousse ses actions hors de l'écran, et l'utilisateur ne
|
|
178
|
+
* peut plus ni valider ni annuler. */
|
|
179
|
+
max-height: 92vh;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
overflow: auto;
|
|
182
|
+
border: 1px solid var(--line);
|
|
183
|
+
border-radius: var(--r-card);
|
|
184
|
+
background: var(--white);
|
|
185
|
+
box-shadow: var(--shadow-panel);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.zv-modal__head {
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: flex-start;
|
|
191
|
+
justify-content: space-between;
|
|
192
|
+
gap: 16px;
|
|
193
|
+
padding: 20px 22px 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.zv-modal__title {
|
|
197
|
+
margin: 0;
|
|
198
|
+
font-family: var(--font-display);
|
|
199
|
+
letter-spacing: -0.02em;
|
|
200
|
+
font-size: var(--app-h2);
|
|
201
|
+
font-weight: 400;
|
|
202
|
+
color: var(--ink);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.zv-modal__body {
|
|
206
|
+
padding: 14px 22px;
|
|
207
|
+
font-size: var(--app);
|
|
208
|
+
line-height: 1.6;
|
|
209
|
+
color: var(--muted);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Les actions restent visibles quand le corps défile : c'est tout l'intérêt
|
|
213
|
+
* du plafond ci-dessus. */
|
|
214
|
+
.zv-modal__foot {
|
|
215
|
+
position: sticky;
|
|
216
|
+
bottom: 0;
|
|
217
|
+
display: flex;
|
|
218
|
+
justify-content: flex-end;
|
|
219
|
+
gap: 10px;
|
|
220
|
+
border-top: 1px solid var(--hairline);
|
|
221
|
+
background: var(--white);
|
|
222
|
+
padding: 14px 22px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* Modale destructive : le filet haut porte l'avertissement, pas un fond
|
|
226
|
+
* rouge — une surface d'alerte pleine rendrait le texte pénible à lire. */
|
|
227
|
+
.zv-modal--danger .zv-modal__panel {
|
|
228
|
+
border-top: 3px solid var(--err);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* ─── Notification flottante ────────────────────────────────────────────
|
|
232
|
+
* ⚠️ HORS RELEVÉ, même raison : Mémoire signale tout en place, par une
|
|
233
|
+
* alerte de bloc. Cette notification est l'alerte du module de retours,
|
|
234
|
+
* détachée et posée en coin.
|
|
235
|
+
*
|
|
236
|
+
* Elle ne remplace PAS .zv-alert : une erreur de formulaire se dit à côté du
|
|
237
|
+
* champ, jamais dans un coin qui disparaît. Réserver ce composant à ce qui
|
|
238
|
+
* est vrai après coup — « copié », « enregistré », « envoyé ». */
|
|
239
|
+
|
|
240
|
+
.zv-toasts {
|
|
241
|
+
position: fixed;
|
|
242
|
+
z-index: 100;
|
|
243
|
+
right: 16px;
|
|
244
|
+
bottom: 16px;
|
|
245
|
+
display: flex;
|
|
246
|
+
flex-direction: column-reverse;
|
|
247
|
+
gap: 8px;
|
|
248
|
+
/* Le conteneur ne capte rien : seules les notifications elles-mêmes le
|
|
249
|
+
* font, sans quoi une colonne invisible bloquerait les clics dans tout le
|
|
250
|
+
* coin de l'écran. */
|
|
251
|
+
pointer-events: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.zv-toast {
|
|
255
|
+
pointer-events: auto;
|
|
256
|
+
display: flex;
|
|
257
|
+
align-items: center;
|
|
258
|
+
gap: 10px;
|
|
259
|
+
max-width: 380px;
|
|
260
|
+
border: 1px solid var(--line);
|
|
261
|
+
border-radius: var(--r-panel);
|
|
262
|
+
background: var(--white);
|
|
263
|
+
box-shadow: var(--shadow-menu);
|
|
264
|
+
padding: 12px 14px;
|
|
265
|
+
font-size: var(--app-sm);
|
|
266
|
+
color: var(--ink);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.zv-toast--ok {
|
|
270
|
+
border-color: var(--ok-line);
|
|
271
|
+
background: var(--ok-soft);
|
|
272
|
+
color: var(--ok);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.zv-toast--err {
|
|
276
|
+
border-color: var(--err-line);
|
|
277
|
+
background: var(--err-soft);
|
|
278
|
+
color: var(--err);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* ─── Mobile ────────────────────────────────────────────────────────────*/
|
|
282
|
+
|
|
283
|
+
@media (max-width: 767px) {
|
|
284
|
+
/* Voile et tiroir n'existent QUE sous le seuil : au-dessus, la navigation
|
|
285
|
+
* est une colonne permanente, elle n'occulte rien. */
|
|
286
|
+
.zv-scrim {
|
|
287
|
+
display: block;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.zv-drawer {
|
|
291
|
+
display: block;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* La notification prend la largeur : un coin de 380px sur un écran de 390
|
|
295
|
+
* n'est plus un coin. */
|
|
296
|
+
.zv-toasts {
|
|
297
|
+
right: 12px;
|
|
298
|
+
left: 12px;
|
|
299
|
+
bottom: 12px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.zv-toast {
|
|
303
|
+
max-width: none;
|
|
304
|
+
}
|
|
305
|
+
}
|