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,71 +1,126 @@
1
- import { getProducts, PRODUCTS } from '../data/products.js';
1
+ import { getProducts } from '../data/products.js';
2
2
  import { Navbar } from '../components/Navbar.js';
3
3
  import { ProductCard } from '../components/ProductCard.js';
4
4
  import { Footer } from '../components/Footer.js';
5
5
 
6
6
  export const metadata = {
7
- title: 'FrootStore — Tactile Instruments for Developers',
8
- description: 'Precision mechanical keyboards, studio audio, and ergonomic setups.',
7
+ title: 'Parcel — Everyday objects, well made',
8
+ description: 'Small-batch goods for your desk, kitchen, and commute. Built to last, priced fairly.',
9
9
  };
10
10
 
11
11
  export default async function StoreHomePage() {
12
12
  const products = await getProducts();
13
- const categories = ['All Products', 'Keyboards', 'Audio', 'Peripherals', 'Desk Setup', 'Accessories'];
13
+ const categories = ['All', 'Keyboards', 'Audio', 'Peripherals', 'Desk Setup', 'Accessories'];
14
14
 
15
15
  return (
16
- <div class="min-h-screen bg-[#090a0f] text-zinc-100 flex flex-col justify-between selection:bg-white/20 selection:text-white">
16
+ <div class="bg-white font-sans text-[#425466]">
17
17
  <Navbar />
18
18
 
19
- <main class="max-w-6xl mx-auto px-6 py-12 sm:py-16 flex-1 w-full">
20
- {/* Curated Collection Hero */}
21
- <div class="mb-12 border-b border-white/[0.08] pb-10">
22
- <div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-white/[0.04] border border-white/[0.08] text-zinc-300 text-xs font-medium mb-4">
23
- <span class="w-1.5 h-1.5 rounded-full bg-emerald-400" />
24
- <span>Curated Hardware Collection</span>
19
+ {/* HERO SECTION */}
20
+ <section class="relative overflow-hidden text-white" style="background: linear-gradient(160deg, #635bff 0%, #7a73ff 40%, #00d4ff 100%)">
21
+ <div class="mx-auto max-w-6xl px-6 grid items-center gap-8 py-16 md:grid-cols-2 md:py-24">
22
+ <div>
23
+ <span class="bd bg-white/15 text-white">Autumn collection</span>
24
+ <h1 class="mt-4 text-[40px] font-light leading-[1.08] tracking-tight md:text-[56px] md:leading-[64px]">
25
+ Everyday objects, well made
26
+ </h1>
27
+ <p class="mt-5 max-w-md text-[18px] font-light leading-7 text-white/90">
28
+ Small-batch goods for your desk, kitchen, and commute. Built to last, priced fairly.
29
+ </p>
30
+ <div class="mt-7 flex flex-wrap gap-3">
31
+ <a href="#shop" class="btn rounded-full bg-white px-5 py-2.5 text-[#635bff] shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)] hover:bg-[#f6f9fc]">
32
+ Shop the collection
33
+ </a>
34
+ <a href="/cart" class="btn rounded-full border border-white/40 px-5 py-2.5 text-white hover:bg-white/10">
35
+ View Cart (2)
36
+ </a>
37
+ </div>
25
38
  </div>
26
39
 
27
- <h1 class="text-3xl sm:text-5xl font-semibold text-white tracking-[-0.03em] leading-tight mb-4">
28
- Tactile Instruments for Focused Engineering.
29
- </h1>
40
+ <div class="relative hidden h-72 md:block" aria-hidden="true">
41
+ <div class="absolute right-4 top-0 h-56 w-44 rotate-6 rounded-lg bg-white shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)] p-3">
42
+ <div class="h-32 rounded-md bg-gradient-to-br from-[#635bff] to-[#a29bff]" />
43
+ <div class="mt-3 h-2 w-20 rounded bg-[#e6ebf1]" />
44
+ <div class="mt-2 h-2 w-12 rounded bg-[#e6ebf1]" />
45
+ </div>
46
+ <div class="absolute bottom-0 left-4 h-52 w-44 -rotate-6 rounded-lg bg-white shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)] p-3">
47
+ <div class="h-28 rounded-md bg-gradient-to-br from-[#00d4ff] to-[#0a2540]" />
48
+ <div class="mt-3 h-2 w-24 rounded bg-[#e6ebf1]" />
49
+ <div class="mt-2 h-2 w-10 rounded bg-[#e6ebf1]" />
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </section>
30
54
 
31
- <p class="text-zinc-400 text-base max-w-2xl leading-relaxed">
32
- Minimal workspace instruments, CNC aluminum mechanical keyboards, and planar audio drivers designed to eliminate friction from extended development sessions.
33
- </p>
55
+ {/* TRUST ROW */}
56
+ <section class="border-b border-[#e6ebf1] bg-[#f6f9fc]">
57
+ <div class="mx-auto max-w-6xl px-6 grid gap-4 py-5 text-[14px] sm:grid-cols-3">
58
+ <p><b class="text-[#0a2540]">Free returns</b> for 30 days</p>
59
+ <p><b class="text-[#0a2540]">Secure checkout</b> with encrypted payments</p>
60
+ <p><b class="text-[#0a2540]">Ships in 24 hours</b> from our warehouse</p>
61
+ </div>
62
+ </section>
34
63
 
35
- {/* Category Filter Pills */}
36
- <div class="flex flex-wrap items-center gap-2 mt-8">
64
+ {/* SHOP SECTION */}
65
+ <section id="shop" class="scroll-mt-16 py-14">
66
+ <div class="mx-auto max-w-6xl px-6">
67
+ <div class="flex flex-wrap items-end justify-between gap-4">
68
+ <div>
69
+ <h2 class="text-[32px] font-light tracking-tight text-[#0a2540]">Shop all</h2>
70
+ <p class="text-[14px] text-[#425466] mt-0.5">{products.length} products available</p>
71
+ </div>
72
+ <div class="flex items-center gap-3">
73
+ <label for="sort" class="text-[13px] text-[#425466]">Sort</label>
74
+ <select id="sort" class="inp w-auto pr-8 py-1.5 text-xs">
75
+ <option value="f">Featured</option>
76
+ <option value="lo">Price: low to high</option>
77
+ <option value="hi">Price: high to low</option>
78
+ <option value="r">Top rated</option>
79
+ </select>
80
+ </div>
81
+ </div>
82
+
83
+ {/* Categories Filter Pills */}
84
+ <div class="mt-5 flex flex-wrap gap-2">
37
85
  {categories.map((c, i) => (
38
86
  <a
39
87
  key={c}
40
- href="/#catalog"
41
- class={`px-3 py-1 rounded-full text-xs font-medium transition-colors ${
88
+ href="/#shop"
89
+ class={`rounded-full px-4 py-1.5 text-[14px] font-medium transition ${
42
90
  i === 0
43
- ? 'bg-white text-zinc-950 shadow-sm'
44
- : 'bg-white/[0.04] border border-white/[0.08] text-zinc-400 hover:text-white hover:bg-white/[0.08]'
91
+ ? 'bg-[#0a2540] text-white shadow-sm'
92
+ : 'bg-[#f6f9fc] text-[#425466] hover:bg-[#e6ebf1]'
45
93
  }`}
46
94
  >
47
95
  {c}
48
96
  </a>
49
97
  ))}
50
98
  </div>
51
- </div>
52
99
 
53
- {/* Catalog Section Header */}
54
- <div id="catalog" class="mb-6 flex items-center justify-between">
55
- <h2 class="text-xs uppercase tracking-wider text-zinc-400 font-semibold">
56
- Featured Instruments
57
- </h2>
58
- <span class="text-xs text-zinc-500 tabular-nums">
59
- {products.length} products available
60
- </span>
100
+ {/* Products Grid */}
101
+ <div class="mt-8 grid grid-cols-2 gap-x-5 gap-y-9 lg:grid-cols-4">
102
+ {products.map((product) => (
103
+ <ProductCard key={product.id} product={product} />
104
+ ))}
105
+ </div>
61
106
  </div>
107
+ </section>
62
108
 
63
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
64
- {products.map((product) => (
65
- <ProductCard key={product.id} product={product} />
66
- ))}
109
+ {/* NEWSLETTER */}
110
+ <section class="pb-16">
111
+ <div class="mx-auto max-w-6xl px-6">
112
+ <div class="grid items-center gap-6 rounded-lg bg-[#f6f9fc] p-8 md:grid-cols-2 md:p-12 border border-[#e6ebf1]">
113
+ <div>
114
+ <h2 class="text-[28px] font-light tracking-tight text-[#0a2540]">New arrivals, once a month</h2>
115
+ <p class="mt-1 text-sm text-[#425466]">No spam. Unsubscribe anytime.</p>
116
+ </div>
117
+ <form class="flex gap-2">
118
+ <input class="inp" type="email" placeholder="you@email.com" aria-label="Email" />
119
+ <button type="button" class="btn btn-p shrink-0">Subscribe</button>
120
+ </form>
121
+ </div>
67
122
  </div>
68
- </main>
123
+ </section>
69
124
 
70
125
  <Footer />
71
126
  </div>
@@ -11,13 +11,13 @@ export default async function ProductDetailPage({ params }: { params?: { id?: st
11
11
  const product = (await getProduct(id)) || PRODUCTS[0]!;
12
12
 
13
13
  return (
14
- <div class="min-h-screen bg-[#090a0f] text-zinc-100 flex flex-col justify-between selection:bg-white/20 selection:text-white">
14
+ <div class="min-h-screen bg-white text-[#425466] flex flex-col justify-between">
15
15
  <Navbar />
16
16
 
17
17
  <main class="max-w-6xl mx-auto px-6 py-12 flex-1 w-full">
18
18
  <a
19
- href="/"
20
- class="inline-flex items-center gap-2 text-xs text-zinc-400 hover:text-white mb-8 transition-colors"
19
+ href="/#shop"
20
+ class="inline-flex items-center gap-2 text-xs text-[#425466] hover:text-[#0a2540] mb-8 transition-colors"
21
21
  >
22
22
  <span>←</span>
23
23
  <span>Back to catalog</span>
@@ -25,67 +25,75 @@ export default async function ProductDetailPage({ params }: { params?: { id?: st
25
25
 
26
26
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-start">
27
27
  {/* Product Gallery Image */}
28
- <div class="rounded-2xl border border-white/[0.08] overflow-hidden bg-[#0f1117] aspect-[4/3] shadow-2xl relative">
28
+ <div class="rounded-lg border border-[#e6ebf1] overflow-hidden bg-[#f6f9fc] aspect-[4/3] shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)]">
29
29
  <img
30
30
  src={product.image}
31
31
  alt={product.name}
32
32
  class="w-full h-full object-cover"
33
33
  />
34
- <div class="absolute inset-0 bg-gradient-to-t from-black/30 via-transparent to-transparent pointer-events-none" />
35
34
  </div>
36
35
 
37
36
  {/* Details & Purchase */}
38
37
  <div>
39
38
  <div class="flex items-center gap-3 mb-3 text-xs">
40
- <span class="px-2.5 py-0.5 rounded-full bg-white/[0.04] text-zinc-300 border border-white/[0.08] font-medium text-[11px] uppercase tracking-wider">
39
+ <span class="bd bg-[#f6f9fc] text-[#425466] border border-[#e6ebf1]">
41
40
  {product.category}
42
41
  </span>
43
- <span class="text-zinc-600">•</span>
44
- <div class="flex items-center gap-1.5 text-zinc-400 font-medium">
45
- <span class="text-amber-400">★</span>
46
- <span class="text-zinc-200 tabular-nums">{product.rating.toFixed(1)}</span>
47
- <span class="text-zinc-500 tabular-nums">({product.reviewsCount} verified reviews)</span>
42
+ <span class="text-[#e6ebf1]">•</span>
43
+ <div class="flex items-center gap-1.5 text-xs text-[#425466]">
44
+ <span class="text-[#f5a623]">★</span>
45
+ <span class="text-[#0a2540] font-medium tabular-nums">{product.rating.toFixed(1)}</span>
46
+ <span class="text-[#425466]">({product.reviewsCount} reviews)</span>
48
47
  </div>
49
48
  </div>
50
49
 
51
- <h1 class="text-3xl sm:text-4xl font-semibold text-white tracking-[-0.025em] mb-4">
50
+ <h1 class="text-3xl sm:text-4xl font-light text-[#0a2540] tracking-tight mb-4">
52
51
  {product.name}
53
52
  </h1>
54
53
 
55
- <div class="text-3xl font-semibold text-white mb-6 tabular-nums">
54
+ <div class="text-3xl font-mono text-[#0a2540] font-light mb-6 tabular-nums">
56
55
  ${product.price.toFixed(2)}
57
56
  </div>
58
57
 
59
- <p class="text-zinc-300 text-sm sm:text-base leading-relaxed mb-8">
58
+ <p class="text-[#425466] text-sm sm:text-base leading-relaxed mb-8">
60
59
  {product.description}
61
60
  </p>
62
61
 
63
- <div class="border-t border-white/[0.08] pt-6 mb-8">
64
- <h2 class="text-xs uppercase tracking-wider font-semibold text-zinc-400 mb-4">
65
- Hardware Specifications
66
- </h2>
67
- <ul class="grid grid-cols-1 sm:grid-cols-2 gap-3 text-xs text-zinc-300">
68
- {product.features.map((f) => (
69
- <li key={f} class="flex items-center gap-2">
70
- <span class="w-1.5 h-1.5 rounded-full bg-zinc-400 shrink-0" />
71
- <span>{f}</span>
62
+ <div class="space-y-4 mb-8">
63
+ <h4 class="text-xs uppercase tracking-wider text-[#0a2540] font-semibold">Features</h4>
64
+ <ul class="space-y-2 text-xs text-[#425466]">
65
+ {product.features.map((feat) => (
66
+ <li key={feat} class="flex items-center gap-2">
67
+ <span class="text-[#24b47e]">✓</span>
68
+ <span>{feat}</span>
72
69
  </li>
73
70
  ))}
74
71
  </ul>
75
72
  </div>
76
73
 
77
- <div class="flex flex-col sm:flex-row sm:items-center gap-4 pt-2">
74
+ <div class="flex items-center gap-4">
78
75
  <a
79
76
  href="/cart"
80
- class="flex-1 py-3 px-6 rounded-xl bg-white text-zinc-950 font-medium text-center text-sm hover:bg-zinc-200 transition-all shadow-md flex items-center justify-center gap-2"
77
+ class="btn btn-p flex-1 py-3 text-sm font-medium"
81
78
  >
82
- <span>Add to Cart</span>
83
- <span class="text-zinc-500">•</span>
84
- <span class="tabular-nums font-semibold">${product.price.toFixed(2)}</span>
79
+ Add to Cart
85
80
  </a>
86
- <div class="flex items-center gap-2 text-xs text-zinc-400 px-3">
87
- <span class="w-2 h-2 rounded-full bg-emerald-400" />
88
- <span>{product.inStock ? 'In Stock · Ships in 24h' : 'Backorder'}</span>
81
+ <a
82
+ href="/cart"
83
+ class="btn btn-s py-3 px-6 text-sm font-medium"
84
+ >
85
+ Buy Now
86
+ </a>
87
+ </div>
88
+
89
+ <div class="mt-8 pt-6 border-t border-[#e6ebf1] grid grid-cols-2 gap-4 text-xs text-[#425466]">
90
+ <div>
91
+ <b class="text-[#0a2540] block">Free Shipping</b>
92
+ <span>Orders over $75 qualify</span>
93
+ </div>
94
+ <div>
95
+ <b class="text-[#0a2540] block">30-Day Returns</b>
96
+ <span>No restocking fee</span>
89
97
  </div>
90
98
  </div>
91
99
  </div>
@@ -7,61 +7,129 @@ export function CartDrawer() {
7
7
  ];
8
8
 
9
9
  const subtotal = cartItems.reduce((acc, item) => acc + item.product.price * item.quantity, 0);
10
- const shipping = subtotal > 100 ? 0 : 15;
10
+ const freeShipThreshold = 75;
11
+ const isFreeShip = subtotal >= freeShipThreshold;
12
+ const shipping = isFreeShip ? 0 : 6.5;
11
13
  const total = subtotal + shipping;
12
14
 
13
15
  return (
14
- <div class="rounded-xl border border-white/[0.08] bg-[#0f1117] p-6 sm:p-8 shadow-xl">
15
- <h2 class="text-base font-semibold text-white mb-6 flex items-center justify-between border-b border-white/[0.08] pb-4">
16
- <span>Order Summary</span>
17
- <span class="text-xs text-zinc-400 font-medium px-2 py-0.5 rounded-full bg-white/[0.04] border border-white/[0.06] tabular-nums">
18
- {cartItems.length} items
19
- </span>
20
- </h2>
16
+ <div class="grid gap-8 lg:grid-cols-[1.3fr_1fr] items-start">
17
+ {/* Cart Items List */}
18
+ <div class="rounded-lg border border-[#e6ebf1] bg-white p-6 shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)]">
19
+ <div class="flex items-center justify-between pb-4 border-b border-[#e6ebf1]">
20
+ <h2 class="text-[18px] font-medium text-[#0a2540]">Your cart</h2>
21
+ <span class="bd bg-[#635bff] text-white">2 items</span>
22
+ </div>
21
23
 
22
- <div class="space-y-4 mb-6">
23
- {cartItems.map(({ product, quantity }) => (
24
- <div key={product.id} class="flex items-center gap-4 pb-4 border-b border-white/[0.06]">
25
- <img
26
- src={product.image}
27
- alt={product.name}
28
- class="w-14 h-14 rounded-lg object-cover bg-zinc-950/40 border border-white/[0.08]"
24
+ {/* Free Shipping Progress */}
25
+ <div class="mt-4 pb-4 border-b border-[#e6ebf1]">
26
+ <p class="text-[13px] text-[#425466]">
27
+ {isFreeShip ? '🎉 You unlocked free shipping!' : `Add $${(freeShipThreshold - subtotal).toFixed(2)} more for free shipping.`}
28
+ </p>
29
+ <div class="mt-2 h-2 overflow-hidden rounded-full bg-[#e6ebf1]">
30
+ <div
31
+ class="h-full rounded-full bg-[#635bff] transition-all"
32
+ style={`width: ${Math.min(100, (subtotal / freeShipThreshold) * 100)}%`}
29
33
  />
30
- <div class="flex-1 min-w-0">
31
- <h4 class="text-xs sm:text-sm font-medium text-zinc-100 truncate">{product.name}</h4>
32
- <div class="text-xs text-zinc-400 mt-0.5 tabular-nums">Qty: {quantity} × ${product.price.toFixed(2)}</div>
33
- </div>
34
- <div class="font-medium text-xs sm:text-sm text-white tabular-nums">
35
- ${(product.price * quantity).toFixed(2)}
36
- </div>
37
34
  </div>
38
- ))}
39
- </div>
40
-
41
- <div class="space-y-2.5 text-xs text-zinc-300 mb-6">
42
- <div class="flex justify-between">
43
- <span class="text-zinc-400">Subtotal</span>
44
- <span class="text-white font-medium tabular-nums">${subtotal.toFixed(2)}</span>
45
- </div>
46
- <div class="flex justify-between">
47
- <span class="text-zinc-400">Standard Shipping</span>
48
- <span class="text-emerald-400 font-medium">{shipping === 0 ? 'FREE' : `$${shipping.toFixed(2)}`}</span>
49
35
  </div>
50
- <div class="pt-3 border-t border-white/[0.08] flex justify-between text-sm text-white font-semibold">
51
- <span>Total</span>
52
- <span class="text-base text-white tabular-nums">${total.toFixed(2)}</span>
36
+
37
+ <div class="divide-y divide-[#e6ebf1]">
38
+ {cartItems.map(({ product, quantity }) => (
39
+ <div key={product.id} class="flex gap-4 py-4">
40
+ <img
41
+ src={product.image}
42
+ alt={product.name}
43
+ class="w-16 h-16 rounded-md object-cover border border-[#e6ebf1] shrink-0"
44
+ />
45
+ <div class="flex-1 min-w-0">
46
+ <div class="flex justify-between items-start">
47
+ <h4 class="font-medium text-[#0a2540] text-sm truncate">{product.name}</h4>
48
+ <span class="font-mono text-sm text-[#0a2540] font-medium">
49
+ ${(product.price * quantity).toFixed(2)}
50
+ </span>
51
+ </div>
52
+ <div class="mt-3 flex items-center justify-between text-xs">
53
+ <div class="inline-flex items-center rounded-md border border-[#e6ebf1]">
54
+ <span class="px-2.5 py-1 text-[#425466]">Qty</span>
55
+ <span class="w-6 text-center font-mono font-medium text-[#0a2540]">{quantity}</span>
56
+ </div>
57
+ <span class="text-xs text-[#425466]">{product.category}</span>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ ))}
53
62
  </div>
54
63
  </div>
55
64
 
56
- <form action="/api/checkout" method="POST">
57
- <button
58
- type="submit"
59
- class="w-full py-3 rounded-lg bg-white text-zinc-950 font-medium text-xs hover:bg-zinc-200 transition-all shadow-sm flex items-center justify-center gap-2 group"
60
- >
61
- <span>Proceed to Checkout</span>
62
- <span class="text-zinc-500 group-hover:translate-x-0.5 transition-transform">→</span>
63
- </button>
64
- </form>
65
+ {/* Stripe Payment & Checkout Form */}
66
+ <div class="rounded-lg border border-[#e6ebf1] bg-white p-6 shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]">
67
+ <h3 class="text-[18px] font-medium text-[#0a2540] mb-4 pb-2 border-b border-[#e6ebf1]">
68
+ Payment & Checkout
69
+ </h3>
70
+
71
+ <form action="/api/checkout" method="POST" class="space-y-3.5">
72
+ <div>
73
+ <label class="block text-[13px] font-medium text-[#0a2540] mb-1" for="email">Email</label>
74
+ <input id="email" name="email" type="email" placeholder="you@company.com" class="inp" defaultValue="buyer@frootkit.dev" required />
75
+ </div>
76
+
77
+ <div>
78
+ <label class="block text-[13px] font-medium text-[#0a2540] mb-1" for="card">Card number</label>
79
+ <div class="relative">
80
+ <input
81
+ id="card"
82
+ name="cardNumber"
83
+ class="inp pr-20 font-mono text-[14px]"
84
+ defaultValue="4242 4242 4242 4242"
85
+ required
86
+ />
87
+ <span class="bd absolute right-2 top-2 bg-[#f6f9fc] text-[#425466] border border-[#e6ebf1] text-[11px]">
88
+ Visa
89
+ </span>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="grid grid-cols-2 gap-3">
94
+ <div>
95
+ <label class="block text-[13px] font-medium text-[#0a2540] mb-1" for="expiry">Expiry</label>
96
+ <input id="expiry" name="expiry" class="inp font-mono text-[14px]" defaultValue="12 / 28" required />
97
+ </div>
98
+ <div>
99
+ <label class="block text-[13px] font-medium text-[#0a2540] mb-1" for="cvc">CVC</label>
100
+ <input id="cvc" name="cvc" class="inp font-mono text-[14px]" defaultValue="123" required />
101
+ </div>
102
+ </div>
103
+
104
+ <div>
105
+ <label class="block text-[13px] font-medium text-[#0a2540] mb-1" for="zip">Shipping postcode</label>
106
+ <input id="zip" name="postcode" class="inp" defaultValue="94103" required />
107
+ </div>
108
+
109
+ <div class="pt-3 border-t border-[#e6ebf1] space-y-1.5 text-xs text-[#425466]">
110
+ <div class="flex justify-between">
111
+ <span>Subtotal</span>
112
+ <span class="font-mono text-[#0a2540]">${subtotal.toFixed(2)}</span>
113
+ </div>
114
+ <div class="flex justify-between">
115
+ <span>Shipping</span>
116
+ <span class="font-mono text-[#0a2540]">{shipping === 0 ? 'Free' : `$${shipping.toFixed(2)}`}</span>
117
+ </div>
118
+ <div class="flex justify-between text-base font-medium text-[#0a2540] pt-2 border-t border-[#e6ebf1]">
119
+ <span>Total</span>
120
+ <span class="font-mono">${total.toFixed(2)}</span>
121
+ </div>
122
+ </div>
123
+
124
+ <button type="submit" class="btn btn-p w-full py-3 mt-4 text-sm font-medium">
125
+ Pay ${total.toFixed(2)}
126
+ </button>
127
+
128
+ <p class="text-center text-[11px] text-[#425466] mt-2">
129
+ Test card 4242 4242 4242 4242, any future date. Zero real money moves.
130
+ </p>
131
+ </form>
132
+ </div>
65
133
  </div>
66
134
  );
67
135
  }
@@ -1,14 +1,16 @@
1
1
  export function Footer() {
2
2
  return (
3
- <footer class="border-t border-white/[0.08] bg-[#090a0f] py-8 text-xs text-zinc-500">
4
- <div class="max-w-6xl mx-auto px-6 flex flex-col sm:flex-row items-center justify-between gap-4">
5
- <div>
6
- FrootStore © {new Date().getFullYear()} OrbitNorth LLC. Built with FrootKit.
7
- </div>
8
- <div class="flex items-center gap-6">
9
- <a href="https://github.com/orbitnorth/frootkit" class="hover:text-zinc-300 transition-colors">GitHub</a>
10
- <a href="https://frootjs.org" class="hover:text-zinc-300 transition-colors">Documentation</a>
11
- <a href="/api/products" class="hover:text-zinc-300 transition-colors font-mono text-[11px]">/api/products</a>
3
+ <footer class="border-t border-[#e6ebf1] py-10 text-[14px] bg-white">
4
+ <div class="mx-auto max-w-6xl px-6 flex flex-wrap justify-between gap-4 text-xs text-[#425466]">
5
+ <span>
6
+ <b class="text-[#635bff] font-semibold text-sm">parcel</b> · Built with FrootKit & Preact SSR.
7
+ </span>
8
+ <div class="flex items-center gap-4">
9
+ <a href="/#shop" class="hover:text-[#0a2540]">Shipping</a>
10
+ <span>·</span>
11
+ <a href="/#shop" class="hover:text-[#0a2540]">Returns</a>
12
+ <span>·</span>
13
+ <a href="/api/products" class="font-mono hover:text-[#0a2540]">/api/products</a>
12
14
  </div>
13
15
  </div>
14
16
  </footer>
@@ -1,44 +1,42 @@
1
1
  export function Navbar() {
2
2
  return (
3
- <header class="border-b border-white/[0.08] bg-[#090a0f]/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-white/[0.06] border border-white/[0.1] flex items-center justify-center text-zinc-200 group-hover:border-white/[0.2] 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
- <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z" />
9
- <line x1="3" y1="6" x2="21" y2="6" />
10
- <path d="M16 10a4 4 0 0 1-8 0" />
3
+ <>
4
+ {/* Top Banner */}
5
+ <div class="bg-[#0a2540] py-2 text-center text-[13px] text-white">
6
+ Free shipping on orders over $75 · 30-day returns
7
+ </div>
8
+
9
+ {/* Main Sticky Navbar */}
10
+ <header class="sticky top-0 z-30 border-b border-[#e6ebf1] bg-white/90 backdrop-blur">
11
+ <div class="mx-auto max-w-6xl px-6 flex items-center gap-4 py-3">
12
+ <a href="/" class="text-[22px] font-semibold tracking-tight text-[#635bff]">
13
+ parcel
14
+ </a>
15
+
16
+ <nav class="ml-4 hidden gap-6 text-[14px] font-medium md:flex">
17
+ <a href="/#shop" class="text-[#0a2540] hover:text-[#635bff] transition-colors">Shop</a>
18
+ <a href="/#shop" class="text-[#425466] hover:text-[#635bff] transition-colors">Home</a>
19
+ <a href="/#shop" class="text-[#425466] hover:text-[#635bff] transition-colors">Office</a>
20
+ <a href="/#shop" class="text-[#425466] hover:text-[#635bff] transition-colors">Tech</a>
21
+ </nav>
22
+
23
+ <div class="relative ml-auto hidden w-64 sm:block">
24
+ <svg class="absolute left-3 top-2.5 h-4 w-4 text-[#425466]/60" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
25
+ <circle cx="11" cy="11" r="7" />
26
+ <path d="m20 20-3-3" />
11
27
  </svg>
28
+ <input class="inp pl-9" placeholder="Search products" aria-label="Search products" />
12
29
  </div>
13
- <span>Froot<span class="text-zinc-400 font-normal">Store</span></span>
14
- </a>
15
30
 
16
- <nav class="flex items-center gap-5 text-xs font-medium">
17
- <a href="/#catalog" class="text-zinc-400 hover:text-zinc-200 transition-colors">
18
- Catalog
19
- </a>
20
- <a
21
- href="/api/products"
22
- class="text-zinc-400 hover:text-zinc-200 font-mono text-[11px] px-2 py-0.5 rounded bg-white/[0.03] border border-white/[0.06] transition-colors"
23
- >
24
- /api/products
25
- </a>
26
- <a
27
- href="/cart"
28
- class="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white/[0.05] border border-white/[0.08] text-zinc-200 hover:text-white hover:bg-white/[0.08] transition-all text-xs font-medium"
29
- >
30
- <svg style="width: 13px; height: 13px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
31
- <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z" />
32
- <line x1="3" y1="6" x2="21" y2="6" />
33
- <path d="M16 10a4 4 0 0 1-8 0" />
31
+ <a href="/cart" class="btn btn-s relative ml-auto sm:ml-0" aria-label="Open cart">
32
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
33
+ <path d="M5 7h14l-1.5 11h-11zM9 7a3 3 0 0 1 6 0" stroke-linejoin="round" />
34
34
  </svg>
35
35
  <span>Cart</span>
36
- <span class="px-1.5 py-0.2 rounded-full bg-white text-zinc-950 font-bold text-[10px] tabular-nums">
37
- 2
38
- </span>
36
+ <span class="bd bg-[#635bff] text-white">2</span>
39
37
  </a>
40
- </nav>
41
- </div>
42
- </header>
38
+ </div>
39
+ </header>
40
+ </>
43
41
  );
44
42
  }