create-frootkit 1.2.11 → 1.2.13

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 (37) hide show
  1. package/package.json +1 -1
  2. package/templates/auth/src/app/dashboard/layout.tsx +8 -11
  3. package/templates/auth/src/app/dashboard/page.tsx +31 -27
  4. package/templates/auth/src/app/layout.tsx +5 -5
  5. package/templates/auth/src/app/login/page.tsx +11 -4
  6. package/templates/auth/src/app/page.tsx +278 -120
  7. package/templates/auth/src/app/signup/page.tsx +12 -6
  8. package/templates/auth/src/components/AuthForm.tsx +27 -34
  9. package/templates/auth/src/components/DashboardNav.tsx +41 -61
  10. package/templates/auth/src/components/MetricCard.tsx +12 -22
  11. package/templates/auth/src/components/Navbar.tsx +18 -21
  12. package/templates/auth/src/styles.css +157 -27
  13. package/templates/blog/src/app/blog/[slug]/page.tsx +85 -46
  14. package/templates/blog/src/app/layout.tsx +5 -5
  15. package/templates/blog/src/app/page.tsx +39 -26
  16. package/templates/blog/src/components/Footer.tsx +11 -9
  17. package/templates/blog/src/components/Navbar.tsx +21 -26
  18. package/templates/blog/src/components/Newsletter.tsx +11 -33
  19. package/templates/blog/src/components/PostCard.tsx +43 -76
  20. package/templates/blog/src/styles.css +113 -25
  21. package/templates/ecommerce/README.md +1 -0
  22. package/templates/ecommerce/src/app/cart/page.tsx +12 -4
  23. package/templates/ecommerce/src/app/layout.tsx +5 -5
  24. package/templates/ecommerce/src/app/page.tsx +92 -37
  25. package/templates/ecommerce/src/app/products/[id]/page.tsx +39 -31
  26. package/templates/ecommerce/src/components/CartDrawer.tsx +113 -45
  27. package/templates/ecommerce/src/components/Footer.tsx +11 -9
  28. package/templates/ecommerce/src/components/Navbar.tsx +32 -34
  29. package/templates/ecommerce/src/components/ProductCard.tsx +27 -38
  30. package/templates/ecommerce/src/styles.css +155 -36
  31. package/templates/starter/README.md +1 -0
  32. package/templates/starter/src/app/layout.tsx +5 -5
  33. package/templates/starter/src/app/page.tsx +114 -107
  34. package/templates/starter/src/components/FeatureCard.tsx +9 -15
  35. package/templates/starter/src/components/Footer.tsx +11 -9
  36. package/templates/starter/src/components/Header.tsx +15 -38
  37. package/templates/starter/src/styles.css +102 -27
@@ -1,40 +1,33 @@
1
1
  export function DashboardNav() {
2
2
  return (
3
- <aside class="w-60 border-r border-white/[0.08] bg-[#0d0e12] p-3 flex flex-col justify-between shrink-0 min-h-screen">
3
+ <aside class="w-60 border-r border-[#e6ebf1] bg-white p-3 flex flex-col justify-between shrink-0 min-h-screen">
4
4
  <div class="space-y-6">
5
- {/* Workspace Brand / Org Selector */}
5
+ {/* Workspace Brand */}
6
6
  <div class="px-2 pt-1">
7
- <div class="flex items-center justify-between p-1.5 rounded-lg hover:bg-white/[0.04] transition-colors cursor-pointer border border-transparent hover:border-white/[0.06]">
8
- <div class="flex items-center gap-2.5 min-w-0">
9
- <div class="w-6 h-6 rounded-md bg-indigo-500/15 border border-indigo-500/30 flex items-center justify-center text-indigo-400 shrink-0">
10
- <svg style="width: 13px; height: 13px; min-width: 13px; min-height: 13px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
11
- <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
12
- </svg>
13
- </div>
14
- <div class="truncate">
15
- <div class="text-xs font-semibold text-zinc-200 truncate">Orbit Workspace</div>
16
- <div class="text-[10px] text-zinc-500 truncate">Production Cluster</div>
17
- </div>
7
+ <a href="/" class="flex items-center gap-2.5 p-1.5 rounded-lg hover:bg-[#f6f9fc] transition-colors">
8
+ <div class="w-7 h-7 rounded-md bg-[#635bff]/10 border border-[#635bff]/20 flex items-center justify-center text-[#635bff] font-bold text-xs shrink-0">
9
+ FA
18
10
  </div>
19
- <svg style="width: 12px; height: 12px; min-width: 12px; min-height: 12px;" class="text-zinc-500 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
20
- <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
21
- </svg>
22
- </div>
11
+ <div class="truncate">
12
+ <div class="text-xs font-semibold text-[#0a2540] truncate">Orbit Workspace</div>
13
+ <div class="text-[10px] text-[#425466] truncate">US-East Production</div>
14
+ </div>
15
+ </a>
23
16
  </div>
24
17
 
25
18
  {/* Navigation Sections */}
26
19
  <div class="space-y-4">
27
20
  <div>
28
- <div class="px-2.5 mb-1.5 text-[10px] font-semibold text-zinc-500 uppercase tracking-wider">
21
+ <div class="px-2.5 mb-1.5 text-[11px] font-semibold text-[#425466]/70 uppercase tracking-wider">
29
22
  Platform
30
23
  </div>
31
24
  <nav class="space-y-0.5 text-xs font-medium">
32
25
  <a
33
26
  href="/dashboard"
34
- class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md bg-white/[0.08] text-white border border-white/[0.08] shadow-[inset_0_1px_0_0_rgba(255,255,255,0.06)]"
27
+ class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md bg-[#635bff]/10 text-[#635bff] font-semibold"
35
28
  >
36
- <span class="w-4 h-4 flex items-center justify-center shrink-0 text-indigo-400">
37
- <svg style="width: 14px; height: 14px; min-width: 14px; min-height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
29
+ <span class="w-4 h-4 flex items-center justify-center shrink-0 text-[#635bff]">
30
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
38
31
  <rect x="3" y="3" width="7" height="7" />
39
32
  <rect x="14" y="3" width="7" height="7" />
40
33
  <rect x="14" y="14" width="7" height="7" />
@@ -46,10 +39,10 @@ export function DashboardNav() {
46
39
 
47
40
  <a
48
41
  href="/dashboard#analytics"
49
- class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-zinc-400 hover:text-zinc-200 hover:bg-white/[0.04] transition-colors"
42
+ class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-[#425466] hover:text-[#0a2540] hover:bg-[#f6f9fc] transition-colors"
50
43
  >
51
- <span class="w-4 h-4 flex items-center justify-center shrink-0 text-zinc-500">
52
- <svg style="width: 14px; height: 14px; min-width: 14px; min-height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
44
+ <span class="w-4 h-4 flex items-center justify-center shrink-0 text-[#425466]">
45
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
53
46
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
54
47
  </svg>
55
48
  </span>
@@ -58,10 +51,10 @@ export function DashboardNav() {
58
51
 
59
52
  <a
60
53
  href="/dashboard#team"
61
- class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-zinc-400 hover:text-zinc-200 hover:bg-white/[0.04] transition-colors"
54
+ class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-[#425466] hover:text-[#0a2540] hover:bg-[#f6f9fc] transition-colors"
62
55
  >
63
- <span class="w-4 h-4 flex items-center justify-center shrink-0 text-zinc-500">
64
- <svg style="width: 14px; height: 14px; min-width: 14px; min-height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
56
+ <span class="w-4 h-4 flex items-center justify-center shrink-0 text-[#425466]">
57
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
65
58
  <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
66
59
  <circle cx="9" cy="7" r="4" />
67
60
  <path d="M23 21v-2a4 4 0 0 0-3-3.87" />
@@ -70,22 +63,15 @@ export function DashboardNav() {
70
63
  </span>
71
64
  <span>Members</span>
72
65
  </a>
73
- </nav>
74
- </div>
75
66
 
76
- <div>
77
- <div class="px-2.5 mb-1.5 text-[10px] font-semibold text-zinc-500 uppercase tracking-wider">
78
- Developer
79
- </div>
80
- <nav class="space-y-0.5 text-xs font-medium">
81
67
  <a
82
68
  href="/api/auth"
83
- class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-zinc-400 hover:text-zinc-200 hover:bg-white/[0.04] transition-colors"
69
+ class="flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-[#425466] hover:text-[#0a2540] hover:bg-[#f6f9fc] transition-colors"
84
70
  >
85
- <span class="w-4 h-4 flex items-center justify-center shrink-0 text-zinc-500">
86
- <svg style="width: 14px; height: 14px; min-width: 14px; min-height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
87
- <polyline points="16 18 22 12 16 6" />
88
- <polyline points="8 6 2 12 8 18" />
71
+ <span class="w-4 h-4 flex items-center justify-center shrink-0 text-[#425466]">
72
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
73
+ <path d="m21 2-2 2m-2-2 2 2" />
74
+ <path d="M15.5 8.5 7 17l-4 1 1-4 8.5-8.5a2.12 2.12 0 0 1 3 3z" />
89
75
  </svg>
90
76
  </span>
91
77
  <span>Session API</span>
@@ -95,30 +81,24 @@ export function DashboardNav() {
95
81
  </div>
96
82
  </div>
97
83
 
98
- {/* User profile & Sign out */}
99
- <div class="p-2 border-t border-white/[0.06] flex items-center justify-between">
100
- <div class="flex items-center gap-2.5 min-w-0">
101
- <div class="w-7 h-7 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center font-medium text-[11px] text-white shrink-0">
102
- AD
103
- </div>
104
- <div class="text-xs min-w-0 truncate">
105
- <div class="font-medium text-zinc-200 truncate">admin@frootkit.dev</div>
106
- <div class="text-zinc-500 text-[10px] truncate">Owner</div>
84
+ {/* User Footer Profile */}
85
+ <div class="pt-3 border-t border-[#e6ebf1]">
86
+ <div class="flex items-center justify-between p-2 rounded-lg bg-[#f6f9fc] text-xs">
87
+ <div class="flex items-center gap-2 min-w-0">
88
+ <div class="w-7 h-7 rounded-full bg-[#635bff] text-white flex items-center justify-center text-xs font-semibold shrink-0">
89
+ AD
90
+ </div>
91
+ <div class="truncate">
92
+ <div class="font-medium text-[#0a2540] truncate">admin@frootkit.dev</div>
93
+ <div class="text-[10px] text-[#425466]">Administrator</div>
94
+ </div>
107
95
  </div>
108
- </div>
109
- <form action="/api/auth?action=logout" method="POST" class="shrink-0 ml-1">
110
- <button
111
- type="submit"
112
- class="p-1.5 rounded-md text-zinc-500 hover:text-zinc-200 hover:bg-white/[0.06] transition-colors"
113
- title="Sign out"
114
- >
115
- <svg style="width: 14px; height: 14px; min-width: 14px; min-height: 14px;" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
116
- <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
117
- <polyline points="16 17 21 12 16 7" />
118
- <line x1="21" y1="12" x2="9" y2="12" />
96
+ <a href="/login" class="text-[#425466] hover:text-[#cd3d64] p-1" title="Sign out">
97
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
98
+ <path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
119
99
  </svg>
120
- </button>
121
- </form>
100
+ </a>
101
+ </div>
122
102
  </div>
123
103
  </aside>
124
104
  );
@@ -1,34 +1,24 @@
1
1
  export interface MetricCardProps {
2
2
  label: string;
3
- value: string;
3
+ value: string | number;
4
4
  change: string;
5
- isPositive: boolean;
6
- context?: string;
5
+ isPositive?: boolean;
7
6
  }
8
7
 
9
- export function MetricCard({ label, value, change, isPositive, context = 'vs last 30d' }: MetricCardProps) {
8
+ export function MetricCard({ label, value, change, isPositive = true }: MetricCardProps) {
10
9
  return (
11
- <div class="rounded-xl border border-white/[0.08] bg-[#0f1117] p-4 sm:p-5 flex flex-col justify-between shadow-[inset_0_1px_0_0_rgba(255,255,255,0.05)] transition-all hover:border-white/[0.14]">
12
- <div class="text-xs font-medium text-zinc-400 mb-3 tracking-normal">
13
- {label}
10
+ <div class="rounded-lg border border-[#e6ebf1] bg-white p-5 shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)] transition hover:-translate-y-0.5 hover:shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]">
11
+ <div class="flex items-center justify-between">
12
+ <span class="text-[13px] font-medium text-[#425466]">{label}</span>
14
13
  </div>
15
- <div class="flex items-baseline justify-between gap-3">
16
- <div class="text-2xl sm:text-3xl font-semibold text-white tracking-tight tabular-nums">
14
+
15
+ <div class="mt-3 flex items-baseline justify-between gap-2">
16
+ <span class="text-[28px] sm:text-[30px] font-mono font-light text-[#0a2540] tracking-tight">
17
17
  {value}
18
- </div>
19
- <span
20
- class={`inline-flex items-center gap-1 text-[11px] font-medium px-2 py-0.5 rounded-full border shrink-0 ${
21
- isPositive
22
- ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'
23
- : 'bg-rose-500/10 text-rose-400 border-rose-500/20'
24
- }`}
25
- >
26
- <span>{isPositive ? '↑' : '↓'}</span>
27
- <span>{change}</span>
28
18
  </span>
29
- </div>
30
- <div class="mt-2 text-[11px] text-zinc-500">
31
- {context}
19
+ <span class={`bd ${isPositive ? 'bg-[#24b47e]/15 text-[#0e6245]' : 'bg-[#cd3d64]/10 text-[#cd3d64]'}`}>
20
+ {change}
21
+ </span>
32
22
  </div>
33
23
  </div>
34
24
  );
@@ -1,33 +1,30 @@
1
1
  export function Navbar() {
2
2
  return (
3
- <header class="border-b border-white/[0.08] bg-[#08090a]/80 backdrop-blur-md sticky top-0 z-50">
4
- <div class="max-w-6xl mx-auto px-6 h-14 flex items-center justify-between">
5
- <a href="/" class="flex items-center gap-2.5 font-semibold text-zinc-100 text-sm tracking-tight group">
6
- <div class="w-7 h-7 rounded-md bg-indigo-500/10 border border-indigo-500/25 flex items-center justify-center text-indigo-400 group-hover:border-indigo-400/50 transition-colors shrink-0">
7
- <svg style="width: 14px; height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
8
- <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
9
- <path d="M7 11V7a5 5 0 0 1 10 0v4" />
10
- </svg>
11
- </div>
12
- <span>Froot<span class="text-indigo-400">Auth</span></span>
3
+ <header class="absolute inset-x-0 top-0 z-30">
4
+ <nav class="mx-auto max-w-6xl px-6 flex items-center justify-between py-5 text-white">
5
+ <a href="/" class="text-[22px] font-semibold tracking-tight flex items-center gap-2">
6
+ <span>froot<span class="opacity-90">auth</span></span>
13
7
  </a>
14
8
 
15
- <nav class="flex items-center gap-5 text-xs font-medium">
16
- <a href="/dashboard" class="text-zinc-400 hover:text-zinc-200 transition-colors">
17
- Dashboard
18
- </a>
19
- <a href="/login" class="text-zinc-400 hover:text-zinc-200 transition-colors">
20
- Sign In
9
+ <div class="hidden items-center gap-8 text-[15px] font-medium md:flex">
10
+ <a href="#features" class="opacity-90 hover:opacity-100 transition-opacity">Product</a>
11
+ <a href="#developers" class="opacity-90 hover:opacity-100 transition-opacity">Developers</a>
12
+ <a href="#pricing" class="opacity-90 hover:opacity-100 transition-opacity">Pricing</a>
13
+ <a href="/dashboard" class="opacity-90 hover:opacity-100 transition-opacity">Dashboard</a>
14
+ </div>
15
+
16
+ <div class="flex items-center gap-4 text-[15px] font-medium">
17
+ <a href="/login" class="hidden opacity-90 hover:opacity-100 sm:block transition-opacity">
18
+ Sign in
21
19
  </a>
22
20
  <a
23
21
  href="/signup"
24
- class="px-3 py-1.5 rounded-md bg-white text-zinc-950 font-medium hover:bg-zinc-200 transition-all text-xs shadow-sm flex items-center gap-1.5"
22
+ class="rounded-full bg-white px-4 py-1.5 text-[14px] text-[#635bff] font-medium shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)] hover:bg-[#f6f9fc] transition-colors"
25
23
  >
26
- <span>Get Started</span>
27
- <span class="text-zinc-500">→</span>
24
+ Start free
28
25
  </a>
29
- </nav>
30
- </div>
26
+ </div>
27
+ </nav>
31
28
  </header>
32
29
  );
33
30
  }
@@ -4,48 +4,178 @@
4
4
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
5
5
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
6
6
 
7
- --color-brand-primary: #6366f1;
8
- --color-brand-hover: #4f46e5;
7
+ --color-brand: #635bff;
8
+ --color-brand-hover: #5249e6;
9
+ --color-navy: #0a2540;
10
+ --color-slate: #425466;
11
+ --color-surface: #f6f9fc;
12
+ --color-hairline: #e6ebf1;
13
+ --color-cyan: #00d4ff;
14
+ --color-success: #24b47e;
15
+ --color-error: #cd3d64;
16
+ --color-warn: #c98a00;
9
17
  }
10
18
 
11
19
  :root {
12
- color-scheme: dark;
13
- --bg-canvas: #08090a;
14
- --bg-surface: #0f1117;
15
- --bg-elevated: #151822;
16
- --border-subtle: rgba(255, 255, 255, 0.08);
17
- --border-strong: rgba(255, 255, 255, 0.16);
18
- --text-primary: #f4f4f5;
19
- --text-secondary: #9496a1;
20
- --text-muted: #5c6370;
21
- }
22
-
23
- html {
24
- background-color: var(--bg-canvas);
25
- color: var(--text-primary);
26
- font-family: var(--font-sans);
27
- scroll-behavior: smooth;
20
+ color-scheme: light;
21
+ --brand: #635bff;
22
+ --navy: #0a2540;
23
+ --slate: #425466;
24
+ --surface: #f6f9fc;
25
+ --hairline: #e6ebf1;
26
+ --cyan: #00d4ff;
27
+ --success: #24b47e;
28
+ --error: #cd3d64;
29
+ --warn: #c98a00;
30
+
31
+ --shadow-card: 0 2px 5px -1px rgba(50, 50, 93, 0.25), 0 1px 3px -1px rgba(0, 0, 0, 0.3);
32
+ --shadow-pop: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3);
33
+ }
34
+
35
+ body {
36
+ font-size: 15px;
37
+ line-height: 24px;
38
+ background-color: #ffffff;
39
+ color: #425466;
28
40
  -webkit-font-smoothing: antialiased;
29
41
  -moz-osx-font-smoothing: grayscale;
30
42
  }
31
43
 
44
+ :focus-visible {
45
+ outline: 2px solid #635bff;
46
+ outline-offset: 2px;
47
+ }
48
+
32
49
  /* Strict SVG safety to prevent flex ballooning */
33
50
  svg {
34
51
  flex-shrink: 0;
35
52
  max-width: 100%;
36
53
  }
37
54
 
38
- /* Linear-style subtle card highlight */
39
- .linear-card {
40
- background-color: var(--bg-surface);
41
- border: 1px solid var(--border-subtle);
42
- box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.06);
43
- border-radius: 0.75rem;
44
- transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
55
+ /* Stripe Design Tokens & Utility Classes */
56
+ .btn {
57
+ display: inline-flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ gap: 0.5rem;
61
+ border-radius: 9999px;
62
+ padding: 0.625rem 1.25rem;
63
+ font-size: 15px;
64
+ font-weight: 500;
65
+ transition: all 150ms cubic-bezier(0.215, 0.61, 0.355, 1);
66
+ cursor: pointer;
67
+ }
68
+
69
+ .btn-p {
70
+ background-color: #635bff;
71
+ color: #ffffff;
72
+ box-shadow: var(--shadow-card);
73
+ }
74
+ .btn-p:hover {
75
+ background-color: #5249e6;
76
+ }
77
+
78
+ .btn-w {
79
+ background-color: #ffffff;
80
+ color: #635bff;
81
+ box-shadow: var(--shadow-card);
82
+ }
83
+ .btn-w:hover {
84
+ background-color: #f6f9fc;
85
+ }
86
+
87
+ .btn-o {
88
+ border: 1px solid #e6ebf1;
89
+ background-color: #ffffff;
90
+ color: #0a2540;
91
+ box-shadow: var(--shadow-card);
92
+ }
93
+ .btn-o:hover {
94
+ background-color: #f6f9fc;
95
+ }
96
+
97
+ .btn-gl {
98
+ border: 1px solid rgba(255, 255, 255, 0.4);
99
+ color: #ffffff;
100
+ }
101
+ .btn-gl:hover {
102
+ background-color: rgba(255, 255, 255, 0.1);
103
+ }
104
+
105
+ .btn-s {
106
+ border: 1px solid #e6ebf1;
107
+ background-color: #ffffff;
108
+ color: #0a2540;
109
+ box-shadow: var(--shadow-card);
110
+ }
111
+ .btn-s:hover {
112
+ background-color: #f6f9fc;
113
+ }
114
+
115
+ .btn-n {
116
+ background-color: #0a2540;
117
+ color: #ffffff;
118
+ box-shadow: var(--shadow-card);
119
+ }
120
+ .btn-n:hover {
121
+ background-color: #425466;
122
+ }
123
+
124
+ .bd {
125
+ display: inline-flex;
126
+ align-items: center;
127
+ border-radius: 9999px;
128
+ padding: 0.125rem 0.625rem;
129
+ font-size: 13px;
130
+ font-weight: 500;
131
+ }
132
+
133
+ .inp {
134
+ width: 100%;
135
+ border-radius: 8px;
136
+ border: 1px solid #e6ebf1;
137
+ background-color: #ffffff;
138
+ padding: 0.5rem 0.75rem;
139
+ font-size: 15px;
140
+ color: #0a2540;
141
+ outline: none;
142
+ transition: all 150ms cubic-bezier(0.215, 0.61, 0.355, 1);
143
+ }
144
+ .inp:focus {
145
+ border-color: #635bff;
146
+ box-shadow: 0 0 0 3px rgba(99, 91, 255, 0.25);
147
+ }
148
+
149
+ .card {
150
+ border-radius: 8px;
151
+ border: 1px solid #e6ebf1;
152
+ background-color: #ffffff;
153
+ padding: 1.25rem;
154
+ box-shadow: var(--shadow-card);
155
+ }
156
+
157
+ .shadow-card {
158
+ box-shadow: var(--shadow-card);
159
+ }
160
+
161
+ .shadow-pop {
162
+ box-shadow: var(--shadow-pop);
163
+ }
164
+
165
+ .hero {
166
+ background: linear-gradient(160deg, #635bff 0%, #7a73ff 38%, #00d4ff 100%);
167
+ clip-path: polygon(0 0, 100% 0, 100% 88%, 0 100%);
168
+ }
169
+
170
+ @keyframes rise {
171
+ from {
172
+ opacity: 0;
173
+ transform: translateY(14px);
174
+ }
45
175
  }
46
176
 
47
- .linear-card:hover {
48
- border-color: var(--border-strong);
177
+ .rise {
178
+ animation: rise 0.6s cubic-bezier(0.215, 0.61, 0.355, 1) both;
49
179
  }
50
180
 
51
181
  /* Accessibility: Mandatory Reduced Motion */
@@ -9,66 +9,105 @@ export async function generateStaticParams() {
9
9
  export default async function BlogPostPage({ params }: { params?: { slug?: string } }) {
10
10
  const slug = params?.slug || 'welcome-to-frootkit';
11
11
  const post = (await getPostBySlug(slug)) || POSTS[0]!;
12
+ const relatedPosts = POSTS.filter((p) => p.slug !== post.slug).slice(0, 3);
12
13
 
13
14
  return (
14
- <div class="min-h-screen bg-[#08090a] text-zinc-100 flex flex-col justify-between selection:bg-indigo-500/20 selection:text-indigo-300">
15
+ <div class="min-h-screen bg-white text-[#425466] flex flex-col justify-between">
15
16
  <Navbar />
16
17
 
17
- <main class="max-w-3xl mx-auto px-6 py-12 flex-1 w-full">
18
- <a
19
- href="/"
20
- class="inline-flex items-center gap-2 text-xs text-zinc-400 hover:text-white mb-8 transition-colors"
21
- >
22
- <span>←</span>
23
- <span>Back to articles</span>
24
- </a>
25
-
26
- <header class="mb-10">
27
- <div class="flex items-center gap-3 mb-4 text-xs">
28
- <span class="px-2.5 py-0.5 rounded-full bg-indigo-500/10 text-indigo-400 border border-indigo-500/20 font-medium text-[11px]">
29
- {post.category}
30
- </span>
31
- <span class="text-zinc-600">•</span>
32
- <span class="text-zinc-400">{post.readTime}</span>
33
- <span class="text-zinc-600">•</span>
34
- <span class="text-zinc-400">{post.date}</span>
35
- </div>
36
-
37
- <h1 class="text-3xl sm:text-4xl font-semibold text-white tracking-[-0.025em] mb-6 leading-tight">
18
+ {/* Article Gradient Header */}
19
+ <header
20
+ class="relative overflow-hidden text-white"
21
+ style="background: linear-gradient(145deg, #635bff, #00d4ff)"
22
+ >
23
+ <div class="mx-auto max-w-3xl px-6 py-14 md:py-20">
24
+ <a href="/#articles" class="text-[14px] text-white/80 hover:text-white transition-colors">
25
+ ← All articles
26
+ </a>
27
+ <span class="bd ml-3 bg-white/15 text-white">{post.category}</span>
28
+ <h1 class="mt-5 text-[34px] font-light leading-[1.15] tracking-tight md:text-[48px]">
38
29
  {post.title}
39
30
  </h1>
40
-
41
- <div class="flex items-center gap-3.5 p-3.5 rounded-xl bg-[#0f1117] border border-white/[0.08] shadow-[inset_0_1px_0_0_rgba(255,255,255,0.05)]">
42
- <img
43
- src={post.author.avatar}
44
- alt={post.author.name}
45
- class="w-9 h-9 rounded-full object-cover border border-white/[0.1]"
46
- />
47
- <div>
48
- <div class="font-medium text-white text-xs">{post.author.name}</div>
49
- <div class="text-[11px] text-zinc-400">{post.author.role}</div>
50
- </div>
31
+ <p class="mt-4 text-[18px] font-light text-white/90">
32
+ {post.summary}
33
+ </p>
34
+ <div class="mt-6 flex items-center gap-3">
35
+ <span class="grid h-10 w-10 text-[13px] shrink-0 place-items-center rounded-full font-medium text-white bg-[#0a2540]">
36
+ {post.author.name.split(' ').map((n) => n[0]).join('')}
37
+ </span>
38
+ <span class="text-[14px]">
39
+ <b class="block">{post.author.name}</b>
40
+ <span class="text-white/80">{post.author.role} · {post.date} · {post.readTime}</span>
41
+ </span>
51
42
  </div>
52
- </header>
43
+ </div>
44
+ </header>
53
45
 
54
- <article class="prose max-w-none space-y-6 text-zinc-300 leading-relaxed text-base">
46
+ {/* Article Content */}
47
+ <main class="mx-auto max-w-3xl px-6 py-12 flex-1 w-full">
48
+ <div class="space-y-6">
55
49
  {post.content.map((paragraph, idx) => (
56
- <p key={idx}>{paragraph}</p>
50
+ <p key={idx} class="prose-p">{paragraph}</p>
57
51
  ))}
58
- </article>
59
52
 
60
- <div class="mt-12 pt-8 border-t border-white/[0.08] flex flex-wrap gap-2">
61
- {post.tags.map((tag) => (
62
- <span
63
- key={tag}
64
- class="px-2.5 py-1 rounded-full bg-white/[0.04] text-xs font-medium text-zinc-300 border border-white/[0.06]"
65
- >
66
- {tag}
67
- </span>
68
- ))}
53
+ <blockquote class="my-8 border-l-4 border-[#635bff] pl-5 text-[22px] font-light leading-8 text-[#0a2540]">
54
+ “If a request can be repeated, it must be safe to repeat.”
55
+ </blockquote>
56
+
57
+ <div class="my-6 flex gap-3 rounded-md border border-[#635bff]/20 bg-[#635bff]/5 p-4 text-[15px] text-[#0a2540]">
58
+ <b class="text-[#635bff]">Note:</b>
59
+ <span>SSR renders directly with sub-millisecond execution latency on FrootKit.</span>
60
+ </div>
61
+ </div>
62
+
63
+ {/* Feedback Row */}
64
+ <div class="mt-10 flex flex-wrap items-center gap-3 border-t border-[#e6ebf1] pt-6 text-sm">
65
+ <span class="text-[#425466]">Was this useful?</span>
66
+ <button type="button" class="btn btn-s text-xs py-1 px-3">Yes</button>
67
+ <button type="button" class="btn btn-s text-xs py-1 px-3">No</button>
68
+ </div>
69
+
70
+ {/* Author Bio Card */}
71
+ <div class="mt-8 flex gap-4 rounded-lg bg-[#f6f9fc] p-5 border border-[#e6ebf1]">
72
+ <span class="grid h-12 w-12 text-[15px] shrink-0 place-items-center rounded-full font-medium text-white bg-[#635bff]">
73
+ {post.author.name.split(' ').map((n) => n[0]).join('')}
74
+ </span>
75
+ <div>
76
+ <b class="text-[#0a2540]">{post.author.name}</b>
77
+ <p class="text-[14px] text-[#425466] mt-0.5">
78
+ {post.author.role} at FrootKit. Writes about how high-throughput systems scale with zero hydration bottlenecks.
79
+ </p>
80
+ </div>
69
81
  </div>
70
82
  </main>
71
83
 
84
+ {/* Keep Reading */}
85
+ <section class="bg-[#f6f9fc] py-14 border-t border-[#e6ebf1]">
86
+ <div class="mx-auto max-w-6xl px-6">
87
+ <h2 class="text-[26px] font-light text-[#0a2540]">Keep reading</h2>
88
+ <div class="mt-6 grid gap-6 sm:grid-cols-3">
89
+ {relatedPosts.map((r) => (
90
+ <a
91
+ key={r.slug}
92
+ href={`/blog/${r.slug}`}
93
+ class="group block rounded-lg bg-white p-3 shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)] transition hover:shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)] border border-[#e6ebf1]"
94
+ >
95
+ <div
96
+ class="aspect-[16/10] overflow-hidden rounded-lg p-3 text-white flex flex-col justify-end"
97
+ style="background: linear-gradient(145deg, #0a2540, #635bff)"
98
+ >
99
+ <span class="font-mono text-[11px] opacity-75">{r.readTime}</span>
100
+ </div>
101
+ <h3 class="mt-3 px-1 font-medium leading-6 text-[#0a2540] group-hover:text-[#635bff] transition-colors">
102
+ {r.title}
103
+ </h3>
104
+ <p class="px-1 pb-1 text-[13px] text-[#425466] mt-1">{r.readTime}</p>
105
+ </a>
106
+ ))}
107
+ </div>
108
+ </div>
109
+ </section>
110
+
72
111
  <Footer />
73
112
  </div>
74
113
  );