create-frootkit 1.2.12 → 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.
- package/package.json +1 -1
- package/templates/auth/src/app/dashboard/layout.tsx +8 -11
- package/templates/auth/src/app/dashboard/page.tsx +31 -27
- package/templates/auth/src/app/layout.tsx +5 -5
- package/templates/auth/src/app/login/page.tsx +11 -4
- package/templates/auth/src/app/page.tsx +278 -120
- package/templates/auth/src/app/signup/page.tsx +12 -6
- package/templates/auth/src/components/AuthForm.tsx +27 -34
- package/templates/auth/src/components/DashboardNav.tsx +41 -61
- package/templates/auth/src/components/MetricCard.tsx +12 -22
- package/templates/auth/src/components/Navbar.tsx +18 -21
- package/templates/auth/src/styles.css +157 -27
- package/templates/blog/src/app/blog/[slug]/page.tsx +85 -46
- package/templates/blog/src/app/layout.tsx +5 -5
- package/templates/blog/src/app/page.tsx +39 -26
- package/templates/blog/src/components/Footer.tsx +11 -9
- package/templates/blog/src/components/Navbar.tsx +21 -26
- package/templates/blog/src/components/Newsletter.tsx +11 -33
- package/templates/blog/src/components/PostCard.tsx +43 -76
- package/templates/blog/src/styles.css +113 -25
- package/templates/ecommerce/src/app/cart/page.tsx +12 -4
- package/templates/ecommerce/src/app/layout.tsx +5 -5
- package/templates/ecommerce/src/app/page.tsx +92 -37
- package/templates/ecommerce/src/app/products/[id]/page.tsx +39 -31
- package/templates/ecommerce/src/components/CartDrawer.tsx +113 -45
- package/templates/ecommerce/src/components/Footer.tsx +11 -9
- package/templates/ecommerce/src/components/Navbar.tsx +32 -34
- package/templates/ecommerce/src/components/ProductCard.tsx +27 -38
- package/templates/ecommerce/src/styles.css +155 -36
- package/templates/starter/src/app/layout.tsx +5 -5
- package/templates/starter/src/app/page.tsx +114 -107
- package/templates/starter/src/components/FeatureCard.tsx +9 -15
- package/templates/starter/src/components/Footer.tsx +11 -9
- package/templates/starter/src/components/Header.tsx +15 -38
- package/templates/starter/src/styles.css +102 -27
|
@@ -2,59 +2,48 @@ import type { Product } from '../data/products.js';
|
|
|
2
2
|
|
|
3
3
|
export function ProductCard({ product }: { product: Product }) {
|
|
4
4
|
return (
|
|
5
|
-
<
|
|
6
|
-
{
|
|
7
|
-
<a href={`/products/${product.id}`} class="block relative aspect-[4/3] overflow-hidden bg-zinc-950/60">
|
|
5
|
+
<article class="group">
|
|
6
|
+
<a href={`/products/${product.id}`} class="relative block w-full overflow-hidden rounded-lg aspect-square bg-[#f6f9fc] border border-[#e6ebf1]">
|
|
8
7
|
<img
|
|
9
8
|
src={product.image}
|
|
10
9
|
alt={product.name}
|
|
11
|
-
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-
|
|
10
|
+
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
|
12
11
|
loading="lazy"
|
|
13
12
|
/>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
{product.inStock && (
|
|
14
|
+
<span class="bd absolute left-3 top-3 bg-white text-[#635bff] shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)]">
|
|
15
|
+
{product.category}
|
|
16
|
+
</span>
|
|
17
|
+
)}
|
|
18
|
+
<span class="absolute inset-x-3 bottom-3 rounded-md bg-white/95 py-1.5 text-center text-[13px] font-medium text-[#0a2540] opacity-0 shadow-[0_2px_5px_-1px_rgba(50,50,93,0.25),0_1px_3px_-1px_rgba(0,0,0,0.3)] transition-opacity group-hover:opacity-100">
|
|
19
|
+
Quick view
|
|
17
20
|
</span>
|
|
18
21
|
</a>
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
<div class="p-5 flex-1 flex flex-col justify-between">
|
|
23
|
+
<div class="mt-3 flex items-start justify-between gap-2">
|
|
22
24
|
<div>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<span class="text-amber-400 text-xs">★</span>
|
|
26
|
-
<span class="text-zinc-200 tabular-nums">{product.rating.toFixed(1)}</span>
|
|
27
|
-
<span class="text-zinc-600">•</span>
|
|
28
|
-
<span class="text-zinc-500 tabular-nums">{product.reviewsCount} reviews</span>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<h3 class="font-semibold text-zinc-100 text-sm mb-2 group-hover:text-white transition-colors line-clamp-1">
|
|
32
|
-
<a href={`/products/${product.id}`}>
|
|
25
|
+
<h3 class="font-medium text-[#0a2540] line-clamp-1">
|
|
26
|
+
<a href={`/products/${product.id}`} class="hover:text-[#635bff] transition-colors">
|
|
33
27
|
{product.name}
|
|
34
28
|
</a>
|
|
35
29
|
</h3>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{product.description}
|
|
30
|
+
<p class="text-[13px] text-[#425466]">
|
|
31
|
+
<span class="text-[#f5a623]">★</span> {product.rating.toFixed(1)} · {product.reviewsCount} reviews
|
|
39
32
|
</p>
|
|
40
33
|
</div>
|
|
34
|
+
<p class="font-mono text-[14px] text-[#0a2540] shrink-0 font-medium">
|
|
35
|
+
${product.price.toFixed(2)}
|
|
36
|
+
</p>
|
|
37
|
+
</div>
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<a
|
|
50
|
-
href={`/products/${product.id}`}
|
|
51
|
-
class="px-3.5 py-1.5 rounded-lg bg-white text-zinc-950 font-medium text-xs hover:bg-zinc-200 transition-all shadow-sm flex items-center gap-1.5 group-hover:shadow"
|
|
52
|
-
>
|
|
53
|
-
<span>View</span>
|
|
54
|
-
<span class="text-zinc-500">→</span>
|
|
55
|
-
</a>
|
|
56
|
-
</div>
|
|
39
|
+
<div class="mt-2.5 flex items-center gap-2">
|
|
40
|
+
<a
|
|
41
|
+
href="/cart"
|
|
42
|
+
class="btn btn-s w-full text-center py-1.5 text-xs font-medium"
|
|
43
|
+
>
|
|
44
|
+
Add to cart
|
|
45
|
+
</a>
|
|
57
46
|
</div>
|
|
58
|
-
</
|
|
47
|
+
</article>
|
|
59
48
|
);
|
|
60
49
|
}
|
|
@@ -4,58 +4,177 @@
|
|
|
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
|
|
8
|
-
--color-brand-hover: #
|
|
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:
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
+
|
|
30
|
+
--shadow-card: 0 2px 5px -1px rgba(50, 50, 93, 0.25), 0 1px 3px -1px rgba(0, 0, 0, 0.3);
|
|
31
|
+
--shadow-pop: 0 13px 27px -5px rgba(50, 50, 93, 0.25), 0 8px 16px -8px rgba(0, 0, 0, 0.3);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
body {
|
|
35
|
+
font-size: 15px;
|
|
36
|
+
line-height: 24px;
|
|
37
|
+
background-color: #ffffff;
|
|
38
|
+
color: #425466;
|
|
28
39
|
-webkit-font-smoothing: antialiased;
|
|
29
40
|
-moz-osx-font-smoothing: grayscale;
|
|
30
41
|
}
|
|
31
42
|
|
|
43
|
+
:focus-visible {
|
|
44
|
+
outline: 2px solid #635bff;
|
|
45
|
+
outline-offset: 2px;
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
/* Strict SVG safety to prevent flex ballooning */
|
|
33
49
|
svg {
|
|
34
50
|
flex-shrink: 0;
|
|
35
51
|
max-width: 100%;
|
|
36
52
|
}
|
|
37
53
|
|
|
38
|
-
/* Stripe
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
/* Stripe Design Tokens & Utility Classes */
|
|
55
|
+
.btn {
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
gap: 0.5rem;
|
|
60
|
+
border-radius: 6px;
|
|
61
|
+
padding: 0.5rem 1rem;
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
font-weight: 500;
|
|
64
|
+
transition: all 150ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
}
|
|
67
|
+
.btn:disabled {
|
|
68
|
+
opacity: 0.5;
|
|
69
|
+
cursor: not-allowed;
|
|
45
70
|
}
|
|
46
71
|
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
box-shadow:
|
|
72
|
+
.btn-p {
|
|
73
|
+
background-color: #635bff;
|
|
74
|
+
color: #ffffff;
|
|
75
|
+
box-shadow: var(--shadow-card);
|
|
76
|
+
}
|
|
77
|
+
.btn-p:hover:not(:disabled) {
|
|
78
|
+
background-color: #5249e6;
|
|
51
79
|
}
|
|
52
80
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
.btn-s {
|
|
82
|
+
border: 1px solid #e6ebf1;
|
|
83
|
+
background-color: #ffffff;
|
|
84
|
+
color: #0a2540;
|
|
85
|
+
box-shadow: var(--shadow-card);
|
|
86
|
+
}
|
|
87
|
+
.btn-s:hover:not(:disabled) {
|
|
88
|
+
background-color: #f6f9fc;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.btn-n {
|
|
92
|
+
background-color: #0a2540;
|
|
93
|
+
color: #ffffff;
|
|
94
|
+
box-shadow: var(--shadow-card);
|
|
95
|
+
}
|
|
96
|
+
.btn-n:hover:not(:disabled) {
|
|
97
|
+
background-color: #425466;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.inp {
|
|
101
|
+
width: 100%;
|
|
102
|
+
border-radius: 6px;
|
|
103
|
+
border: 1px solid #e6ebf1;
|
|
104
|
+
background-color: #ffffff;
|
|
105
|
+
padding: 0.5rem 0.75rem;
|
|
106
|
+
font-size: 15px;
|
|
107
|
+
color: #0a2540;
|
|
108
|
+
outline: none;
|
|
109
|
+
transition: all 150ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
110
|
+
}
|
|
111
|
+
.inp:focus {
|
|
112
|
+
border-color: #635bff;
|
|
113
|
+
box-shadow: 0 0 0 3px rgba(99, 91, 255, 0.25);
|
|
114
|
+
}
|
|
115
|
+
.inp.bad {
|
|
116
|
+
border-color: #cd3d64;
|
|
117
|
+
}
|
|
118
|
+
.inp.bad:focus {
|
|
119
|
+
box-shadow: 0 0 0 3px rgba(205, 61, 100, 0.2);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.bd {
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
border-radius: 9999px;
|
|
126
|
+
padding: 0.125rem 0.625rem;
|
|
127
|
+
font-size: 12px;
|
|
128
|
+
font-weight: 500;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.shadow-card {
|
|
132
|
+
box-shadow: var(--shadow-card);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.shadow-pop {
|
|
136
|
+
box-shadow: var(--shadow-pop);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
dialog {
|
|
140
|
+
margin: auto;
|
|
141
|
+
width: calc(100% - 2rem);
|
|
142
|
+
max-width: 32rem;
|
|
143
|
+
border-radius: 8px;
|
|
144
|
+
border: 0;
|
|
145
|
+
background-color: #ffffff;
|
|
146
|
+
padding: 1.5rem;
|
|
147
|
+
color: #425466;
|
|
148
|
+
box-shadow: var(--shadow-pop);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
dialog::backdrop {
|
|
152
|
+
background: rgba(10, 37, 64, 0.5);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
dialog[open] {
|
|
156
|
+
animation: dlg 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@keyframes dlg {
|
|
160
|
+
from {
|
|
161
|
+
opacity: 0;
|
|
162
|
+
transform: translateY(8px);
|
|
60
163
|
}
|
|
61
164
|
}
|
|
165
|
+
|
|
166
|
+
dialog.drawer {
|
|
167
|
+
margin-top: 0;
|
|
168
|
+
margin-bottom: 0;
|
|
169
|
+
margin-right: 0;
|
|
170
|
+
margin-left: auto;
|
|
171
|
+
display: flex;
|
|
172
|
+
height: 100%;
|
|
173
|
+
max-height: none;
|
|
174
|
+
width: 420px;
|
|
175
|
+
max-width: 100%;
|
|
176
|
+
flex-direction: column;
|
|
177
|
+
border-radius: 0;
|
|
178
|
+
border-top-left-radius: 8px;
|
|
179
|
+
border-bottom-left-radius: 8px;
|
|
180
|
+
}
|
|
@@ -9,13 +9,13 @@ export interface RootLayoutProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export default function RootLayout({ children, metadata }: RootLayoutProps) {
|
|
12
|
-
const title = metadata?.title || 'FrootKit —
|
|
12
|
+
const title = metadata?.title || 'FrootKit — The Preact Fullstack Framework';
|
|
13
13
|
const description =
|
|
14
14
|
metadata?.description ||
|
|
15
|
-
'Sub-millisecond Preact SSR,
|
|
15
|
+
'Sub-millisecond Preact SSR, native Tailwind CSS v4, and App Router conventions.';
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
|
-
<html lang="en" class="
|
|
18
|
+
<html lang="en" class="scroll-smooth">
|
|
19
19
|
<head>
|
|
20
20
|
<meta charSet="UTF-8" />
|
|
21
21
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
@@ -24,12 +24,12 @@ export default function RootLayout({ children, metadata }: RootLayoutProps) {
|
|
|
24
24
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
25
25
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
|
26
26
|
<link
|
|
27
|
-
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600
|
|
27
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
28
28
|
rel="stylesheet"
|
|
29
29
|
/>
|
|
30
30
|
<link rel="stylesheet" href="/styles.css" />
|
|
31
31
|
</head>
|
|
32
|
-
<body class="bg-
|
|
32
|
+
<body class="bg-white font-sans text-[#425466] antialiased">
|
|
33
33
|
{children}
|
|
34
34
|
</body>
|
|
35
35
|
</html>
|
|
@@ -1,129 +1,136 @@
|
|
|
1
1
|
import { Header } from '../components/Header.js';
|
|
2
2
|
import { FeatureCard } from '../components/FeatureCard.js';
|
|
3
|
-
import { TerminalPreview } from '../components/TerminalPreview.js';
|
|
4
3
|
import { Footer } from '../components/Footer.js';
|
|
5
4
|
|
|
6
5
|
export const metadata = {
|
|
7
|
-
title: 'FrootKit —
|
|
8
|
-
description: 'Sub-millisecond Preact SSR,
|
|
6
|
+
title: 'FrootKit — The Fullstack Preact Framework',
|
|
7
|
+
description: 'Sub-millisecond Preact SSR, native Tailwind CSS v4, and App Router conventions.',
|
|
9
8
|
};
|
|
10
9
|
|
|
11
|
-
export default function
|
|
10
|
+
export default function StarterPage() {
|
|
11
|
+
const features = [
|
|
12
|
+
{
|
|
13
|
+
title: 'Sub-Millisecond SSR',
|
|
14
|
+
description: 'Preact VNode rendering tuned for ultra-fast TTFB and zero unnecessary hydration overhead.',
|
|
15
|
+
icon: (
|
|
16
|
+
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
17
|
+
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
|
|
18
|
+
</svg>
|
|
19
|
+
),
|
|
20
|
+
badge: '0.42ms TTFB',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
title: 'App Router Conventions',
|
|
24
|
+
description: 'Nested layouts, dynamic segments, loading boundaries, and standard web Request/Response route handlers.',
|
|
25
|
+
icon: (
|
|
26
|
+
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
27
|
+
<rect x="3" y="3" width="7" height="7" />
|
|
28
|
+
<rect x="14" y="3" width="7" height="7" />
|
|
29
|
+
<rect x="14" y="14" width="7" height="7" />
|
|
30
|
+
<rect x="3" y="14" width="7" height="7" />
|
|
31
|
+
</svg>
|
|
32
|
+
),
|
|
33
|
+
badge: 'Next.js Parity',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'Stripe Design System',
|
|
37
|
+
description: 'Polished FrootUI component library with multi-layer blue shadows and semantic tints built-in.',
|
|
38
|
+
icon: (
|
|
39
|
+
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
40
|
+
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
|
41
|
+
</svg>
|
|
42
|
+
),
|
|
43
|
+
badge: 'Zero Slop',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
12
47
|
return (
|
|
13
|
-
<div class="
|
|
48
|
+
<div class="bg-white font-sans text-[#425466]">
|
|
14
49
|
<Header />
|
|
15
50
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<div class="max-w-
|
|
19
|
-
<div class="
|
|
20
|
-
<span class="
|
|
21
|
-
<
|
|
51
|
+
{/* HERO SECTION */}
|
|
52
|
+
<section class="hero relative pb-44 pt-32 text-white md:pb-56">
|
|
53
|
+
<div class="mx-auto max-w-6xl px-6 grid items-center gap-14 lg:grid-cols-2">
|
|
54
|
+
<div class="rise">
|
|
55
|
+
<span class="bd bg-white/15 text-white">FrootKit 1.2 · Stripe Design System</span>
|
|
56
|
+
<h1 class="mt-5 text-[42px] font-light leading-[1.08] tracking-tight md:text-[56px] md:leading-[64px]">
|
|
57
|
+
The web framework built for precision engineering
|
|
58
|
+
</h1>
|
|
59
|
+
<p class="mt-6 max-w-md text-[18px] font-light leading-7 text-white/90">
|
|
60
|
+
Zero-bloat Preact SSR, native Tailwind CSS v4, and standard Web Crypto APIs packaged into a high-performance developer runtime.
|
|
61
|
+
</p>
|
|
62
|
+
<div class="mt-8 flex flex-wrap gap-3">
|
|
63
|
+
<a href="https://frootjs.org" class="btn btn-w">Read the Documentation</a>
|
|
64
|
+
<a href="/api/health" class="btn btn-gl font-mono text-xs">Test Health API</a>
|
|
65
|
+
</div>
|
|
66
|
+
<p class="mt-4 text-[13px] text-white/75">
|
|
67
|
+
Edit <code class="font-mono bg-white/10 px-1.5 py-0.5 rounded">src/app/page.tsx</code> to start building your application.
|
|
68
|
+
</p>
|
|
22
69
|
</div>
|
|
23
70
|
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
71
|
+
<div class="rise overflow-hidden rounded-lg bg-[#06182b] text-white/90 shadow-[0_13px_27px_-5px_rgba(50,50,93,0.25),0_8px_16px_-8px_rgba(0,0,0,0.3)]">
|
|
72
|
+
<div class="flex items-center gap-1.5 border-b border-white/10 bg-[#0a2540] px-4 py-2.5">
|
|
73
|
+
<i class="h-2.5 w-2.5 rounded-full bg-white/20" />
|
|
74
|
+
<i class="h-2.5 w-2.5 rounded-full bg-white/20" />
|
|
75
|
+
<i class="h-2.5 w-2.5 rounded-full bg-white/20" />
|
|
76
|
+
<span class="ml-3 font-mono text-[12px] text-white/70">terminal ~ frootkit dev</span>
|
|
77
|
+
</div>
|
|
78
|
+
<pre class="p-5 font-mono text-[13px] leading-6 overflow-x-auto">
|
|
79
|
+
<code>{`$ npx create-frootkit my-app
|
|
80
|
+
$ cd my-app && npm run dev
|
|
27
81
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
82
|
+
⚡ FrootKit v1.2.12 [Preact SSR Engine]
|
|
83
|
+
- Local: http://localhost:3000
|
|
84
|
+
- Network: http://192.168.1.5:3000
|
|
31
85
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
rel="noreferrer"
|
|
37
|
-
class="px-4 py-2.5 rounded-lg bg-white text-zinc-950 font-medium hover:bg-zinc-200 transition-all text-sm shadow-sm flex items-center gap-2 group"
|
|
38
|
-
>
|
|
39
|
-
<span>Explore Documentation</span>
|
|
40
|
-
<span class="text-zinc-500 group-hover:translate-x-0.5 transition-transform">→</span>
|
|
41
|
-
</a>
|
|
42
|
-
<a
|
|
43
|
-
href="https://github.com/orbitnorth/frootkit"
|
|
44
|
-
target="_blank"
|
|
45
|
-
rel="noreferrer"
|
|
46
|
-
class="px-4 py-2.5 rounded-lg bg-white/[0.04] border border-white/[0.08] text-zinc-300 font-medium hover:bg-white/[0.08] hover:text-white transition-all text-sm flex items-center gap-2"
|
|
47
|
-
>
|
|
48
|
-
<svg style="width: 14px; height: 14px;" class="fill-current shrink-0" viewBox="0 0 24 24">
|
|
49
|
-
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
50
|
-
</svg>
|
|
51
|
-
<span>Star on GitHub</span>
|
|
52
|
-
</a>
|
|
86
|
+
✓ Compiled src/app/page.tsx in 1.2ms
|
|
87
|
+
✓ Tailwind CSS v4 styles active
|
|
88
|
+
✓ Zero hydration mismatch`}</code>
|
|
89
|
+
</pre>
|
|
53
90
|
</div>
|
|
54
91
|
</div>
|
|
92
|
+
</section>
|
|
55
93
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
description="Zero postcss or tailwind config files required. Native Tailwind CSS v4 compiles in single-digit milliseconds."
|
|
74
|
-
>
|
|
75
|
-
<svg style="width: 16px; height: 16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
76
|
-
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
|
77
|
-
</svg>
|
|
78
|
-
</FeatureCard>
|
|
79
|
-
|
|
80
|
-
<FeatureCard
|
|
81
|
-
badge="App Router"
|
|
82
|
-
title="Nested File Layouts"
|
|
83
|
-
description="Intuitive file-based routing with layout.tsx, page.tsx, and loading.tsx mirroring modern Next.js conventions."
|
|
84
|
-
>
|
|
85
|
-
<svg style="width: 16px; height: 16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
86
|
-
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
|
87
|
-
<line x1="9" y1="3" x2="9" y2="21" />
|
|
88
|
-
</svg>
|
|
89
|
-
</FeatureCard>
|
|
90
|
-
|
|
91
|
-
<FeatureCard
|
|
92
|
-
badge="Fullstack API"
|
|
93
|
-
title="Dynamic Route Handlers"
|
|
94
|
-
description="First-class Web Standard Request/Response handlers inside src/app/api/**/route.ts with JSON and SSE streaming."
|
|
95
|
-
>
|
|
96
|
-
<svg style="width: 16px; height: 16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
97
|
-
<polyline points="16 18 22 12 16 6" />
|
|
98
|
-
<polyline points="8 6 2 12 8 18" />
|
|
99
|
-
</svg>
|
|
100
|
-
</FeatureCard>
|
|
101
|
-
|
|
102
|
-
<FeatureCard
|
|
103
|
-
badge="Pure Web Standards"
|
|
104
|
-
title="Zero C++ Dependencies"
|
|
105
|
-
description="Built entirely on pure ECMAScript and Web Crypto. Clean deployments to Node.js, Bun, and Edge runners."
|
|
106
|
-
>
|
|
107
|
-
<svg style="width: 16px; height: 16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
108
|
-
<circle cx="12" cy="12" r="10" />
|
|
109
|
-
<line x1="2" y1="12" x2="22" y2="12" />
|
|
110
|
-
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
|
111
|
-
</svg>
|
|
112
|
-
</FeatureCard>
|
|
94
|
+
{/* STATS */}
|
|
95
|
+
<section class="bg-[#f6f9fc] py-14">
|
|
96
|
+
<div class="mx-auto max-w-6xl px-6 grid gap-8 text-center sm:grid-cols-3">
|
|
97
|
+
<div>
|
|
98
|
+
<p class="font-mono text-[40px] font-light text-[#0a2540]">0.42ms</p>
|
|
99
|
+
<p class="text-[14px]">Sub-millisecond server rendering</p>
|
|
100
|
+
</div>
|
|
101
|
+
<div>
|
|
102
|
+
<p class="font-mono text-[40px] font-light text-[#0a2540]">3.2 kB</p>
|
|
103
|
+
<p class="text-[14px]">Ultra-lean Preact client runtime</p>
|
|
104
|
+
</div>
|
|
105
|
+
<div>
|
|
106
|
+
<p class="font-mono text-[40px] font-light text-[#0a2540]">100%</p>
|
|
107
|
+
<p class="text-[14px]">Web Standards API compatibility</p>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</section>
|
|
113
111
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
112
|
+
{/* FEATURES */}
|
|
113
|
+
<section id="features" class="scroll-mt-16 py-24">
|
|
114
|
+
<div class="mx-auto max-w-6xl px-6">
|
|
115
|
+
<h2 class="text-[32px] font-light leading-[1.15] tracking-tight text-[#0a2540] md:text-[40px] max-w-2xl">
|
|
116
|
+
Engineered for high velocity, built with zero AI slop
|
|
117
|
+
</h2>
|
|
118
|
+
<p class="mt-4 max-w-xl text-[17px] leading-7">
|
|
119
|
+
Every token, font, shadow, and component has been modeled after Stripe's award-winning design system.
|
|
120
|
+
</p>
|
|
121
|
+
<div class="mt-12 grid gap-6 md:grid-cols-3">
|
|
122
|
+
{features.map((f) => (
|
|
123
|
+
<FeatureCard
|
|
124
|
+
key={f.title}
|
|
125
|
+
title={f.title}
|
|
126
|
+
description={f.description}
|
|
127
|
+
icon={f.icon}
|
|
128
|
+
badge={f.badge}
|
|
129
|
+
/>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
125
132
|
</div>
|
|
126
|
-
</
|
|
133
|
+
</section>
|
|
127
134
|
|
|
128
135
|
<Footer />
|
|
129
136
|
</div>
|
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
import type { ComponentChildren } from 'preact';
|
|
2
2
|
|
|
3
3
|
export interface FeatureCardProps {
|
|
4
|
-
badge: string;
|
|
5
4
|
title: string;
|
|
6
5
|
description: string;
|
|
7
|
-
|
|
6
|
+
icon?: ComponentChildren;
|
|
7
|
+
badge?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function FeatureCard({
|
|
10
|
+
export function FeatureCard({ title, description, icon, badge }: FeatureCardProps) {
|
|
11
11
|
return (
|
|
12
|
-
<div class="rounded-
|
|
12
|
+
<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)] 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)]">
|
|
13
13
|
<div class="flex items-center justify-between mb-4">
|
|
14
|
-
<
|
|
15
|
-
{
|
|
16
|
-
</div>
|
|
17
|
-
<span class="text-[11px] font-medium px-2 py-0.5 rounded-full bg-white/[0.04] text-zinc-300 border border-white/[0.06]">
|
|
18
|
-
{badge}
|
|
14
|
+
<span class="grid h-10 w-10 place-items-center rounded-md bg-[#635bff]/10 text-[#635bff]">
|
|
15
|
+
{icon}
|
|
19
16
|
</span>
|
|
17
|
+
{badge && <span class="bd bg-[#635bff]/10 text-[#635bff]">{badge}</span>}
|
|
20
18
|
</div>
|
|
21
|
-
<h3 class="text-
|
|
22
|
-
|
|
23
|
-
</h3>
|
|
24
|
-
<p class="text-zinc-400 text-xs leading-relaxed">
|
|
25
|
-
{description}
|
|
26
|
-
</p>
|
|
19
|
+
<h3 class="text-[17px] font-medium text-[#0a2540]">{title}</h3>
|
|
20
|
+
<p class="mt-1 text-[14px] text-[#425466] leading-relaxed">{description}</p>
|
|
27
21
|
</div>
|
|
28
22
|
);
|
|
29
23
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
export function Footer() {
|
|
2
2
|
return (
|
|
3
|
-
<footer class="border-t border-
|
|
4
|
-
<div class="max-w-6xl
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
</
|
|
8
|
-
<div class="flex items-center gap-
|
|
9
|
-
<a href="https://
|
|
10
|
-
<
|
|
11
|
-
<a href="/
|
|
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">frootkit</b> · High-performance Preact fullstack web framework.
|
|
7
|
+
</span>
|
|
8
|
+
<div class="flex items-center gap-4">
|
|
9
|
+
<a href="https://frootjs.org" class="hover:text-[#0a2540]">Docs</a>
|
|
10
|
+
<span>·</span>
|
|
11
|
+
<a href="https://github.com/orbitnorth/frootkit" class="hover:text-[#0a2540]">GitHub</a>
|
|
12
|
+
<span>·</span>
|
|
13
|
+
<a href="/api/health" class="font-mono hover:text-[#0a2540]">/api/health</a>
|
|
12
14
|
</div>
|
|
13
15
|
</div>
|
|
14
16
|
</footer>
|