create-swift-rust 0.3.1 → 0.7.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 (65) hide show
  1. package/dist/index.js +87 -881
  2. package/dist/index.js.map +1 -1
  3. package/package.json +8 -6
  4. package/templates/default/README.md +29 -0
  5. package/templates/default/_env.example +5 -0
  6. package/templates/default/_env.local +5 -0
  7. package/templates/default/_gitignore +14 -0
  8. package/templates/default/biome.json +12 -0
  9. package/templates/default/globals.d.ts +19 -0
  10. package/templates/default/package.json +30 -0
  11. package/templates/default/postcss.config.mjs +7 -0
  12. package/templates/default/public/blog/introducing-swift-rust.svg +12 -0
  13. package/templates/default/public/og-image.svg +25 -0
  14. package/templates/default/public/samples/axelborg-towers-modern-architecture.jpg +0 -0
  15. package/templates/default/public/samples/brown-high-skyscrapers.jpg +0 -0
  16. package/templates/default/public/samples/close-up-portrait-gorgeous-young-woman.jpg +0 -0
  17. package/templates/default/public/samples/full-shot-woman-taking-selfie.jpg +0 -0
  18. package/templates/default/public/samples/happy-black-parents-with-kids-making-video-call-smart-phone-home.jpg +0 -0
  19. package/templates/default/public/samples/metaverse-concept-collage-design.jpg +0 -0
  20. package/templates/default/src/app/about/layout.tsx +5 -0
  21. package/templates/default/src/app/about/page.tsx +74 -0
  22. package/templates/default/src/app/about/team/page.tsx +88 -0
  23. package/templates/default/src/app/api/contact/route.ts +15 -0
  24. package/templates/default/src/app/api/health/route.ts +7 -0
  25. package/templates/default/src/app/api/posts/[id]/route.ts +33 -0
  26. package/templates/default/src/app/api/posts/route.ts +29 -0
  27. package/templates/default/src/app/api/search/route.ts +15 -0
  28. package/templates/default/src/app/api/stats/route.ts +16 -0
  29. package/templates/default/src/app/blog/[slug]/page.tsx +116 -0
  30. package/templates/default/src/app/blog/page.tsx +101 -0
  31. package/templates/default/src/app/blog/tag/[tag]/page.tsx +82 -0
  32. package/templates/default/src/app/contact/page.tsx +131 -0
  33. package/templates/default/src/app/dashboard/analytics/page.tsx +103 -0
  34. package/templates/default/src/app/dashboard/layout.tsx +11 -0
  35. package/templates/default/src/app/dashboard/loading.tsx +18 -0
  36. package/templates/default/src/app/dashboard/page.tsx +181 -0
  37. package/templates/default/src/app/dashboard/posts/[id]/not-found.tsx +23 -0
  38. package/templates/default/src/app/dashboard/posts/[id]/page.tsx +87 -0
  39. package/templates/default/src/app/dashboard/posts/new/page.tsx +103 -0
  40. package/templates/default/src/app/dashboard/posts/page.tsx +81 -0
  41. package/templates/default/src/app/dashboard/settings/page.tsx +119 -0
  42. package/templates/default/src/app/error.tsx +42 -0
  43. package/templates/default/src/app/favicon.ico +0 -0
  44. package/templates/default/src/app/favicon.svg +8 -0
  45. package/templates/default/src/app/fonts/page.tsx +610 -0
  46. package/templates/default/src/app/globals.css +238 -0
  47. package/templates/default/src/app/images/page.tsx +56 -0
  48. package/templates/default/src/app/layout.tsx +44 -0
  49. package/templates/default/src/app/loading.tsx +13 -0
  50. package/templates/default/src/app/not-found.tsx +25 -0
  51. package/templates/default/src/app/page.tsx +410 -0
  52. package/templates/default/src/app/videos/page.tsx +182 -0
  53. package/templates/default/src/components/font-studio.tsx +378 -0
  54. package/templates/default/src/components/footer.tsx +75 -0
  55. package/templates/default/src/components/logo.tsx +26 -0
  56. package/templates/default/src/components/nav.tsx +102 -0
  57. package/templates/default/src/components/sidebar.tsx +109 -0
  58. package/templates/default/src/lib/blur.ts +5 -0
  59. package/templates/default/src/lib/format.ts +31 -0
  60. package/templates/default/src/lib/posts.ts +132 -0
  61. package/templates/default/src/lib/site.config.ts +39 -0
  62. package/templates/default/src/lib/stats.ts +25 -0
  63. package/templates/default/swift-rust.config.json +8 -0
  64. package/templates/default/tsconfig.json +29 -0
  65. package/templates/default/vercel.json +23 -0
@@ -0,0 +1,610 @@
1
+ import { FontStudio } from "@/components/font-studio";
2
+ import type { Metadata } from "swift-rust";
3
+ import { localFontCss } from "swift-rust/font";
4
+ import {
5
+ DxSlight,
6
+ DxSlightExtBdUltraSlant,
7
+ DxSlightMediumUltra,
8
+ Lausanne,
9
+ VarentGrotesk,
10
+ VarentGroteskBold,
11
+ VarentGroteskExtLtIta,
12
+ Zimula,
13
+ } from "swift-rust/font";
14
+ import {
15
+ BebasNeue,
16
+ BricolageGrotesque,
17
+ Caveat,
18
+ Cinzel,
19
+ DmSans,
20
+ DmSerifDisplay,
21
+ Figtree,
22
+ FiraCode,
23
+ Geist,
24
+ GeistMono,
25
+ IbmPlexMono,
26
+ IbmPlexSans,
27
+ IbmPlexSerif,
28
+ Inter,
29
+ JetbrainsMono,
30
+ Lora,
31
+ Manrope,
32
+ Outfit,
33
+ PlayfairDisplay,
34
+ PlusJakartaSans,
35
+ Poppins,
36
+ Roboto,
37
+ Sora,
38
+ SourceCodePro,
39
+ SpaceGrotesk,
40
+ } from "swift-rust/font/google";
41
+
42
+ export const metadata: Metadata = { title: "Fonts" };
43
+
44
+ type Factory = (opts?: { variable?: boolean }) => {
45
+ className: string;
46
+ style: Record<string, string>;
47
+ variable?: string;
48
+ };
49
+
50
+ type FeaturedFont = {
51
+ name: string;
52
+ importName: string;
53
+ factory: Factory;
54
+ category: "Sans" | "Serif" | "Mono" | "Display" | "Script";
55
+ blurb: string;
56
+ };
57
+
58
+ const FEATURED: FeaturedFont[] = [
59
+ {
60
+ name: "Geist",
61
+ importName: "Geist",
62
+ factory: Geist as Factory,
63
+ category: "Sans",
64
+ blurb: "Default UI sans, by Vercel",
65
+ },
66
+ {
67
+ name: "Geist Mono",
68
+ importName: "GeistMono",
69
+ factory: GeistMono as Factory,
70
+ category: "Mono",
71
+ blurb: "Default monospace, by Vercel",
72
+ },
73
+ {
74
+ name: "Inter",
75
+ importName: "Inter",
76
+ factory: Inter as Factory,
77
+ category: "Sans",
78
+ blurb: "Designed for screens",
79
+ },
80
+ {
81
+ name: "Roboto",
82
+ importName: "Roboto",
83
+ factory: Roboto as Factory,
84
+ category: "Sans",
85
+ blurb: "Modern, friendly, versatile",
86
+ },
87
+ {
88
+ name: "Poppins",
89
+ importName: "Poppins",
90
+ factory: Poppins as Factory,
91
+ category: "Sans",
92
+ blurb: "Geometric, warm heart",
93
+ },
94
+ {
95
+ name: "Manrope",
96
+ importName: "Manrope",
97
+ factory: Manrope as Factory,
98
+ category: "Sans",
99
+ blurb: "Clean for editorial",
100
+ },
101
+ {
102
+ name: "IBM Plex Sans",
103
+ importName: "IbmPlexSans",
104
+ factory: IbmPlexSans as Factory,
105
+ category: "Sans",
106
+ blurb: "Corporate with character",
107
+ },
108
+ {
109
+ name: "Space Grotesk",
110
+ importName: "SpaceGrotesk",
111
+ factory: SpaceGrotesk as Factory,
112
+ category: "Sans",
113
+ blurb: "Quirky display",
114
+ },
115
+ {
116
+ name: "Plus Jakarta Sans",
117
+ importName: "PlusJakartaSans",
118
+ factory: PlusJakartaSans as Factory,
119
+ category: "Sans",
120
+ blurb: "Elegant, modern",
121
+ },
122
+ {
123
+ name: "Outfit",
124
+ importName: "Outfit",
125
+ factory: Outfit as Factory,
126
+ category: "Sans",
127
+ blurb: "Geometric branding",
128
+ },
129
+ {
130
+ name: "Figtree",
131
+ importName: "Figtree",
132
+ factory: Figtree as Factory,
133
+ category: "Sans",
134
+ blurb: "Friendly, approachable",
135
+ },
136
+ {
137
+ name: "Sora",
138
+ importName: "Sora",
139
+ factory: Sora as Factory,
140
+ category: "Sans",
141
+ blurb: "Made for UI",
142
+ },
143
+ {
144
+ name: "DM Sans",
145
+ importName: "DmSans",
146
+ factory: DmSans as Factory,
147
+ category: "Sans",
148
+ blurb: "Low-contrast geometric",
149
+ },
150
+ {
151
+ name: "Playfair Display",
152
+ importName: "PlayfairDisplay",
153
+ factory: PlayfairDisplay as Factory,
154
+ category: "Serif",
155
+ blurb: "Editorial elegance",
156
+ },
157
+ {
158
+ name: "Lora",
159
+ importName: "Lora",
160
+ factory: Lora as Factory,
161
+ category: "Serif",
162
+ blurb: "Balanced for the screen",
163
+ },
164
+ {
165
+ name: "DM Serif Display",
166
+ importName: "DmSerifDisplay",
167
+ factory: DmSerifDisplay as Factory,
168
+ category: "Serif",
169
+ blurb: "Editorial display",
170
+ },
171
+ {
172
+ name: "IBM Plex Serif",
173
+ importName: "IbmPlexSerif",
174
+ factory: IbmPlexSerif as Factory,
175
+ category: "Serif",
176
+ blurb: "Corporate serif",
177
+ },
178
+ {
179
+ name: "JetBrains Mono",
180
+ importName: "JetbrainsMono",
181
+ factory: JetbrainsMono as Factory,
182
+ category: "Mono",
183
+ blurb: "Code editor default",
184
+ },
185
+ {
186
+ name: "Fira Code",
187
+ importName: "FiraCode",
188
+ factory: FiraCode as Factory,
189
+ category: "Mono",
190
+ blurb: "Monospace ligatures",
191
+ },
192
+ {
193
+ name: "Source Code Pro",
194
+ importName: "SourceCodePro",
195
+ factory: SourceCodePro as Factory,
196
+ category: "Mono",
197
+ blurb: "Adobe monospace",
198
+ },
199
+ {
200
+ name: "IBM Plex Mono",
201
+ importName: "IbmPlexMono",
202
+ factory: IbmPlexMono as Factory,
203
+ category: "Mono",
204
+ blurb: "Corporate mono",
205
+ },
206
+ {
207
+ name: "Bricolage Grotesque",
208
+ importName: "BricolageGrotesque",
209
+ factory: BricolageGrotesque as Factory,
210
+ category: "Display",
211
+ blurb: "Bold contemporary",
212
+ },
213
+ {
214
+ name: "Bebas Neue",
215
+ importName: "BebasNeue",
216
+ factory: BebasNeue as Factory,
217
+ category: "Display",
218
+ blurb: "All-caps display",
219
+ },
220
+ {
221
+ name: "Cinzel",
222
+ importName: "Cinzel",
223
+ factory: Cinzel as Factory,
224
+ category: "Display",
225
+ blurb: "Roman capitals",
226
+ },
227
+ {
228
+ name: "Caveat",
229
+ importName: "Caveat",
230
+ factory: Caveat as Factory,
231
+ category: "Script",
232
+ blurb: "Casual handwriting",
233
+ },
234
+ ];
235
+
236
+ type LocalFont = {
237
+ name: string;
238
+ factory: Factory;
239
+ importName: string;
240
+ weights: string;
241
+ blurb: string;
242
+ };
243
+
244
+ const LOCAL: LocalFont[] = [
245
+ {
246
+ name: "Lausanne",
247
+ factory: Lausanne as Factory,
248
+ importName: "Lausanne",
249
+ weights: "400",
250
+ blurb: "A precise editorial display",
251
+ },
252
+ {
253
+ name: "DxSlight",
254
+ factory: DxSlight as Factory,
255
+ importName: "DxSlight",
256
+ weights: "500, 800 italic",
257
+ blurb: "Decorative medium + ultra",
258
+ },
259
+ {
260
+ name: "DxSlight Medium Ultra",
261
+ factory: DxSlightMediumUltra as Factory,
262
+ importName: "DxSlightMediumUltra",
263
+ weights: "500",
264
+ blurb: "Decorative medium",
265
+ },
266
+ {
267
+ name: "DxSlight ExtBd UltraSlant",
268
+ factory: DxSlightExtBdUltraSlant as Factory,
269
+ importName: "DxSlightExtBdUltraSlant",
270
+ weights: "800 italic",
271
+ blurb: "Heavy display slant",
272
+ },
273
+ {
274
+ name: "Varent Grotesk",
275
+ factory: VarentGrotesk as Factory,
276
+ importName: "VarentGrotesk",
277
+ weights: "700, 200 italic",
278
+ blurb: "Modern grotesk",
279
+ },
280
+ {
281
+ name: "Varent Grotesk Bold",
282
+ factory: VarentGroteskBold as Factory,
283
+ importName: "VarentGroteskBold",
284
+ weights: "700",
285
+ blurb: "Bold variant",
286
+ },
287
+ {
288
+ name: "Varent Grotesk ExtLtIta",
289
+ factory: VarentGroteskExtLtIta as Factory,
290
+ importName: "VarentGroteskExtLtIta",
291
+ weights: "200 italic",
292
+ blurb: "Extra-light italic",
293
+ },
294
+ {
295
+ name: "Zimula",
296
+ factory: Zimula as Factory,
297
+ importName: "Zimula",
298
+ weights: "100-900 + ink trap + ink spot",
299
+ blurb: "Full weight ladder",
300
+ },
301
+ ];
302
+
303
+ const ZIMULA_WEIGHTS: ReadonlyArray<{
304
+ weight: number;
305
+ style: "normal" | "ink-trap" | "ink-spot";
306
+ label: string;
307
+ }> = [
308
+ { weight: 100, style: "normal", label: "Thin" },
309
+ { weight: 200, style: "normal", label: "ExtraLight" },
310
+ { weight: 300, style: "normal", label: "Light" },
311
+ { weight: 400, style: "normal", label: "Regular" },
312
+ { weight: 500, style: "normal", label: "Medium" },
313
+ { weight: 600, style: "normal", label: "SemiBold" },
314
+ { weight: 700, style: "normal", label: "Bold" },
315
+ { weight: 800, style: "normal", label: "ExtraBold" },
316
+ { weight: 900, style: "normal", label: "Black" },
317
+ ];
318
+
319
+ function ZIMULA_FAMILY(style: "normal" | "ink-trap" | "ink-spot"): string {
320
+ if (style === "ink-trap") return "'ZimulaInkTrap'";
321
+ if (style === "ink-spot") return "'ZimulaInkSpot'";
322
+ return "'Zimula'";
323
+ }
324
+
325
+ function ZimulaLadder() {
326
+ return (
327
+ <div className="space-y-2.5">
328
+ {ZIMULA_WEIGHTS.map((w) => (
329
+ <div key={`${w.weight}-${w.style}`} className="flex items-baseline gap-4">
330
+ <span className="w-20 shrink-0 font-mono text-xs text-[var(--color-fg-subtle)]">
331
+ {w.weight} {w.label}
332
+ </span>
333
+ <span
334
+ className="truncate text-[var(--color-fg)]"
335
+ style={{
336
+ fontFamily: ZIMULA_FAMILY(w.style),
337
+ fontWeight: w.weight,
338
+ fontSize: "1.75rem",
339
+ lineHeight: 1.2,
340
+ }}
341
+ >
342
+ The quick brown fox jumps
343
+ </span>
344
+ </div>
345
+ ))}
346
+ </div>
347
+ );
348
+ }
349
+
350
+ const FEATURED_GROUPS: ReadonlyArray<{ name: string; fonts: FeaturedFont[] }> = [
351
+ { name: "Sans-serif", fonts: FEATURED.filter((f) => f.category === "Sans") },
352
+ { name: "Serif", fonts: FEATURED.filter((f) => f.category === "Serif") },
353
+ { name: "Monospace", fonts: FEATURED.filter((f) => f.category === "Mono") },
354
+ {
355
+ name: "Display & script",
356
+ fonts: FEATURED.filter((f) => f.category === "Display" || f.category === "Script"),
357
+ },
358
+ ];
359
+
360
+ export default function FontsPage() {
361
+ const localCss = localFontCss();
362
+
363
+ return (
364
+ <div className="container-page py-16 sm:py-20">
365
+ <header className="mx-auto max-w-3xl text-center">
366
+ <div className="badge badge-accent mb-5 inline-flex">
367
+ <span className="pulse-dot h-1.5 w-1.5 rounded-full bg-current" />
368
+ Typography
369
+ </div>
370
+ <h1 className="text-balance text-5xl font-semibold tracking-tight text-[var(--color-fg)] sm:text-6xl">
371
+ Every font, every weight,
372
+ <br />
373
+ live in your browser.
374
+ </h1>
375
+ <p className="mx-auto mt-6 max-w-2xl text-pretty text-lg leading-relaxed text-[var(--color-fg-muted)]">
376
+ 2,071 Google fonts and 8 local font families, all available as on-demand imports. Pick a
377
+ font, see it in action, and copy the import statement into your project.
378
+ </p>
379
+ <dl className="mt-10 grid grid-cols-2 gap-4 sm:grid-cols-4">
380
+ {[
381
+ { v: "2,071", l: "Google fonts" },
382
+ { v: "8", l: "Local families" },
383
+ { v: "27+", l: "Zimula weights" },
384
+ { v: "auto", l: "Subsetting" },
385
+ ].map((s) => (
386
+ <div
387
+ key={s.l}
388
+ className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-4"
389
+ >
390
+ <dt className="font-mono text-2xl font-semibold text-[var(--color-fg)]">{s.v}</dt>
391
+ <dd className="mt-1 text-xs uppercase tracking-wider text-[var(--color-fg-subtle)]">
392
+ {s.l}
393
+ </dd>
394
+ </div>
395
+ ))}
396
+ </dl>
397
+ </header>
398
+
399
+ <section className="mt-20">
400
+ <div className="mb-6 flex items-baseline justify-between">
401
+ <h2 className="text-2xl font-semibold tracking-tight text-[var(--color-fg)]">
402
+ Live preview
403
+ </h2>
404
+ <span className="text-sm text-[var(--color-fg-muted)]">
405
+ Type a name to load any of the 2,071 Google fonts.
406
+ </span>
407
+ </div>
408
+ <FontStudio />
409
+ </section>
410
+
411
+ <section className="mt-24">
412
+ <div className="mb-2 flex items-baseline justify-between">
413
+ <h2 className="text-2xl font-semibold tracking-tight text-[var(--color-fg)]">
414
+ Featured Google fonts
415
+ </h2>
416
+ <span className="text-sm text-[var(--color-fg-muted)]">
417
+ 25 of 2,071 — every one ships the same way.
418
+ </span>
419
+ </div>
420
+ <p className="mb-8 max-w-2xl text-[var(--color-fg-muted)]">
421
+ These are the fonts most teams reach for. Use the search above to preview any of the
422
+ remaining 2,046.
423
+ </p>
424
+ {FEATURED_GROUPS.map((group) => (
425
+ <div key={group.name} className="mb-10">
426
+ <h3 className="mb-3 text-xs font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
427
+ {group.name}
428
+ </h3>
429
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
430
+ {group.fonts.map((f) => {
431
+ const font = f.factory();
432
+ return (
433
+ <div
434
+ key={f.name}
435
+ className="group flex flex-col gap-2 rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-5 transition-colors hover:border-[var(--color-border-strong)]"
436
+ style={font.style}
437
+ >
438
+ <div className="flex items-baseline justify-between gap-2">
439
+ <h4 className="text-sm font-medium text-[var(--color-fg)]">{f.name}</h4>
440
+ <span className="text-xs text-[var(--color-fg-subtle)]">{f.blurb}</span>
441
+ </div>
442
+ <div className="truncate text-2xl text-[var(--color-fg)]">Beautiful type</div>
443
+ <code
444
+ className="mt-auto truncate font-mono text-[11px] text-[var(--color-fg-muted)]"
445
+ style={{ fontFamily: "ui-monospace, monospace" }}
446
+ >
447
+ import {"{ "}
448
+ {f.importName}
449
+ {" }"} from &quot;swift-rust/font/google&quot;
450
+ </code>
451
+ </div>
452
+ );
453
+ })}
454
+ </div>
455
+ </div>
456
+ ))}
457
+ </section>
458
+
459
+ <section className="mt-24">
460
+ <div className="mb-2 flex items-baseline justify-between">
461
+ <h2 className="text-2xl font-semibold tracking-tight text-[var(--color-fg)]">
462
+ Local fonts
463
+ </h2>
464
+ <span className="text-sm text-[var(--color-fg-muted)]">
465
+ 8 families bundled in the framework.
466
+ </span>
467
+ </div>
468
+ <p className="mb-8 max-w-2xl text-[var(--color-fg-muted)]">
469
+ Bundled in <code className="font-mono text-xs">packages/font/src/local/</code> and served
470
+ from <code className="font-mono text-xs">/_swift-rust/fonts/</code>. Use them when you
471
+ need exact control over weights or want to avoid a network round-trip.
472
+ </p>
473
+ <style dangerouslySetInnerHTML={{ __html: localCss }} />
474
+
475
+ <div className="mb-6 grid grid-cols-1 gap-3 sm:grid-cols-2">
476
+ {LOCAL.filter((f) => f.name !== "Zimula").map((f) => {
477
+ const font = f.factory();
478
+ return (
479
+ <div
480
+ key={f.name}
481
+ className="flex flex-col gap-2 rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-6"
482
+ style={font.style}
483
+ >
484
+ <div className="flex items-baseline justify-between gap-2">
485
+ <h4 className="text-sm font-medium text-[var(--color-fg)]">{f.name}</h4>
486
+ <span
487
+ className="font-mono text-[11px] text-[var(--color-fg-subtle)]"
488
+ style={{ fontFamily: "ui-monospace, monospace" }}
489
+ >
490
+ {f.weights}
491
+ </span>
492
+ </div>
493
+ <div className="text-3xl text-[var(--color-fg)]">Aa Bb Cc 123</div>
494
+ <p className="text-xs text-[var(--color-fg-muted)]">{f.blurb}</p>
495
+ <code
496
+ className="mt-auto truncate font-mono text-[11px] text-[var(--color-fg-muted)]"
497
+ style={{ fontFamily: "ui-monospace, monospace" }}
498
+ >
499
+ import {"{ "}
500
+ {f.importName}
501
+ {" }"} from &quot;swift-rust/font&quot;
502
+ </code>
503
+ </div>
504
+ );
505
+ })}
506
+ </div>
507
+
508
+ <div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-6">
509
+ <div className="mb-4 flex items-baseline justify-between gap-2">
510
+ <div>
511
+ <h4 className="text-sm font-medium text-[var(--color-fg)]">Zimula</h4>
512
+ <p className="mt-0.5 text-xs text-[var(--color-fg-muted)]">
513
+ Full weight ladder 100–900 in three styles (normal, ink-trap, ink-spot)
514
+ </p>
515
+ </div>
516
+ <span className="font-mono text-[11px] text-[var(--color-fg-subtle)]">27 weights</span>
517
+ </div>
518
+ <ZimulaLadder />
519
+ <code className="mt-6 block truncate font-mono text-[11px] text-[var(--color-fg-muted)]">
520
+ import {"{ "}Zimula{" }"} from &quot;swift-rust/font&quot;
521
+ </code>
522
+ </div>
523
+ </section>
524
+
525
+ <section className="mt-24">
526
+ <h2 className="mb-2 text-2xl font-semibold tracking-tight text-[var(--color-fg)]">
527
+ How to use
528
+ </h2>
529
+ <p className="mb-8 max-w-2xl text-[var(--color-fg-muted)]">
530
+ Three patterns cover almost every case. Drop them into your{" "}
531
+ <code className="font-mono text-xs">app/layout.tsx</code> and you&apos;re shipping.
532
+ </p>
533
+ <div className="space-y-6">
534
+ <CodeBlock
535
+ filename="app/layout.tsx"
536
+ caption="Google fonts · one import, one variable, the whole tree gets it"
537
+ code={`import { Geist, GeistMono } from "swift-rust/font/google";
538
+
539
+ const geist = Geist({ subsets: ["latin"], display: "swap", variable: true });
540
+ const geistMono = GeistMono({ subsets: ["latin"], display: "swap", variable: true });
541
+
542
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
543
+ return (
544
+ <html className={\`\${geist.variable} \${geistMono.variable}\`}>
545
+ <body className={geist.className}>{children}</body>
546
+ </html>
547
+ );
548
+ }`}
549
+ />
550
+ <CodeBlock
551
+ filename="app/fonts.ts"
552
+ caption="Local fonts · point at a file, get a CSS class back"
553
+ code={`import { localFont } from "swift-rust/font/local";
554
+
555
+ const heading = localFont({
556
+ src: "./fonts/Heading-Bold.woff2",
557
+ weight: "700",
558
+ display: "swap",
559
+ variable: true,
560
+ });
561
+
562
+ export { heading };`}
563
+ />
564
+ <CodeBlock
565
+ filename="app/layout.tsx"
566
+ caption="Local font CSS · inject @font-face declarations from the framework"
567
+ code={`import { localFontCss, Lausanne } from "swift-rust/font";
568
+ import { Lausanne as LausanneFont } from "swift-rust/font";
569
+
570
+ const css = localFontCss();
571
+ const lausanne = Lausanne({ variable: true });
572
+
573
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
574
+ return (
575
+ <html className={lausanne.variable}>
576
+ <head>
577
+ <style dangerouslySetInnerHTML={{ __html: css }} />
578
+ </head>
579
+ <body>{children}</body>
580
+ </html>
581
+ );
582
+ }`}
583
+ />
584
+ </div>
585
+ </section>
586
+ </div>
587
+ );
588
+ }
589
+
590
+ function CodeBlock({
591
+ filename,
592
+ caption,
593
+ code,
594
+ }: {
595
+ filename: string;
596
+ caption: string;
597
+ code: string;
598
+ }) {
599
+ return (
600
+ <div className="overflow-hidden rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)]">
601
+ <div className="flex items-center justify-between border-b border-[var(--color-border)] bg-[var(--color-surface-2)] px-4 py-2.5">
602
+ <span className="font-mono text-xs text-[var(--color-fg-muted)]">{filename}</span>
603
+ <span className="text-xs text-[var(--color-fg-subtle)]">{caption}</span>
604
+ </div>
605
+ <pre className="overflow-x-auto bg-[var(--color-bg)] p-5 font-mono text-xs leading-relaxed text-[var(--color-fg)]">
606
+ <code>{code}</code>
607
+ </pre>
608
+ </div>
609
+ );
610
+ }