odoro 0.3.4 → 1.0.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.
Files changed (41) hide show
  1. package/dist/cli.js +3 -3
  2. package/dist/{commands-PT3NS65O.js → commands-AHMXBWQQ.js} +25 -5
  3. package/dist/{create-IOFAPDBO.js → create-LTT3SAWT.js} +11 -7
  4. package/dist/{package-Q2FV4K3H.js → package-LK46RV2I.js} +1 -1
  5. package/package.json +1 -1
  6. package/templates/react-ts/_variantes/avec-moteur/src/sections/Fond.tsx +118 -0
  7. package/templates/react-ts/_variantes/sans-libs/src/App.tsx +22 -51
  8. package/templates/react-ts/_variantes/sans-libs/src/composants/Marque.tsx +37 -0
  9. package/templates/react-ts/_variantes/sans-libs/src/sections/Cloture.tsx +60 -0
  10. package/templates/react-ts/_variantes/sans-libs/src/sections/Fond.tsx +23 -0
  11. package/templates/react-ts/_variantes/sans-libs/src/sections/Hero.tsx +92 -0
  12. package/templates/react-ts/_variantes/sans-libs/src/sections/Piliers.tsx +44 -0
  13. package/templates/react-ts/_variantes/sans-libs/src/styles.css +322 -40
  14. package/templates/react-ts/_variantes/sans-routeur/src/App.tsx +35 -77
  15. package/templates/react-ts/src/App.tsx +56 -29
  16. package/templates/react-ts/src/composants/Marque.tsx +50 -0
  17. package/templates/react-ts/src/routes/About.tsx +38 -14
  18. package/templates/react-ts/src/routes/Home.tsx +21 -73
  19. package/templates/react-ts/src/sections/Cloture.tsx +69 -0
  20. package/templates/react-ts/src/sections/Fond.tsx +42 -0
  21. package/templates/react-ts/src/sections/Hero.tsx +119 -0
  22. package/templates/react-ts/src/sections/Piliers.tsx +68 -0
  23. package/templates/react-ts/src/styles.css +4 -5
  24. package/templates/react-ts-server/_variantes/avec-moteur/client/src/sections/Fond.tsx +118 -0
  25. package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +22 -51
  26. package/templates/react-ts-server/_variantes/sans-libs/client/src/composants/Marque.tsx +37 -0
  27. package/templates/react-ts-server/_variantes/sans-libs/client/src/sections/Cloture.tsx +60 -0
  28. package/templates/react-ts-server/_variantes/sans-libs/client/src/sections/Fond.tsx +23 -0
  29. package/templates/react-ts-server/_variantes/sans-libs/client/src/sections/Hero.tsx +92 -0
  30. package/templates/react-ts-server/_variantes/sans-libs/client/src/sections/Piliers.tsx +44 -0
  31. package/templates/react-ts-server/_variantes/sans-libs/client/src/styles.css +322 -40
  32. package/templates/react-ts-server/_variantes/sans-routeur/client/src/App.tsx +35 -77
  33. package/templates/react-ts-server/client/src/App.tsx +56 -29
  34. package/templates/react-ts-server/client/src/composants/Marque.tsx +50 -0
  35. package/templates/react-ts-server/client/src/routes/About.tsx +38 -14
  36. package/templates/react-ts-server/client/src/routes/Home.tsx +18 -138
  37. package/templates/react-ts-server/client/src/sections/Cloture.tsx +69 -0
  38. package/templates/react-ts-server/client/src/sections/Fond.tsx +42 -0
  39. package/templates/react-ts-server/client/src/sections/Hero.tsx +119 -0
  40. package/templates/react-ts-server/client/src/sections/Piliers.tsx +68 -0
  41. package/templates/react-ts-server/client/src/styles.css +4 -5
@@ -1,18 +1,23 @@
1
1
  /* Styles de l'application.
2
2
 
3
3
  Les bibliotheques Odoro n'ont pas ete retenues a la creation : il n'y a donc
4
- ni jetons ni classes utilitaires, et cette feuille porte tout. C'est du CSS
5
- ordinaire, a remplacer par ce que vous voulez.
4
+ ni jetons ni classes utilitaires, et cette feuille porte tout. Le dessin est
5
+ celui de la version complete meme typographie, memes cartes, meme teinte
6
+ de marque — en CSS ordinaire.
7
+
8
+ Une seule variable commande la couleur : `--marque`. La changer repeint le
9
+ signe, les accents et les boutons d'un seul coup.
6
10
 
7
11
  Pour passer aux jetons plus tard : `npm i @odoro-cli/libs`, puis importer
8
12
  `@odoro-cli/libs/styles.css` dans `main.tsx` avant cette feuille. */
9
13
 
10
14
  :root {
15
+ --marque: #f97316;
11
16
  --fond: #ffffff;
12
17
  --encre: #18181b;
13
18
  --doux: #71717a;
14
19
  --filet: #e4e4e7;
15
- --accent: #4f46e5;
20
+ --surface: #fafafa;
16
21
  color-scheme: light dark;
17
22
  }
18
23
 
@@ -25,7 +30,7 @@
25
30
  --encre: #fafafa;
26
31
  --doux: #a1a1aa;
27
32
  --filet: #27272a;
28
- --accent: #818cf8;
33
+ --surface: #18181b;
29
34
  }
30
35
  }
31
36
 
@@ -37,102 +42,379 @@ body {
37
42
  margin: 0;
38
43
  background: var(--fond);
39
44
  color: var(--encre);
40
- font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
45
+ font-family: system-ui, -apple-system, Segoe UI, sans-serif;
41
46
  line-height: 1.6;
42
47
  }
43
48
 
49
+ code,
50
+ pre {
51
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
52
+ }
53
+
44
54
  .app-shell {
45
55
  min-height: 100dvh;
46
56
  display: grid;
47
57
  grid-template-rows: auto 1fr auto;
48
58
  }
49
59
 
50
- .nav,
51
- .pied {
52
- padding: 1rem 1.5rem;
60
+ /* ----- Marque ------------------------------------------------------------- */
61
+
62
+ .marque {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ gap: 0.5rem;
66
+ font-weight: 600;
67
+ letter-spacing: -0.01em;
68
+ }
69
+
70
+ .marque-signe {
71
+ color: var(--marque);
72
+ display: inline-flex;
73
+ }
74
+
75
+ .signe {
76
+ width: 1.15em;
77
+ height: 1.15em;
53
78
  }
54
79
 
80
+ /* ----- Coquille ----------------------------------------------------------- */
81
+
55
82
  .nav {
83
+ position: sticky;
84
+ top: 0;
85
+ z-index: 20;
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 1.5rem;
89
+ padding: 1rem 1.5rem;
56
90
  border-bottom: 1px solid var(--filet);
91
+ background: color-mix(in oklab, var(--fond) 75%, transparent);
92
+ backdrop-filter: blur(10px);
93
+ }
94
+
95
+ .nav-lien,
96
+ .lien-discret {
97
+ margin-left: auto;
98
+ color: var(--doux);
99
+ font-size: 0.875rem;
100
+ text-decoration: none;
101
+ }
102
+
103
+ .nav-lien:hover,
104
+ .lien-discret:hover {
105
+ color: var(--marque);
106
+ }
107
+
108
+ .principal {
109
+ position: relative;
57
110
  }
58
111
 
59
112
  .pied {
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: space-between;
116
+ gap: 1rem;
117
+ padding: 2rem 1.5rem;
60
118
  border-top: 1px solid var(--filet);
61
119
  color: var(--doux);
62
120
  font-size: 0.875rem;
63
121
  }
64
122
 
65
- .marque {
66
- font-weight: 600;
67
- color: var(--accent);
123
+ /* ----- Fond decoratif ----------------------------------------------------- */
124
+
125
+ .fond {
126
+ position: absolute;
127
+ inset-inline: 0;
128
+ top: 0;
129
+ height: 42rem;
130
+ overflow: hidden;
131
+ pointer-events: none;
68
132
  }
69
133
 
70
- .contenu {
134
+ .fond-nappe {
135
+ position: absolute;
136
+ border-radius: 9999px;
137
+ filter: blur(64px);
138
+ }
139
+
140
+ .fond-nappe-haute {
141
+ left: 50%;
142
+ top: -18rem;
143
+ width: 46rem;
144
+ height: 46rem;
145
+ transform: translateX(-50%);
146
+ opacity: 0.3;
147
+ background: radial-gradient(closest-side, var(--marque), transparent);
148
+ }
149
+
150
+ .fond-nappe-droite {
151
+ right: -10rem;
152
+ top: 6rem;
153
+ width: 30rem;
154
+ height: 30rem;
155
+ opacity: 0.18;
156
+ background: radial-gradient(closest-side, var(--marque), transparent);
157
+ }
158
+
159
+ /* ----- Mise en page ------------------------------------------------------- */
160
+
161
+ .contenu,
162
+ .contenu-etroit {
71
163
  width: 100%;
72
- max-width: 48rem;
73
164
  margin: 0 auto;
74
- padding: 3rem 1.5rem;
165
+ position: relative;
75
166
  }
76
167
 
77
- h1 {
78
- font-size: 2.25rem;
79
- letter-spacing: -0.02em;
80
- margin: 0;
168
+ .contenu {
169
+ max-width: 64rem;
81
170
  }
82
171
 
83
- .chapeau {
84
- color: var(--doux);
172
+ .contenu-etroit {
173
+ max-width: 48rem;
174
+ text-align: center;
175
+ }
176
+
177
+ .hero {
178
+ padding: 6rem 1.5rem 5rem;
179
+ }
180
+
181
+ .section {
182
+ padding: 5rem 1.5rem;
183
+ border-top: 1px solid var(--filet);
184
+ }
185
+
186
+ .section-centre {
187
+ padding: 6rem 1.5rem;
188
+ }
189
+
190
+ /* ----- Hero --------------------------------------------------------------- */
191
+
192
+ .pastille-marque {
193
+ display: inline-flex;
194
+ align-items: center;
195
+ gap: 0.5rem;
196
+ padding: 0.25rem 0.75rem;
197
+ border-radius: 9999px;
198
+ border: 1px solid color-mix(in oklab, var(--marque) 30%, transparent);
199
+ background: color-mix(in oklab, var(--marque) 10%, transparent);
200
+ color: var(--marque);
201
+ font-size: 0.75rem;
202
+ font-weight: 500;
203
+ }
204
+
205
+ .hero-titre {
206
+ margin: 1.5rem 0 0;
207
+ max-width: 46rem;
208
+ font-size: clamp(2.5rem, 6vw, 3.75rem);
209
+ font-weight: 700;
210
+ letter-spacing: -0.03em;
211
+ line-height: 1.05;
212
+ }
213
+
214
+ .accent {
215
+ color: var(--marque);
216
+ }
217
+
218
+ .hero-chapeau {
219
+ margin: 1.25rem 0 0;
220
+ max-width: 36rem;
85
221
  font-size: 1.125rem;
86
- max-width: 60ch;
222
+ color: var(--doux);
223
+ }
224
+
225
+ .hero-actions {
226
+ display: flex;
227
+ flex-wrap: wrap;
228
+ gap: 0.75rem;
229
+ margin-top: 2rem;
230
+ }
231
+
232
+ .faits {
233
+ display: flex;
234
+ flex-wrap: wrap;
235
+ gap: 0.5rem;
236
+ margin-top: 1.5rem;
237
+ }
238
+
239
+ .fait {
240
+ padding: 0.25rem 0.75rem;
241
+ border-radius: 9999px;
242
+ border: 1px solid var(--filet);
243
+ color: var(--doux);
244
+ font-size: 0.75rem;
245
+ }
246
+
247
+ .hero-fenetre {
248
+ margin-top: 3.5rem;
249
+ }
250
+
251
+ /* ----- Fenetre du produit ------------------------------------------------- */
252
+
253
+ .fenetre {
254
+ overflow: hidden;
255
+ border-radius: 1rem;
256
+ border: 1px solid var(--filet);
257
+ background: var(--surface);
258
+ box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
259
+ }
260
+
261
+ .fenetre-barre {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 0.5rem;
265
+ padding: 0.625rem 1rem;
266
+ border-bottom: 1px solid var(--filet);
267
+ }
268
+
269
+ .fenetre-pastilles {
270
+ display: flex;
271
+ gap: 0.375rem;
272
+ }
273
+
274
+ .fenetre-pastilles span {
275
+ width: 0.625rem;
276
+ height: 0.625rem;
277
+ border-radius: 9999px;
278
+ background: var(--filet);
87
279
  }
88
280
 
89
- code {
281
+ .fenetre-chemin {
282
+ margin-left: 0.5rem;
283
+ color: var(--doux);
90
284
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
91
- font-size: 0.9em;
285
+ font-size: 0.75rem;
286
+ }
287
+
288
+ .fenetre-code {
289
+ margin: 0;
290
+ padding: 1.25rem;
291
+ overflow-x: auto;
292
+ font-size: 0.75rem;
293
+ line-height: 1.7;
294
+ color: var(--doux);
295
+ }
296
+
297
+ /* ----- Cartes ------------------------------------------------------------- */
298
+
299
+ .sur-titre {
300
+ margin: 0;
301
+ font-size: 0.8125rem;
302
+ font-weight: 600;
303
+ letter-spacing: 0.12em;
304
+ text-transform: uppercase;
305
+ color: var(--doux);
92
306
  }
93
307
 
94
308
  .grille {
95
309
  display: grid;
96
310
  gap: 1rem;
97
- grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
98
- margin: 3rem 0;
311
+ grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
312
+ margin-top: 2rem;
99
313
  }
100
314
 
101
315
  .carte {
102
316
  border: 1px solid var(--filet);
103
- border-radius: 0.5rem;
104
- padding: 1.25rem;
317
+ border-radius: 0.75rem;
318
+ background: var(--surface);
319
+ padding: 1.5rem;
105
320
  }
106
321
 
107
- .carte h2 {
108
- font-size: 1rem;
109
- margin: 0;
322
+ .carte-numero {
323
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
324
+ font-size: 0.75rem;
325
+ color: var(--marque);
326
+ }
327
+
328
+ .carte h3 {
329
+ margin: 0.75rem 0 0;
330
+ font-size: 1.125rem;
331
+ letter-spacing: -0.01em;
110
332
  }
111
333
 
112
334
  .carte p {
113
- color: var(--doux);
114
- font-size: 0.875rem;
115
335
  margin: 0.5rem 0 0;
336
+ font-size: 0.875rem;
337
+ color: var(--doux);
338
+ }
339
+
340
+ /* ----- Cloture ------------------------------------------------------------ */
341
+
342
+ .cloture-titre {
343
+ margin: 0;
344
+ font-size: clamp(1.75rem, 4vw, 2.25rem);
345
+ font-weight: 700;
346
+ letter-spacing: -0.02em;
347
+ }
348
+
349
+ .cloture-chapeau {
350
+ margin: 1rem auto 0;
351
+ max-width: 40rem;
352
+ color: var(--doux);
116
353
  }
117
354
 
118
- .ligne {
355
+ .commande {
119
356
  display: flex;
120
357
  align-items: center;
121
- gap: 1rem;
358
+ gap: 0.5rem;
359
+ max-width: 28rem;
360
+ margin: 2rem auto 0;
361
+ padding: 0.5rem 0.5rem 0.5rem 1rem;
362
+ border: 1px solid var(--filet);
363
+ border-radius: 0.75rem;
364
+ background: var(--surface);
365
+ }
366
+
367
+ .commande code {
368
+ flex: 1;
369
+ text-align: left;
370
+ font-size: 0.875rem;
371
+ }
372
+
373
+ .lien-discret {
374
+ display: inline-block;
375
+ margin: 1.5rem auto 0;
122
376
  }
123
377
 
378
+ /* ----- Boutons ------------------------------------------------------------ */
379
+
124
380
  .bouton {
125
- background: var(--accent);
126
- color: var(--fond);
381
+ display: inline-flex;
382
+ align-items: center;
383
+ justify-content: center;
384
+ height: 2.5rem;
385
+ padding: 0 1rem;
127
386
  border: 0;
128
387
  border-radius: 0.5rem;
129
- padding: 0.5rem 1rem;
130
388
  font: inherit;
389
+ font-size: 0.9375rem;
131
390
  font-weight: 500;
391
+ text-decoration: none;
132
392
  cursor: pointer;
133
393
  }
134
394
 
135
- .compteur {
136
- font-size: 1.125rem;
137
- font-variant-numeric: tabular-nums;
395
+ .bouton-primaire {
396
+ background: var(--marque);
397
+ color: #ffffff;
398
+ }
399
+
400
+ .bouton-primaire:hover {
401
+ background: color-mix(in oklab, var(--marque) 85%, black);
402
+ }
403
+
404
+ .bouton-secondaire {
405
+ background: var(--surface);
406
+ color: var(--encre);
407
+ border: 1px solid var(--filet);
408
+ }
409
+
410
+ .bouton-fantome {
411
+ height: 2rem;
412
+ padding: 0 0.75rem;
413
+ background: transparent;
414
+ color: var(--encre);
415
+ font-size: 0.875rem;
416
+ }
417
+
418
+ .bouton-fantome:hover {
419
+ background: var(--filet);
138
420
  }
@@ -1,92 +1,50 @@
1
- import { useState } from 'react'
1
+ import type { ReactElement } from 'react'
2
2
 
3
- import { Reveal, Stagger, useAnimate } from '@odoro-cli/libs/motion'
4
- import { Button } from '@odoro-cli/libs/ui'
5
-
6
- /** Trois arguments mis en avant. */
7
- const FEATURES = [
8
- {
9
- title: 'Animations',
10
- body: "Une couche mince sur le moteur du navigateur. Les animations reduites sont respectees d'office.",
11
- },
12
- {
13
- title: 'Styles',
14
- body: 'Des tokens en source de verite, une feuille statique, aucun scan a l execution.',
15
- },
16
- {
17
- title: 'Interface',
18
- body: 'Des composants qui lisent les memes tokens que votre code : les rethemer suffit.',
19
- },
20
- ]
3
+ import { Marque } from '@/composants/Marque'
4
+ import { Cloture } from '@/sections/Cloture'
5
+ import { Fond } from '@/sections/Fond'
6
+ import { Hero } from '@/sections/Hero'
7
+ import { Piliers } from '@/sections/Piliers'
21
8
 
22
9
  /**
23
10
  * Racine de l'application.
24
11
  *
25
- * Une seule page : le routeur n'a pas ete retenu a la creation. Pour en
26
- * ajouter un plus tard, `@odoro-cli/libs/router` est deja installeil vient
27
- * avec les bibliotheques.
12
+ * Une seule page : le routeur n'a pas ete retenu a la creation. Les sections
13
+ * sont les memes que dans la version routeememe dessin, memes fichiers — et
14
+ * seul leur assemblage change.
15
+ *
16
+ * Pour ajouter un routeur plus tard, `@odoro-cli/libs/router` est deja
17
+ * installe : il vient avec les bibliotheques.
28
18
  */
29
- export function App() {
30
- const [ref, controls] = useAnimate<HTMLSpanElement>()
31
- const [count, setCount] = useState(0)
32
-
19
+ export function App(): ReactElement {
33
20
  return (
34
21
  <div className="app-shell">
35
- <nav className="o-flex o-items-center o-gap-4 o-px-6 o-py-4 o-border-b o-border-zinc-200 dark:o-border-zinc-800">
36
- <span className="o-font-semibold o-text-brand-600 dark:o-text-brand-400">
37
- Odoro
38
- </span>
39
- </nav>
40
-
41
- <main className="o-mx-auto o-w-full o-max-w-3xl o-px-6 o-py-12 o-flex o-flex-col o-gap-12">
42
- <Reveal>
43
- <h1 className="o-text-4xl o-font-bold o-tracking-tight">
44
- Un point de depart maitrise.
45
- </h1>
46
- <p className="o-mt-4 o-text-lg o-text-zinc-500 dark:o-text-zinc-400 o-max-w-prose">
47
- Ce projet a ete genere par <code className="o-font-mono">odoro create</code>.
48
- Le moteur d animation et le systeme de style viennent de la meme librairie.
49
- </p>
50
- </Reveal>
51
-
52
- <Stagger step={80} className="o-grid o-grid-cols-1 md:o-grid-cols-3 o-gap-4">
53
- {FEATURES.map((feature) => (
54
- <article
55
- key={feature.title}
56
- className="o-rounded-lg o-border-w-1 o-border-zinc-200 dark:o-border-zinc-800 o-bg-white dark:o-bg-zinc-900 o-p-5"
57
- >
58
- <h2 className="o-font-semibold">{feature.title}</h2>
59
- <p className="o-mt-2 o-text-sm o-text-zinc-500 dark:o-text-zinc-400">
60
- {feature.body}
61
- </p>
62
- </article>
63
- ))}
64
- </Stagger>
65
-
66
- <div className="o-flex o-items-center o-gap-4">
67
- <Button
68
- onClick={() => {
69
- setCount((value) => value + 1)
70
- void controls.play(
71
- [
72
- { transform: 'translateY(0)' },
73
- { transform: 'translateY(-0.4rem)' },
74
- { transform: 'translateY(0)' },
75
- ],
76
- { duration: 'fast', easing: 'emphasized' },
77
- )
78
- }}
22
+ <nav className="o-sticky o-top-0 o-z-20 o-border-b o-border-zinc-200/70 dark:o-border-zinc-800/70 o-bg-white/70 dark:o-bg-zinc-950/70 o-backdrop-blur">
23
+ <div className="o-mx-auto o-flex o-w-full o-max-w-5xl o-items-center o-px-6 o-py-4">
24
+ <Marque />
25
+ <a
26
+ href="https://odoro.dev"
27
+ target="_blank"
28
+ rel="noreferrer"
29
+ className="o-ml-auto o-text-sm o-text-zinc-500 dark:o-text-zinc-400 o-no-underline hover:o-text-brand-500"
79
30
  >
80
- Compter
81
- </Button>
82
- <span ref={ref} className="o-inline-block o-text-lg o-tabular-nums">
83
- {count}
84
- </span>
31
+ odoro.dev
32
+ </a>
85
33
  </div>
34
+ </nav>
35
+
36
+ <main className="o-relative">
37
+ <Fond />
38
+ <Hero />
39
+ <Piliers />
40
+ <Cloture />
86
41
  </main>
87
42
 
88
- <footer className="o-px-6 o-py-6 o-text-sm o-text-zinc-500 dark:o-text-zinc-400 o-border-t o-border-zinc-200 dark:o-border-zinc-800">
89
- Construit avec Odoro.
43
+ <footer className="o-border-t o-border-zinc-200 dark:o-border-zinc-800 o-px-6 o-py-8">
44
+ <div className="o-mx-auto o-flex o-w-full o-max-w-5xl o-items-center o-justify-between o-gap-4 o-text-sm o-text-zinc-500 dark:o-text-zinc-400">
45
+ <Marque />
46
+ <span>Construit avec Odoro.</span>
47
+ </div>
90
48
  </footer>
91
49
  </div>
92
50
  )
@@ -1,57 +1,84 @@
1
1
  import { Link, Outlet, Route, Router, Routes, useLocation } from '@odoro-cli/libs/router'
2
+ import type { ReactElement } from 'react'
2
3
 
4
+ import { Marque } from '@/composants/Marque'
3
5
  import { About } from '@/routes/About'
4
6
  import { Home } from '@/routes/Home'
5
7
  import { NotFound } from '@/routes/NotFound'
6
8
 
7
- /** Barre de navigation, avec mise en valeur de la route courante. */
8
- function Nav() {
9
- const { pathname } = useLocation()
9
+ /** Les entrees de la navigation. */
10
+ const LIENS = [
11
+ { to: '/', label: 'Accueil' },
12
+ { to: '/a-propos', label: 'A propos' },
13
+ ]
10
14
 
11
- const links = [
12
- { to: '/', label: 'Accueil' },
13
- { to: '/a-propos', label: 'A propos' },
14
- ]
15
+ /**
16
+ * La barre de navigation.
17
+ *
18
+ * Elle flotte sur le fond plutot que de le couper : un filet clair et un flou
19
+ * d'arriere-plan suffisent a la detacher, et la nappe de couleur continue de
20
+ * passer dessous.
21
+ */
22
+ function Nav(): ReactElement {
23
+ const { pathname } = useLocation()
15
24
 
16
25
  return (
17
- <nav className="o-flex o-items-center o-gap-4 o-px-6 o-py-4 o-border-b o-border-zinc-200 dark:o-border-zinc-800">
18
- <span className="o-font-semibold o-text-brand-600 dark:o-text-brand-400">
19
- Odoro
20
- </span>
21
- {links.map((link) => (
22
- <Link
23
- key={link.to}
24
- to={link.to}
25
- className={
26
- pathname === link.to
27
- ? 'o-text-zinc-900 dark:o-text-zinc-50 o-font-medium o-no-underline'
28
- : 'o-text-zinc-500 dark:o-text-zinc-400 o-no-underline hover:o-text-zinc-900 dark:hover:o-text-zinc-50 o-transition'
29
- }
30
- >
31
- {link.label}
26
+ <nav className="o-sticky o-top-0 o-z-20 o-border-b o-border-zinc-200/70 dark:o-border-zinc-800/70 o-bg-white/70 dark:o-bg-zinc-950/70 o-backdrop-blur">
27
+ <div className="o-mx-auto o-flex o-w-full o-max-w-5xl o-items-center o-gap-6 o-px-6 o-py-4">
28
+ <Link to="/" className="o-no-underline o-text-zinc-900 dark:o-text-zinc-50">
29
+ <Marque />
32
30
  </Link>
33
- ))}
31
+
32
+ <div className="o-flex o-items-center o-gap-5 o-text-sm">
33
+ {LIENS.map((lien) => (
34
+ <Link
35
+ key={lien.to}
36
+ to={lien.to}
37
+ className={
38
+ pathname === lien.to
39
+ ? 'o-font-medium o-text-zinc-900 dark:o-text-zinc-50 o-no-underline'
40
+ : 'o-text-zinc-500 dark:o-text-zinc-400 o-no-underline hover:o-text-zinc-900 dark:hover:o-text-zinc-50 o-transition'
41
+ }
42
+ >
43
+ {lien.label}
44
+ </Link>
45
+ ))}
46
+ </div>
47
+
48
+ <a
49
+ href="https://odoro.dev"
50
+ target="_blank"
51
+ rel="noreferrer"
52
+ className="o-ml-auto o-text-sm o-text-zinc-500 dark:o-text-zinc-400 o-no-underline hover:o-text-brand-500"
53
+ >
54
+ odoro.dev
55
+ </a>
56
+ </div>
34
57
  </nav>
35
58
  )
36
59
  }
37
60
 
38
- /** Enveloppe commune a toutes les pages. */
39
- function Layout() {
61
+ /** L'enveloppe commune a toutes les pages. */
62
+ function Layout(): ReactElement {
40
63
  return (
41
64
  <div className="app-shell">
42
65
  <Nav />
43
- <main className="o-view-transition-page o-mx-auto o-w-full o-max-w-3xl o-px-6 o-py-12">
66
+ {/* `relative` porte le fond decoratif, qui se place en absolu dedans. */}
67
+ <main className="o-relative o-view-transition-page">
44
68
  <Outlet />
45
69
  </main>
46
- <footer className="o-px-6 o-py-6 o-text-sm o-text-zinc-500 dark:o-text-zinc-400 o-border-t o-border-zinc-200 dark:o-border-zinc-800">
47
- Construit avec Odoro.
70
+ <footer className="o-border-t o-border-zinc-200 dark:o-border-zinc-800 o-px-6 o-py-8">
71
+ <div className="o-mx-auto o-flex o-w-full o-max-w-5xl o-items-center o-justify-between o-gap-4 o-text-sm o-text-zinc-500 dark:o-text-zinc-400">
72
+ <Marque />
73
+ <span>Construit avec Odoro.</span>
74
+ </div>
48
75
  </footer>
49
76
  </div>
50
77
  )
51
78
  }
52
79
 
53
80
  /** Racine de l'application. */
54
- export function App() {
81
+ export function App(): ReactElement {
55
82
  return (
56
83
  <Router>
57
84
  <Routes>