@zevra/ui 0.1.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.
Files changed (77) hide show
  1. package/DECISIONS.md +150 -0
  2. package/README.md +177 -0
  3. package/logos/index.json +599 -0
  4. package/logos/png/campus-glyph-ink.png +0 -0
  5. package/logos/png/campus-glyph-orig.png +0 -0
  6. package/logos/png/campus-glyph-white.png +0 -0
  7. package/logos/png/campus-lockup.png +0 -0
  8. package/logos/png/lexform-glyph-ink.png +0 -0
  9. package/logos/png/lexform-glyph-orig.png +0 -0
  10. package/logos/png/lexform-glyph-white.png +0 -0
  11. package/logos/png/lexform-lockup.png +0 -0
  12. package/logos/png/mcp-glyph-ink.png +0 -0
  13. package/logos/png/mcp-glyph-orig.png +0 -0
  14. package/logos/png/mcp-glyph-white.png +0 -0
  15. package/logos/png/mcp-lockup.png +0 -0
  16. package/logos/png/memoire-dark.png +0 -0
  17. package/logos/png/memoire-glyph-ink.png +0 -0
  18. package/logos/png/memoire-glyph-orig.png +0 -0
  19. package/logos/png/memoire-glyph-white.png +0 -0
  20. package/logos/png/memoire-lockup.png +0 -0
  21. package/logos/png/zevra-lockup-ink.png +0 -0
  22. package/logos/png/zevra-lockup-white.png +0 -0
  23. package/logos/png/zevra-lockup.png +0 -0
  24. package/logos/png/zevra-mark-ink.png +0 -0
  25. package/logos/png/zevra-mark-orig.png +0 -0
  26. package/logos/png/zevra-mark-white.png +0 -0
  27. package/logos/png/zevra-mark.png +0 -0
  28. package/logos/svg/campus-glyph-ink.svg +1 -0
  29. package/logos/svg/campus-glyph-navy.svg +1 -0
  30. package/logos/svg/campus-glyph-orig.svg +1 -0
  31. package/logos/svg/campus-glyph-white.svg +1 -0
  32. package/logos/svg/campus-lockup-ink.svg +1 -0
  33. package/logos/svg/campus-lockup-navy.svg +1 -0
  34. package/logos/svg/campus-lockup-orig.svg +1 -0
  35. package/logos/svg/campus-lockup-white.svg +1 -0
  36. package/logos/svg/lexform-glyph-ink.svg +1 -0
  37. package/logos/svg/lexform-glyph-navy.svg +1 -0
  38. package/logos/svg/lexform-glyph-orig.svg +1 -0
  39. package/logos/svg/lexform-glyph-white.svg +1 -0
  40. package/logos/svg/lexform-lockup-ink.svg +1 -0
  41. package/logos/svg/lexform-lockup-navy.svg +1 -0
  42. package/logos/svg/lexform-lockup-orig.svg +1 -0
  43. package/logos/svg/lexform-lockup-white.svg +1 -0
  44. package/logos/svg/mcp-glyph-ink.svg +1 -0
  45. package/logos/svg/mcp-glyph-navy.svg +1 -0
  46. package/logos/svg/mcp-glyph-orig.svg +1 -0
  47. package/logos/svg/mcp-glyph-white.svg +1 -0
  48. package/logos/svg/mcp-lockup-ink.svg +1 -0
  49. package/logos/svg/mcp-lockup-navy.svg +1 -0
  50. package/logos/svg/mcp-lockup-orig.svg +1 -0
  51. package/logos/svg/mcp-lockup-white.svg +1 -0
  52. package/logos/svg/memoire-glyph-ink.svg +1 -0
  53. package/logos/svg/memoire-glyph-navy.svg +1 -0
  54. package/logos/svg/memoire-glyph-orig.svg +1 -0
  55. package/logos/svg/memoire-glyph-white.svg +1 -0
  56. package/logos/svg/memoire-lockup-ink.svg +1 -0
  57. package/logos/svg/memoire-lockup-navy.svg +1 -0
  58. package/logos/svg/memoire-lockup-orig.svg +1 -0
  59. package/logos/svg/memoire-lockup-white.svg +1 -0
  60. package/logos/svg/zevra-lockup-ink.svg +1 -0
  61. package/logos/svg/zevra-lockup-navy.svg +1 -0
  62. package/logos/svg/zevra-lockup-orig.svg +1 -0
  63. package/logos/svg/zevra-lockup-white.svg +1 -0
  64. package/logos/svg/zevra-mark-ink.svg +1 -0
  65. package/logos/svg/zevra-mark-navy.svg +1 -0
  66. package/logos/svg/zevra-mark-orig.svg +1 -0
  67. package/logos/svg/zevra-mark-white.svg +1 -0
  68. package/package.json +52 -0
  69. package/src/base.css +367 -0
  70. package/src/components.css +553 -0
  71. package/src/fonts.css +16 -0
  72. package/src/index.css +11 -0
  73. package/src/layout.css +542 -0
  74. package/src/motion.css +248 -0
  75. package/src/surfaces.css +235 -0
  76. package/src/tokens.css +76 -0
  77. package/tokens/index.js +91 -0
package/src/motion.css ADDED
@@ -0,0 +1,248 @@
1
+ /* Zevra UI — mouvement : les neuf animations autorisées
2
+ * Dépend de tokens.css. Relevé dans `maquettes/Motion.html`, qui les nomme
3
+ * et fixe leurs plages de défilement.
4
+ *
5
+ * NEUF, PAS UNE DE PLUS. Les règles de la passation :
6
+ * · un seul type de mouvement par scène, plus le flottement continu ;
7
+ * · les entrées se font toujours vers le HAUT, jamais latéralement ;
8
+ * · aucun mouvement sur la typographie une fois posée ;
9
+ * · une seule courbe : cubic-bezier(.2, .7, .2, 1).
10
+ *
11
+ * ─── Comment c'est construit ────────────────────────────────────────────
12
+ * L'état AU REPOS de chaque élément est son état FINAL : sans animation, la
13
+ * page est complète et lisible. Les animations d'entrée ne vivent que dans
14
+ * le bloc @supports, si bien qu'un navigateur sans `animation-timeline`
15
+ * affiche simplement le contenu en place plutôt qu'un écran vide.
16
+ *
17
+ * Les deux boucles (drift, breathe) animent les propriétés `translate` et
18
+ * `scale` INDÉPENDANTES, jamais `transform` : les éléments concernés — halo
19
+ * centré par translateX(-50%), glyphe en scène — portent déjà un transform
20
+ * de placement, qu'une animation sur `transform` écraserait.
21
+ *
22
+ * Décalage : poser `--zv-delay` en style inline. Le stagger d'un groupe est
23
+ * de 120 ms (entrées), les vagues de badges de 6 % de défilement.
24
+ */
25
+
26
+ :root {
27
+ --zv-easing: cubic-bezier(0.2, 0.7, 0.2, 1);
28
+ --zv-delay: 0s;
29
+ }
30
+
31
+ /* ─── Boucles continues ─────────────────────────────────────────────────
32
+ * Les deux seules animations qui ne dépendent pas du défilement. */
33
+
34
+ /* 1 · Flottement des glyphes en scène. Jamais sur du texte. */
35
+ @keyframes zv-drift {
36
+ 0%,
37
+ 100% {
38
+ translate: 0 0;
39
+ }
40
+ 50% {
41
+ translate: 0 -10px;
42
+ }
43
+ }
44
+
45
+ .zv-anim-drift {
46
+ animation: zv-drift 7s ease-in-out infinite;
47
+ animation-delay: var(--zv-delay);
48
+ }
49
+
50
+ /* 2 · Respiration du halo de hero. Le SEUL mouvement autorisé avant le
51
+ * premier défilement. */
52
+ @keyframes zv-breathe {
53
+ 0%,
54
+ 100% {
55
+ scale: 1;
56
+ opacity: 1;
57
+ }
58
+ 50% {
59
+ scale: 1.06;
60
+ opacity: 0.92;
61
+ }
62
+ }
63
+
64
+ .zv-anim-breathe {
65
+ animation: zv-breathe 9s ease-in-out infinite;
66
+ }
67
+
68
+ /* ─── Entrées pilotées par le défilement ────────────────────────────────
69
+ * Hors de ce bloc, aucune de ces classes ne produit d'effet : l'élément
70
+ * reste dans son état final. C'est voulu. */
71
+
72
+ @supports (animation-timeline: view()) {
73
+ /* 3 · Entrée standard de tout contenu texte et panneau.
74
+ * Stagger de 120 ms entre les éléments d'un même groupe. */
75
+ @keyframes zv-rise {
76
+ from {
77
+ opacity: 0;
78
+ transform: translateY(32px);
79
+ }
80
+ }
81
+
82
+ .zv-anim-rise {
83
+ animation: zv-rise var(--zv-easing) both;
84
+ animation-timeline: view();
85
+ animation-range: contain 10% contain 45%;
86
+ }
87
+
88
+ /* 4 · Entrée des glyphes produit dans leur scène : montée et grossissement. */
89
+ @keyframes zv-glyph-in {
90
+ from {
91
+ opacity: 0;
92
+ transform: translateY(40px) scale(0.7);
93
+ }
94
+ }
95
+
96
+ .zv-anim-glyph-in {
97
+ animation: zv-glyph-in var(--zv-easing) both;
98
+ animation-timeline: view();
99
+ animation-range: contain 0% contain 45%;
100
+ }
101
+
102
+ /* 5 · La signature de la page d'accueil : les glyphes convergent vers le
103
+ * Z depuis les quatre coins. La direction se choisit par modificateur —
104
+ * c'est la seule entrée latérale du système, et elle est latérale
105
+ * parce qu'elle converge, pas parce qu'elle glisse. */
106
+ @keyframes zv-converge-tl {
107
+ from {
108
+ opacity: 0;
109
+ transform: translate(-420px, -420px) scale(1.5);
110
+ }
111
+ }
112
+
113
+ @keyframes zv-converge-tr {
114
+ from {
115
+ opacity: 0;
116
+ transform: translate(420px, -420px) scale(1.5);
117
+ }
118
+ }
119
+
120
+ @keyframes zv-converge-bl {
121
+ from {
122
+ opacity: 0;
123
+ transform: translate(-420px, 420px) scale(1.5);
124
+ }
125
+ }
126
+
127
+ @keyframes zv-converge-br {
128
+ from {
129
+ opacity: 0;
130
+ transform: translate(420px, 420px) scale(1.5);
131
+ }
132
+ }
133
+
134
+ .zv-anim-converge {
135
+ animation: zv-converge-tl var(--zv-easing) both;
136
+ animation-timeline: view();
137
+ animation-range: contain 0% contain 55%;
138
+ }
139
+
140
+ .zv-anim-converge--tr {
141
+ animation-name: zv-converge-tr;
142
+ }
143
+
144
+ .zv-anim-converge--bl {
145
+ animation-name: zv-converge-bl;
146
+ }
147
+
148
+ .zv-anim-converge--br {
149
+ animation-name: zv-converge-br;
150
+ }
151
+
152
+ /* 6 · Badges, jetons d'entités, éléments de preuve.
153
+ * En cascade : six vagues espacées de 6 % de défilement. */
154
+ @keyframes zv-pop {
155
+ from {
156
+ opacity: 0;
157
+ transform: scale(0.6);
158
+ }
159
+ }
160
+
161
+ .zv-anim-pop {
162
+ animation: zv-pop var(--zv-easing) both;
163
+ animation-timeline: view();
164
+ animation-range: contain 30% contain 70%;
165
+ }
166
+
167
+ /* Les six vagues. Décaler la plage, et non le délai : un délai en secondes
168
+ * n'a aucun sens sur une timeline de défilement. */
169
+ .zv-anim-pop--2 { animation-range: contain 36% contain 76%; }
170
+ .zv-anim-pop--3 { animation-range: contain 42% contain 82%; }
171
+ .zv-anim-pop--4 { animation-range: contain 48% contain 88%; }
172
+ .zv-anim-pop--5 { animation-range: contain 54% contain 94%; }
173
+ .zv-anim-pop--6 { animation-range: contain 60% contain 100%; }
174
+
175
+ /* 7 · Le filet de spectre se dessine à l'entrée de chaque en-tête de
176
+ * section. On anime bien la largeur, et non une mise à l'échelle :
177
+ * le dégradé doit se révéler, pas s'étirer. */
178
+ @keyframes zv-filet {
179
+ from {
180
+ width: 0;
181
+ }
182
+ }
183
+
184
+ .zv-anim-filet {
185
+ animation: zv-filet var(--zv-easing) both;
186
+ animation-timeline: view();
187
+ animation-range: contain 5% contain 25%;
188
+ }
189
+
190
+ /* 8 · Le point d'étape s'allume à l'entrée de sa scène et s'éteint à la
191
+ * sortie. Un seul actif à la fois. */
192
+ @keyframes zv-dot-active {
193
+ 0%,
194
+ 100% {
195
+ background: var(--accent-line);
196
+ box-shadow: none;
197
+ }
198
+ 35%,
199
+ 70% {
200
+ background: var(--iris);
201
+ box-shadow: 0 0 0 4px rgba(88, 72, 248, 0.18);
202
+ }
203
+ }
204
+
205
+ .zv-anim-dot-active {
206
+ animation: zv-dot-active linear both;
207
+ animation-timeline: view();
208
+ animation-range: cover 0% cover 100%;
209
+ }
210
+
211
+ /* 9 · La barre de progression, pilotée par le défilement global de la
212
+ * page — la seule à lire `scroll(root)` plutôt que sa propre entrée. */
213
+ @keyframes zv-progress {
214
+ from {
215
+ transform: scaleX(0);
216
+ }
217
+ to {
218
+ transform: scaleX(1);
219
+ }
220
+ }
221
+
222
+ .zv-anim-progress {
223
+ transform-origin: 0 50%;
224
+ animation: zv-progress linear both;
225
+ animation-timeline: scroll(root);
226
+ }
227
+ }
228
+
229
+ /* ─── Accessibilité ─────────────────────────────────────────────────────
230
+ * Les neuf classes se neutralisent d'un seul bloc. Une durée courte ne
231
+ * suffit PAS pour une animation pilotée par le défilement — elle ignore la
232
+ * durée : il faut couper la timeline et l'animation elles-mêmes. La page
233
+ * reste complète et lisible, dans son état final. */
234
+
235
+ @media (prefers-reduced-motion: reduce) {
236
+ .zv-anim-drift,
237
+ .zv-anim-breathe,
238
+ .zv-anim-rise,
239
+ .zv-anim-glyph-in,
240
+ .zv-anim-converge,
241
+ .zv-anim-pop,
242
+ .zv-anim-filet,
243
+ .zv-anim-dot-active,
244
+ .zv-anim-progress {
245
+ animation: none !important;
246
+ animation-timeline: none !important;
247
+ }
248
+ }
@@ -0,0 +1,235 @@
1
+ /* Zevra UI — surfaces : halos, sols quadrillés, voiles, bandeau manifeste
2
+ * Dépend de tokens.css. Indépendant des autres modules.
3
+ *
4
+ * C'est ce qui distingue une page Zevra d'une page blanche bien composée.
5
+ * Toutes ces surfaces sont DÉCORATIVES : elles se posent en absolu derrière
6
+ * le contenu, ne captent jamais le pointeur, et n'ont pas à être lues par un
7
+ * lecteur d'écran (les poser en aria-hidden).
8
+ *
9
+ * Elles supposent toutes un parent `position: relative; overflow: hidden`.
10
+ */
11
+
12
+ /* ─── Halo ambiant ──────────────────────────────────────────────────────
13
+ * Le violet et le cyan du spectre, très dilués, derrière un bloc centré.
14
+ * Ce n'est pas un emploi du spectre en aplat : c'est une lumière. */
15
+
16
+ .zv-halo {
17
+ position: absolute;
18
+ left: 50%;
19
+ transform: translateX(-50%);
20
+ border-radius: 50%;
21
+ pointer-events: none;
22
+ }
23
+
24
+ /* Derrière un hero. */
25
+ .zv-halo--hero {
26
+ top: -140px;
27
+ width: 1400px;
28
+ height: 900px;
29
+ background: radial-gradient(
30
+ closest-side,
31
+ rgba(138, 58, 248, 0.2),
32
+ rgba(20, 194, 248, 0.1) 48%,
33
+ rgba(245, 246, 250, 0) 72%
34
+ );
35
+ }
36
+
37
+ /* Derrière un CTA final : plus petit, plus discret. */
38
+ .zv-halo--cta {
39
+ top: 40px;
40
+ width: 900px;
41
+ height: 500px;
42
+ background: radial-gradient(
43
+ closest-side,
44
+ rgba(138, 58, 248, 0.18),
45
+ rgba(20, 194, 248, 0.08) 55%,
46
+ rgba(245, 246, 250, 0) 75%
47
+ );
48
+ }
49
+
50
+ /* Noyau lumineux d'une scène : blanc au centre, pour détacher un objet. */
51
+ .zv-halo--core {
52
+ top: 42%;
53
+ width: 980px;
54
+ height: 560px;
55
+ transform: translate(-50%, -50%);
56
+ background: radial-gradient(
57
+ closest-side,
58
+ rgba(255, 255, 255, 1) 0%,
59
+ rgba(228, 222, 255, 0.85) 35%,
60
+ rgba(138, 58, 248, 0.14) 62%,
61
+ rgba(20, 194, 248, 0.06) 80%,
62
+ rgba(245, 246, 250, 0) 100%
63
+ );
64
+ }
65
+
66
+ /* ─── Sol quadrillé ─────────────────────────────────────────────────────
67
+ * Une grille indigo très pâle, basculée en perspective : le « sol » sur
68
+ * lequel les glyphes produit sont posés. */
69
+
70
+ .zv-grid-floor {
71
+ position: absolute;
72
+ left: 50%;
73
+ bottom: 0;
74
+ width: 1800px;
75
+ height: 560px;
76
+ transform: translateX(-50%) perspective(900px) rotateX(58deg);
77
+ transform-origin: 50% 100%;
78
+ pointer-events: none;
79
+ background-image:
80
+ linear-gradient(rgba(88, 72, 248, 0.13) 1px, transparent 1px),
81
+ linear-gradient(90deg, rgba(88, 72, 248, 0.13) 1px, transparent 1px);
82
+ background-size: 64px 64px;
83
+ }
84
+
85
+ /* Variante à l'échelle d'une carte. */
86
+ .zv-grid-floor--card {
87
+ width: 900px;
88
+ height: 260px;
89
+ transform: translateX(-50%) perspective(600px) rotateX(62deg);
90
+ }
91
+
92
+ /* ─── Voiles de fondu ───────────────────────────────────────────────────
93
+ * Posés PAR-DESSUS un sol ou une scène pour en noyer les bords dans le
94
+ * papier. Sans eux, la grille se coupe net et le truc se voit. */
95
+
96
+ .zv-fade {
97
+ position: absolute;
98
+ inset: 0;
99
+ pointer-events: none;
100
+ background: linear-gradient(
101
+ 180deg,
102
+ var(--paper) 0%,
103
+ rgba(245, 246, 250, 0) 40%,
104
+ rgba(245, 246, 250, 0) 66%,
105
+ var(--paper) 100%
106
+ );
107
+ }
108
+
109
+ /* Variante sur fond blanc (intérieur de carte). */
110
+ .zv-fade--white {
111
+ background: linear-gradient(
112
+ 180deg,
113
+ #fff 0%,
114
+ rgba(255, 255, 255, 0) 35%,
115
+ rgba(255, 255, 255, 0) 70%,
116
+ #fff 100%
117
+ );
118
+ }
119
+
120
+ /* ─── Bandeau manifeste ─────────────────────────────────────────────────
121
+ * La seule surface sombre du système. Le dégradé est celui du logo, en
122
+ * diagonale. On n'y inverse PAS les neutres : les labels passent en indigo
123
+ * clair (--accent-on-dark), les filets en blanc à 18 %. */
124
+
125
+ .zv-band {
126
+ position: relative;
127
+ overflow: hidden;
128
+ background: var(--manifeste);
129
+ color: var(--white);
130
+ padding-top: 112px;
131
+ padding-bottom: 112px;
132
+ }
133
+
134
+ .zv-band__content {
135
+ position: relative;
136
+ z-index: 1;
137
+ }
138
+
139
+ /* Filigrane : le glyphe, blanchi et presque effacé, débordant du cadre. */
140
+ .zv-band__watermark {
141
+ position: absolute;
142
+ right: -40px;
143
+ top: -80px;
144
+ width: 640px;
145
+ height: auto;
146
+ opacity: 0.07;
147
+ pointer-events: none;
148
+ /* Ramène n'importe quel logo couleur en blanc pur. */
149
+ filter: brightness(0) invert(1);
150
+ }
151
+
152
+ /* Lueur bleue au pied du bandeau, qui l'empêche de finir en aplat mort. */
153
+ .zv-band__glow {
154
+ position: absolute;
155
+ left: 50%;
156
+ bottom: -300px;
157
+ width: 900px;
158
+ height: 600px;
159
+ transform: translateX(-50%);
160
+ border-radius: 50%;
161
+ pointer-events: none;
162
+ background: radial-gradient(closest-side, rgba(32, 160, 240, 0.5), rgba(32, 160, 240, 0) 70%);
163
+ }
164
+
165
+ /* Texte courant sur fond sombre : ni blanc pur, ni le gris du mode clair. */
166
+ .zv-band .zv-body,
167
+ .zv-band .zv-rule,
168
+ .zv-text-on-dark {
169
+ color: #c7cde0;
170
+ }
171
+
172
+ /* Panneau navy compact (planches de logos). */
173
+ .zv-panel-navy {
174
+ background: linear-gradient(135deg, #06063a 0%, #0b0b5e 40%, var(--accent-deep) 100%);
175
+ border-radius: var(--r-card);
176
+ padding: 28px;
177
+ color: var(--white);
178
+ }
179
+
180
+ /* ─── Glyphes ───────────────────────────────────────────────────────────
181
+ * Les glyphes produit se posent toujours avec leur ombre portée — c'est
182
+ * elle qui les décolle du papier. Jamais de recoloration ni d'inclinaison. */
183
+
184
+ .zv-glyph {
185
+ filter: var(--shadow-glyph);
186
+ }
187
+
188
+ /* Version appuyée : glyphe présenté seul, en grand. */
189
+ .zv-glyph--deep {
190
+ filter:
191
+ drop-shadow(0 24px 32px rgba(88, 72, 248, 0.28))
192
+ drop-shadow(0 2px 4px rgba(11, 16, 32, 0.08));
193
+ }
194
+
195
+ /* Glyphe en filigrane d'une carte, poussé dans l'angle. */
196
+ .zv-glyph--watermark {
197
+ position: absolute;
198
+ top: 26px;
199
+ right: 24px;
200
+ width: 100px;
201
+ height: auto;
202
+ pointer-events: none;
203
+ }
204
+
205
+ /* ─── Mouvement ────────────────────────────────────────────────────────
206
+ * Les animations vivent dans motion.css, qui porte les neuf mouvements
207
+ * autorisés par la passation. Les surfaces d'ici en sont les cibles
208
+ * habituelles : .zv-anim-breathe sur un halo, .zv-anim-drift sur un glyphe. */
209
+
210
+ /* ─── Mobile ────────────────────────────────────────────────────────────*/
211
+
212
+ @media (max-width: 767px) {
213
+ .zv-halo--hero {
214
+ top: -160px;
215
+ width: 600px;
216
+ height: 520px;
217
+ background: radial-gradient(
218
+ closest-side,
219
+ rgba(91, 75, 240, 0.3),
220
+ rgba(32, 128, 240, 0.16) 45%,
221
+ rgba(245, 246, 250, 0) 72%
222
+ );
223
+ }
224
+
225
+ .zv-band {
226
+ padding-top: 48px;
227
+ padding-bottom: 48px;
228
+ }
229
+
230
+ .zv-band__watermark {
231
+ right: -40px;
232
+ top: -40px;
233
+ width: 280px;
234
+ }
235
+ }
package/src/tokens.css ADDED
@@ -0,0 +1,76 @@
1
+ /* Zevra — Design tokens « Encre & Papier »
2
+ * Source : passation DA du 23/08/2026 (canvas Claude Design « Zevra DA Encre
3
+ * et Papier »). Ce fichier est la SOURCE DE VÉRITÉ des valeurs de marque :
4
+ * toute app doit lire ces variables plutôt que recopier un hexadécimal.
5
+ *
6
+ * ⚠️ Système CLAIR par nature (papier/encre). Les surfaces sombres se font
7
+ * avec --manifeste et --accent-on-dark, pas en inversant les neutres : les
8
+ * contrastes ont été réglés dans ce sens-là et pas dans l'autre.
9
+ */
10
+
11
+ :root {
12
+ /* Neutres (charte Mémoire) */
13
+ --ink: #0b1020;
14
+ --muted: #5a637a;
15
+ --label: #8a93a8;
16
+ --line: #e3e6ef;
17
+ --hairline: #eef1f8;
18
+ --paper: #f5f6fa;
19
+ --panel: #fbfcfe;
20
+ --white: #ffffff;
21
+
22
+ /* Indigo (action, labels, liens) */
23
+ --accent-deep: #1e1b8c;
24
+ --accent: #2a2aa8;
25
+ --accent-line: #c9d6f5;
26
+ --accent-soft: #eaf1ff;
27
+
28
+ /* Spectre produit (prélevé dans les logos officiels).
29
+ * ⚠️ Usage strictement limité : filet de section (56×2), barre de
30
+ * progression, points de méthode. Jamais en fond, jamais en texte. */
31
+ --violet: #8a3af8;
32
+ --iris: #5848f8;
33
+ --bleu: #2f6ef8;
34
+ --cyan: #14c2f8;
35
+ --lavande: #b8a8f8;
36
+ --spectre: linear-gradient(90deg, #8a3af8, #2f6ef8 55%, #14c2f8);
37
+
38
+ /* Fond sombre (bande manifeste) — dégradé type logo */
39
+ --manifeste: linear-gradient(135deg, #06063a 0%, #0b0b5e 28%, #1e1b8c 55%, #3d36d6 80%, #2080f0 100%);
40
+ --accent-on-dark: #a5b4fc;
41
+
42
+ /* États */
43
+ --ok: #1c6b4a;
44
+ --ok-soft: #e6f3ec;
45
+ --ok-line: #bfdccb;
46
+ --warn: #b98a1f;
47
+ --err: #b5202c;
48
+
49
+ /* Typo — Google Fonts : Outfit (display), Public Sans (texte), Space Grotesk (labels) */
50
+ --font-display: "Outfit", "Helvetica Neue", Arial, sans-serif; /* titres 200-400, wordmarks 600 */
51
+ --font-text: "Public Sans", "Helvetica Neue", Arial, sans-serif; /* 400/600 */
52
+ --font-mono: "Space Grotesk", "Menlo", monospace; /* labels caps, 0.18-0.2em */
53
+
54
+ /* Échelle display (letter-spacing -0.02em, graisse 300 + chute en 400 --accent-deep) */
55
+ --h1: 76px;
56
+ --h2: 56px;
57
+ --h3: 38px;
58
+ --card-title: 24px;
59
+ --lead: 18px;
60
+ --body: 15px;
61
+
62
+ /* Rayons */
63
+ --r-btn: 5px;
64
+ --r-pill: 3px;
65
+ --r-panel: 10px;
66
+ --r-card: 16px;
67
+
68
+ /* Ombres */
69
+ --shadow-card: 0 2px 10px rgba(11, 16, 32, 0.05);
70
+ --shadow-panel: 0 24px 60px rgba(30, 27, 140, 0.12);
71
+ --shadow-btn: 0 10px 28px rgba(30, 27, 140, 0.30);
72
+ --shadow-glyph: drop-shadow(0 24px 32px rgba(88, 72, 248, 0.28));
73
+
74
+ /* Signature */
75
+ --spec-filet: 56px; /* filet de spectre 56×2px devant chaque kicker */
76
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Zevra — tokens de la DA « Encre & Papier », en JavaScript.
3
+ *
4
+ * ⚠️ MIROIR de src/tokens.css, pas une seconde source. Le CSS fait foi pour
5
+ * tout ce qui s'affiche ; cet export existe pour les usages où l'on ne peut
6
+ * pas lire une variable CSS : génération d'images, graphes (Recharts, Canvas),
7
+ * couleurs de thème d'un e-mail, scripts de build.
8
+ *
9
+ * Toute modification ici DOIT être reportée dans src/tokens.css, et
10
+ * inversement. Le test tokens.test.js vérifie que les deux ne divergent pas.
11
+ */
12
+
13
+ export const couleurs = {
14
+ // Neutres
15
+ ink: '#0b1020',
16
+ muted: '#5a637a',
17
+ label: '#8a93a8',
18
+ line: '#e3e6ef',
19
+ hairline: '#eef1f8',
20
+ paper: '#f5f6fa',
21
+ panel: '#fbfcfe',
22
+ white: '#ffffff',
23
+
24
+ // Indigo — l'action
25
+ accentDeep: '#1e1b8c',
26
+ accent: '#2a2aa8',
27
+ accentLine: '#c9d6f5',
28
+ accentSoft: '#eaf1ff',
29
+
30
+ // Spectre produit — usage restreint (filets, progression, points)
31
+ violet: '#8a3af8',
32
+ iris: '#5848f8',
33
+ bleu: '#2f6ef8',
34
+ cyan: '#14c2f8',
35
+ lavande: '#b8a8f8',
36
+
37
+ // Sur fond sombre
38
+ accentOnDark: '#a5b4fc',
39
+
40
+ // États
41
+ ok: '#1c6b4a',
42
+ okSoft: '#e6f3ec',
43
+ okLine: '#bfdccb',
44
+ warn: '#b98a1f',
45
+ err: '#b5202c',
46
+ }
47
+
48
+ /** Dégradés. Ce sont des valeurs CSS complètes, pas des couleurs. */
49
+ export const degrades = {
50
+ spectre: 'linear-gradient(90deg, #8a3af8, #2f6ef8 55%, #14c2f8)',
51
+ manifeste:
52
+ 'linear-gradient(135deg, #06063a 0%, #0b0b5e 28%, #1e1b8c 55%, #3d36d6 80%, #2080f0 100%)',
53
+ }
54
+
55
+ /**
56
+ * Palette ordonnée pour les visualisations de données. Dérivée du spectre :
57
+ * un graphe reste ainsi dans la marque sans emprunter à l'indigo, qui porte
58
+ * l'action et ne doit pas se confondre avec une série de données.
59
+ */
60
+ export const serieGraphique = ['#5848f8', '#2f6ef8', '#14c2f8', '#8a3af8', '#b8a8f8']
61
+
62
+ export const polices = {
63
+ display: '"Outfit", "Helvetica Neue", Arial, sans-serif',
64
+ texte: '"Public Sans", "Helvetica Neue", Arial, sans-serif',
65
+ mono: '"Space Grotesk", "Menlo", monospace',
66
+ }
67
+
68
+ export const tailles = {
69
+ h1: '76px',
70
+ h2: '56px',
71
+ h3: '38px',
72
+ cardTitle: '24px',
73
+ lead: '18px',
74
+ body: '15px',
75
+ }
76
+
77
+ export const rayons = {
78
+ btn: '5px',
79
+ pill: '3px',
80
+ panel: '10px',
81
+ card: '16px',
82
+ }
83
+
84
+ export const ombres = {
85
+ card: '0 2px 10px rgba(11, 16, 32, 0.05)',
86
+ panel: '0 24px 60px rgba(30, 27, 140, 0.12)',
87
+ btn: '0 10px 28px rgba(30, 27, 140, 0.30)',
88
+ glyph: 'drop-shadow(0 24px 32px rgba(88, 72, 248, 0.28))',
89
+ }
90
+
91
+ export default { couleurs, degrades, serieGraphique, polices, tailles, rayons, ombres }