create-stitchkit 0.1.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 (174) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/LICENSE +21 -0
  3. package/README.md +35 -0
  4. package/dist/cli.js +230 -0
  5. package/package.json +50 -0
  6. package/template/README.md +69 -0
  7. package/template/_env +11 -0
  8. package/template/_env.example +12 -0
  9. package/template/_gitignore +10 -0
  10. package/template/biome.json +43 -0
  11. package/template/bun.lock +1372 -0
  12. package/template/e2e/starter.spec.ts +237 -0
  13. package/template/ecosystem.config.cjs +28 -0
  14. package/template/ecosystem.dev.config.cjs +29 -0
  15. package/template/package.json +55 -0
  16. package/template/packages/backend/package.json +31 -0
  17. package/template/packages/backend/src/cli.ts +12 -0
  18. package/template/packages/backend/src/domain/errors.ts +9 -0
  19. package/template/packages/backend/src/domain/repository/github-cache.test.ts +72 -0
  20. package/template/packages/backend/src/domain/repository/github-cache.ts +205 -0
  21. package/template/packages/backend/src/index.ts +56 -0
  22. package/template/packages/backend/src/lib/db.ts +7 -0
  23. package/template/packages/backend/src/surface.ts +13 -0
  24. package/template/packages/backend/src/tools.ts +21 -0
  25. package/template/packages/backend/src/transport/errors.ts +28 -0
  26. package/template/packages/backend/src/transport/repository-service.ts +18 -0
  27. package/template/packages/backend/tsconfig.json +8 -0
  28. package/template/packages/config/package.json +22 -0
  29. package/template/packages/config/src/server.ts +24 -0
  30. package/template/packages/config/tsconfig.json +5 -0
  31. package/template/packages/db/migrations/20260808000000_init/migration.sql +16 -0
  32. package/template/packages/db/migrations/20260808170000_repository_visibility/migration.sql +4 -0
  33. package/template/packages/db/migrations/migration_lock.toml +1 -0
  34. package/template/packages/db/package.json +29 -0
  35. package/template/packages/db/prisma.config.ts +8 -0
  36. package/template/packages/db/schema.prisma +31 -0
  37. package/template/packages/db/tsconfig.json +5 -0
  38. package/template/packages/frontend/messages/en.json +15 -0
  39. package/template/packages/frontend/messages/ru.json +15 -0
  40. package/template/packages/frontend/next.config.ts +18 -0
  41. package/template/packages/frontend/package.json +62 -0
  42. package/template/packages/frontend/postcss.config.mjs +1 -0
  43. package/template/packages/frontend/public/ASSETS.md +6 -0
  44. package/template/packages/frontend/public/favicon/mascot-stitch.png +0 -0
  45. package/template/packages/frontend/public/icons/biome.svg +1 -0
  46. package/template/packages/frontend/public/icons/bun.svg +19 -0
  47. package/template/packages/frontend/public/icons/next.svg +1 -0
  48. package/template/packages/frontend/public/icons/postgresql.svg +1 -0
  49. package/template/packages/frontend/public/icons/prisma.svg +6 -0
  50. package/template/packages/frontend/public/icons/react-query.svg +9 -0
  51. package/template/packages/frontend/public/icons/react.svg +1 -0
  52. package/template/packages/frontend/public/icons/tailwind.svg +12 -0
  53. package/template/packages/frontend/public/icons/tsconfig.svg +1 -0
  54. package/template/packages/frontend/public/icons/typescript.svg +7 -0
  55. package/template/packages/frontend/public/icons/zod.svg +1 -0
  56. package/template/packages/frontend/public/mascot-stitch.png +0 -0
  57. package/template/packages/frontend/public/theme-dark.svg +8 -0
  58. package/template/packages/frontend/public/theme-light.svg +8 -0
  59. package/template/packages/frontend/src/app/[locale]/error.tsx +19 -0
  60. package/template/packages/frontend/src/app/[locale]/layout.tsx +59 -0
  61. package/template/packages/frontend/src/app/[locale]/loading.tsx +14 -0
  62. package/template/packages/frontend/src/app/[locale]/page.tsx +44 -0
  63. package/template/packages/frontend/src/app/[locale]/starter-page.tsx +151 -0
  64. package/template/packages/frontend/src/app/[locale]/ui/[story]/page.tsx +40 -0
  65. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/account-showcase.tsx +112 -0
  66. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/admin-showcase.tsx +101 -0
  67. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/catalogue-navigation.tsx +56 -0
  68. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/catalogue-shell.tsx +51 -0
  69. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/code-foundation.tsx +35 -0
  70. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/components-story.tsx +54 -0
  71. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/data-story.tsx +126 -0
  72. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/image-preloader.tsx +8 -0
  73. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/index.ts +3 -0
  74. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/landing-sections.tsx +269 -0
  75. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/landing-showcase.tsx +159 -0
  76. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/motion-story.tsx +78 -0
  77. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/overlays-story.tsx +134 -0
  78. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/payment-showcase.tsx +82 -0
  79. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/presentation-story.tsx +68 -0
  80. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/primitives-story.tsx +267 -0
  81. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/story-page.tsx +14 -0
  82. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/theme-story-client.tsx +208 -0
  83. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/theme-story.tsx +13 -0
  84. package/template/packages/frontend/src/app/[locale]/ui/layout.tsx +6 -0
  85. package/template/packages/frontend/src/app/[locale]/ui/page.tsx +10 -0
  86. package/template/packages/frontend/src/app/api/og/[locale]/[page]/route.tsx +53 -0
  87. package/template/packages/frontend/src/app/globals.css +210 -0
  88. package/template/packages/frontend/src/app/robots.ts +9 -0
  89. package/template/packages/frontend/src/app/sitemap.ts +22 -0
  90. package/template/packages/frontend/src/components/brand-mark.tsx +22 -0
  91. package/template/packages/frontend/src/components/repository-summary.tsx +107 -0
  92. package/template/packages/frontend/src/components/system-controls.tsx +92 -0
  93. package/template/packages/frontend/src/components/ui/adaptive-modal.tsx +382 -0
  94. package/template/packages/frontend/src/components/ui/animated-counter.tsx +80 -0
  95. package/template/packages/frontend/src/components/ui/avatar.tsx +46 -0
  96. package/template/packages/frontend/src/components/ui/badge.tsx +94 -0
  97. package/template/packages/frontend/src/components/ui/button-variants.ts +24 -0
  98. package/template/packages/frontend/src/components/ui/button.tsx +81 -0
  99. package/template/packages/frontend/src/components/ui/card-deck.tsx +73 -0
  100. package/template/packages/frontend/src/components/ui/card.tsx +81 -0
  101. package/template/packages/frontend/src/components/ui/charts/area-chart.tsx +167 -0
  102. package/template/packages/frontend/src/components/ui/charts/index.ts +7 -0
  103. package/template/packages/frontend/src/components/ui/checkbox.tsx +133 -0
  104. package/template/packages/frontend/src/components/ui/confirmation-modal.tsx +129 -0
  105. package/template/packages/frontend/src/components/ui/copyable-text.tsx +85 -0
  106. package/template/packages/frontend/src/components/ui/drawer.tsx +136 -0
  107. package/template/packages/frontend/src/components/ui/dropdown-menu.tsx +185 -0
  108. package/template/packages/frontend/src/components/ui/field.tsx +28 -0
  109. package/template/packages/frontend/src/components/ui/hand-drawn-icons.tsx +41 -0
  110. package/template/packages/frontend/src/components/ui/index.ts +36 -0
  111. package/template/packages/frontend/src/components/ui/input.tsx +24 -0
  112. package/template/packages/frontend/src/components/ui/label.tsx +18 -0
  113. package/template/packages/frontend/src/components/ui/otp-input.tsx +140 -0
  114. package/template/packages/frontend/src/components/ui/select.tsx +240 -0
  115. package/template/packages/frontend/src/components/ui/sidebar.tsx +343 -0
  116. package/template/packages/frontend/src/components/ui/skeleton.tsx +8 -0
  117. package/template/packages/frontend/src/components/ui/slider.tsx +31 -0
  118. package/template/packages/frontend/src/components/ui/slot-drum.tsx +50 -0
  119. package/template/packages/frontend/src/components/ui/spinner.tsx +30 -0
  120. package/template/packages/frontend/src/components/ui/switch.tsx +31 -0
  121. package/template/packages/frontend/src/components/ui/table.tsx +106 -0
  122. package/template/packages/frontend/src/components/ui/tabs.tsx +196 -0
  123. package/template/packages/frontend/src/components/ui/textarea.tsx +21 -0
  124. package/template/packages/frontend/src/components/ui/toaster.tsx +72 -0
  125. package/template/packages/frontend/src/components/ui/tooltip.tsx +31 -0
  126. package/template/packages/frontend/src/env.ts +21 -0
  127. package/template/packages/frontend/src/features/data-table/components/column-helpers.tsx +136 -0
  128. package/template/packages/frontend/src/features/data-table/components/data-table.tsx +303 -0
  129. package/template/packages/frontend/src/features/data-table/components/inline-stat.tsx +113 -0
  130. package/template/packages/frontend/src/features/data-table/components/sortable-header.tsx +33 -0
  131. package/template/packages/frontend/src/features/data-table/components/table-error.tsx +24 -0
  132. package/template/packages/frontend/src/features/data-table/components/table-filters.tsx +82 -0
  133. package/template/packages/frontend/src/features/data-table/hooks/useTableState.ts +111 -0
  134. package/template/packages/frontend/src/features/data-table/index.ts +26 -0
  135. package/template/packages/frontend/src/features/data-table/table-features.ts +26 -0
  136. package/template/packages/frontend/src/hooks/index.ts +3 -0
  137. package/template/packages/frontend/src/hooks/useInfiniteScroll.ts +67 -0
  138. package/template/packages/frontend/src/hooks/useIsTouchDevice.ts +25 -0
  139. package/template/packages/frontend/src/hooks/useMediaQuery.ts +30 -0
  140. package/template/packages/frontend/src/i18n/locales.ts +6 -0
  141. package/template/packages/frontend/src/i18n/navigation.ts +4 -0
  142. package/template/packages/frontend/src/i18n/request.ts +9 -0
  143. package/template/packages/frontend/src/i18n/routing.ts +8 -0
  144. package/template/packages/frontend/src/lib/api/client.ts +20 -0
  145. package/template/packages/frontend/src/lib/api/queries.ts +11 -0
  146. package/template/packages/frontend/src/lib/query-client.ts +20 -0
  147. package/template/packages/frontend/src/lib/realtime/repository.ts +21 -0
  148. package/template/packages/frontend/src/lib/seo/metadata.ts +50 -0
  149. package/template/packages/frontend/src/lib/seo/pages.test.ts +25 -0
  150. package/template/packages/frontend/src/lib/seo/pages.ts +107 -0
  151. package/template/packages/frontend/src/lib/utils/cn.ts +6 -0
  152. package/template/packages/frontend/src/lib/utils/format.ts +25 -0
  153. package/template/packages/frontend/src/lib/utils/index.ts +2 -0
  154. package/template/packages/frontend/src/providers/index.tsx +18 -0
  155. package/template/packages/frontend/src/providers/react-query.tsx +9 -0
  156. package/template/packages/frontend/src/providers/realtime.tsx +16 -0
  157. package/template/packages/frontend/src/proxy.ts +6 -0
  158. package/template/packages/frontend/src/theme/config.ts +20 -0
  159. package/template/packages/frontend/src/theme/transition.ts +54 -0
  160. package/template/packages/frontend/tsconfig.json +13 -0
  161. package/template/packages/shared/package.json +23 -0
  162. package/template/packages/shared/src/contracts/repository.ts +28 -0
  163. package/template/packages/shared/src/events/repository.ts +9 -0
  164. package/template/packages/shared/src/index.ts +3 -0
  165. package/template/packages/shared/src/schemas/repository.test.ts +36 -0
  166. package/template/packages/shared/src/schemas/repository.ts +28 -0
  167. package/template/packages/shared/tsconfig.json +5 -0
  168. package/template/playwright.config.ts +15 -0
  169. package/template/scripts/check-authored.ts +81 -0
  170. package/template/scripts/dev.ts +23 -0
  171. package/template/scripts/local-env.ts +12 -0
  172. package/template/scripts/runtime-smoke.ts +67 -0
  173. package/template/scripts/tooling-env.ts +15 -0
  174. package/template/tsconfig.json +13 -0
@@ -0,0 +1,269 @@
1
+ 'use client';
2
+
3
+ import {
4
+ IconArrowUpRight,
5
+ IconBooks,
6
+ IconBox,
7
+ IconBrandReact,
8
+ IconCode,
9
+ IconDatabase,
10
+ IconDeviceDesktop,
11
+ IconRoute,
12
+ IconServer,
13
+ IconShieldCheck,
14
+ IconUsers,
15
+ } from '@tabler/icons-react';
16
+ import { ThemedImage } from '@wrksz/themes/client/themed-image';
17
+ import { useState } from 'react';
18
+ import {
19
+ AdaptiveModal,
20
+ Badge,
21
+ Button,
22
+ Card,
23
+ CardContent,
24
+ CardHeader,
25
+ CardTitle,
26
+ Tabs,
27
+ TabsContent,
28
+ TabsList,
29
+ TabsTrigger,
30
+ } from '@/components/ui';
31
+
32
+ const problems = [
33
+ ['Schema drift', 'One named Zod schema drives server validation and every client.'],
34
+ ['Transport duplication', 'HTTP, tools and CLI share the same implementation.'],
35
+ ['Frontend plumbing', 'Queries, realtime cache updates and errors arrive composed.'],
36
+ ];
37
+
38
+ export function ProblemsSection() {
39
+ return (
40
+ <section className='space-y-6 py-14'>
41
+ <div className='max-w-2xl'>
42
+ <p className='text-sm text-muted-foreground'>The repetitive work</p>
43
+ <h2 className='text-3xl font-medium'>Keep infrastructure from becoming the product</h2>
44
+ </div>
45
+ <div className='grid gap-3 md:grid-cols-3'>
46
+ {problems.map(([title, copy], index) => (
47
+ <Card key={title}>
48
+ <CardHeader>
49
+ <Badge className='w-fit'>{String(index + 1).padStart(2, '0')}</Badge>
50
+ <CardTitle className='pt-4'>{title}</CardTitle>
51
+ </CardHeader>
52
+ <CardContent className='text-sm text-muted-foreground'>{copy}</CardContent>
53
+ </Card>
54
+ ))}
55
+ </div>
56
+ </section>
57
+ );
58
+ }
59
+
60
+ export function BuilderSection() {
61
+ return (
62
+ <section className='grid gap-8 border-y border-border py-14 md:grid-cols-[0.8fr_1.2fr] md:items-center'>
63
+ <div className='grid aspect-square max-w-sm place-items-center rounded-3xl bg-muted'>
64
+ <IconCode className='size-20 text-muted-foreground' stroke={1.25} />
65
+ </div>
66
+ <div className='space-y-4'>
67
+ <Badge variant='secondary'>Built for product teams</Badge>
68
+ <h2 className='text-3xl font-medium'>A neutral foundation, not a sample business</h2>
69
+ <p className='text-muted-foreground'>
70
+ Replace the example domain while retaining contracts, runtime boundaries, operational
71
+ tooling and the complete component vocabulary.
72
+ </p>
73
+ <div className='flex items-center gap-2 text-sm'>
74
+ <IconUsers /> Solo developers and teams
75
+ </div>
76
+ </div>
77
+ </section>
78
+ );
79
+ }
80
+
81
+ const foundations = [
82
+ {
83
+ icon: IconShieldCheck,
84
+ title: 'Validated boundaries',
85
+ copy: 'Fail-first environment and data parsing.',
86
+ },
87
+ {
88
+ icon: IconDatabase,
89
+ title: 'Persistent by default',
90
+ copy: 'Prisma and PostgreSQL with a real migration.',
91
+ },
92
+ {
93
+ icon: IconServer,
94
+ title: 'Independent backend',
95
+ copy: 'Long-running work, sockets and MCP stay outside Next.',
96
+ },
97
+ {
98
+ icon: IconRoute,
99
+ title: 'Typed navigation',
100
+ copy: 'Contracts build calls and URLs without repeated strings.',
101
+ },
102
+ ];
103
+
104
+ export function WhySection() {
105
+ return (
106
+ <section className='space-y-6 py-14'>
107
+ <div>
108
+ <p className='text-sm text-muted-foreground'>Why this topology</p>
109
+ <h2 className='text-3xl font-medium'>Clear ownership at every boundary</h2>
110
+ </div>
111
+ <div className='grid gap-3 sm:grid-cols-2'>
112
+ {foundations.map((item) => (
113
+ <Card key={item.title}>
114
+ <CardContent className='flex gap-4 pt-4'>
115
+ <div className='grid size-10 shrink-0 place-items-center rounded-xl bg-muted'>
116
+ <item.icon />
117
+ </div>
118
+ <div>
119
+ <h3 className='font-medium'>{item.title}</h3>
120
+ <p className='mt-1 text-sm text-muted-foreground'>{item.copy}</p>
121
+ </div>
122
+ </CardContent>
123
+ </Card>
124
+ ))}
125
+ </div>
126
+ </section>
127
+ );
128
+ }
129
+
130
+ export function AudienceSection() {
131
+ return (
132
+ <section className='grid gap-3 py-14 md:grid-cols-3'>
133
+ {[
134
+ ['Start', 'Generate a complete system and learn it through one bounded domain.'],
135
+ ['Adapt', 'Keep the boundaries and replace fixtures with your own product model.'],
136
+ ['Scale', 'Split deployments without rewriting the contract or transport surface.'],
137
+ ].map(([title, copy]) => (
138
+ <div className='rounded-2xl border border-border p-5' key={title}>
139
+ <h3 className='text-xl font-medium'>{title}</h3>
140
+ <p className='mt-2 text-sm text-muted-foreground'>{copy}</p>
141
+ </div>
142
+ ))}
143
+ </section>
144
+ );
145
+ }
146
+
147
+ export function LearningSection() {
148
+ return (
149
+ <section className='grid gap-8 py-14 md:grid-cols-[0.8fr_1.2fr]'>
150
+ <div>
151
+ <IconBooks className='mb-4 size-10' />
152
+ <h2 className='text-3xl font-medium'>Learn by tracing one operation</h2>
153
+ <p className='mt-3 text-muted-foreground'>
154
+ Follow a project mutation from schema to contract, service, HTTP client, query cache,
155
+ Socket.IO event, MCP tool and CLI command.
156
+ </p>
157
+ </div>
158
+ <ol className='space-y-3'>
159
+ {[
160
+ 'Define a named schema',
161
+ 'Reference it from a thin contract',
162
+ 'Implement the domain once',
163
+ 'Consume the generated surfaces',
164
+ ].map((step, index) => (
165
+ <li
166
+ className='flex items-center gap-4 rounded-xl border border-border p-4'
167
+ key={step}
168
+ >
169
+ <Badge>{index + 1}</Badge>
170
+ <span>{step}</span>
171
+ </li>
172
+ ))}
173
+ </ol>
174
+ </section>
175
+ );
176
+ }
177
+
178
+ export function TemplatePreviewSection() {
179
+ return (
180
+ <section className='space-y-6 py-14'>
181
+ <div className='flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between'>
182
+ <div>
183
+ <p className='text-sm text-muted-foreground'>Theme-aware media</p>
184
+ <h2 className='text-3xl font-medium'>A project shell ready to become yours</h2>
185
+ </div>
186
+ <Badge variant='success'>Light + dark</Badge>
187
+ </div>
188
+ <ThemedImage
189
+ src={{ light: '/theme-light.svg', dark: '/theme-dark.svg' }}
190
+ alt='Theme-aware generic application preview'
191
+ width={1280}
192
+ height={720}
193
+ className='w-full rounded-2xl border border-border'
194
+ />
195
+ </section>
196
+ );
197
+ }
198
+
199
+ export function FrontendSection() {
200
+ return (
201
+ <section className='space-y-6 py-14'>
202
+ <h2 className='text-3xl font-medium'>Frontend foundations included</h2>
203
+ <div className='grid gap-3 sm:grid-cols-3'>
204
+ {[
205
+ {
206
+ icon: IconBrandReact,
207
+ title: 'React 19',
208
+ copy: 'Server-first composition and isolated client islands.',
209
+ },
210
+ {
211
+ icon: IconDeviceDesktop,
212
+ title: 'Responsive UI',
213
+ copy: 'Touch, keyboard and desktop interaction patterns.',
214
+ },
215
+ {
216
+ icon: IconBox,
217
+ title: 'Source components',
218
+ copy: 'Own and adapt every primitive without a black box.',
219
+ },
220
+ ].map((item) => (
221
+ <Card key={item.title}>
222
+ <CardContent className='pt-4'>
223
+ <item.icon className='mb-5 size-8' />
224
+ <h3 className='font-medium'>{item.title}</h3>
225
+ <p className='mt-2 text-sm text-muted-foreground'>{item.copy}</p>
226
+ </CardContent>
227
+ </Card>
228
+ ))}
229
+ </div>
230
+ </section>
231
+ );
232
+ }
233
+
234
+ export function DemoModal() {
235
+ const [open, setOpen] = useState(false);
236
+ return (
237
+ <>
238
+ <Button variant='outline' onClick={() => setOpen(true)}>
239
+ Explore starter <IconArrowUpRight />
240
+ </Button>
241
+ <AdaptiveModal
242
+ isOpen={open}
243
+ onClose={() => setOpen(false)}
244
+ title='Choose a surface'
245
+ desktopWidth='2xl'
246
+ mobileVariant='sheet'
247
+ >
248
+ <div className='p-4 pt-1'>
249
+ <Tabs defaultValue='http'>
250
+ <TabsList>
251
+ <TabsTrigger value='http'>HTTP</TabsTrigger>
252
+ <TabsTrigger value='tools'>Tools</TabsTrigger>
253
+ <TabsTrigger value='realtime'>Realtime</TabsTrigger>
254
+ </TabsList>
255
+ <TabsContent className='pt-5 text-sm text-muted-foreground' value='http'>
256
+ Typed browser and server clients consume the same contract.
257
+ </TabsContent>
258
+ <TabsContent className='pt-5 text-sm text-muted-foreground' value='tools'>
259
+ MCP, Agent and CLI surfaces reuse the service runner and validation.
260
+ </TabsContent>
261
+ <TabsContent className='pt-5 text-sm text-muted-foreground' value='realtime'>
262
+ Socket.IO events update the TanStack Query cache through Stitchkit.
263
+ </TabsContent>
264
+ </Tabs>
265
+ </div>
266
+ </AdaptiveModal>
267
+ </>
268
+ );
269
+ }
@@ -0,0 +1,159 @@
1
+ 'use client';
2
+
3
+ import { IconArrowRight, IconBraces, IconDatabase, IconWorld } from '@tabler/icons-react';
4
+ import type { ReactNode } from 'react';
5
+ import { BrandMark } from '@/components/brand-mark';
6
+ import {
7
+ Badge,
8
+ Button,
9
+ Card,
10
+ CardContent,
11
+ CardDeck,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from '@/components/ui';
15
+
16
+ const features = [
17
+ {
18
+ icon: IconBraces,
19
+ title: 'Contract first',
20
+ copy: 'Schemas, clients and transports stay aligned.',
21
+ },
22
+ {
23
+ icon: IconDatabase,
24
+ title: 'Production data',
25
+ copy: 'Prisma and PostgreSQL are ready from day one.',
26
+ },
27
+ {
28
+ icon: IconWorld,
29
+ title: 'Every surface',
30
+ copy: 'HTTP, realtime, MCP, agents and CLI share one service.',
31
+ },
32
+ ];
33
+
34
+ export function LandingHeader({ actions }: { actions?: ReactNode }) {
35
+ return (
36
+ <header className='flex items-center justify-between border-b border-border py-4'>
37
+ <div className='flex items-center gap-2 font-medium'>
38
+ <BrandMark priority />
39
+ Stitchkit Starter
40
+ </div>
41
+ <div className='flex items-center gap-2'>{actions}</div>
42
+ </header>
43
+ );
44
+ }
45
+
46
+ export function HeroSection() {
47
+ return (
48
+ <section className='grid gap-8 py-20 lg:grid-cols-[1.2fr_0.8fr] lg:items-end'>
49
+ <div className='space-y-6'>
50
+ <Badge variant='primary'>Production starter</Badge>
51
+ <h1 className='max-w-3xl font-display text-5xl font-medium tracking-tight sm:text-7xl'>
52
+ Build the product, not its plumbing.
53
+ </h1>
54
+ <p className='max-w-2xl text-lg text-muted-foreground'>
55
+ A precise fullstack base with a complete UI system and one contract across every
56
+ interface.
57
+ </p>
58
+ <div className='flex flex-wrap gap-3'>
59
+ <Button variant='primary'>
60
+ Start building <IconArrowRight />
61
+ </Button>
62
+ <Button variant='outline'>Read the architecture</Button>
63
+ </div>
64
+ </div>
65
+ <CardDeck
66
+ items={features}
67
+ overlap={-28}
68
+ rotationStep={4}
69
+ keyExtractor={(feature) => feature.title}
70
+ renderCard={(feature) => (
71
+ <Card className='w-48 bg-card/95 backdrop-blur'>
72
+ <CardHeader>
73
+ <feature.icon />
74
+ <CardTitle>{feature.title}</CardTitle>
75
+ </CardHeader>
76
+ <CardContent className='text-sm text-muted-foreground'>{feature.copy}</CardContent>
77
+ </Card>
78
+ )}
79
+ />
80
+ </section>
81
+ );
82
+ }
83
+
84
+ export function FeatureGrid() {
85
+ return (
86
+ <section className='grid gap-3 py-10 md:grid-cols-3'>
87
+ {features.map((feature) => (
88
+ <Card key={feature.title}>
89
+ <CardHeader>
90
+ <div className='mb-5 grid size-10 place-items-center rounded-xl bg-muted'>
91
+ <feature.icon />
92
+ </div>
93
+ <CardTitle>{feature.title}</CardTitle>
94
+ </CardHeader>
95
+ <CardContent className='text-sm text-muted-foreground'>{feature.copy}</CardContent>
96
+ </Card>
97
+ ))}
98
+ </section>
99
+ );
100
+ }
101
+
102
+ export function PortfolioSection() {
103
+ return (
104
+ <section className='space-y-6 py-16'>
105
+ <div>
106
+ <p className='text-sm text-muted-foreground'>Composable sections</p>
107
+ <h2 className='text-3xl font-medium'>From landing page to application shell</h2>
108
+ </div>
109
+ <div className='grid gap-4 md:grid-cols-2'>
110
+ {[
111
+ 'Editorial landing',
112
+ 'Operational dashboard',
113
+ 'Account workspace',
114
+ 'Data-heavy admin',
115
+ ].map((title, index) => (
116
+ <div
117
+ className='aspect-[16/9] rounded-2xl border border-border bg-gradient-to-br from-muted to-background p-5'
118
+ key={title}
119
+ >
120
+ <Badge>{String(index + 1).padStart(2, '0')}</Badge>
121
+ <p className='mt-auto pt-20 text-lg font-medium'>{title}</p>
122
+ </div>
123
+ ))}
124
+ </div>
125
+ </section>
126
+ );
127
+ }
128
+
129
+ export function PricingSection() {
130
+ return (
131
+ <section className='grid gap-4 py-16 md:grid-cols-3'>
132
+ {['Foundation', 'Product', 'Scale'].map((name, index) => (
133
+ <Card className={index === 1 ? 'border-primary' : undefined} key={name}>
134
+ <CardHeader>
135
+ <Badge variant={index === 1 ? 'primary' : 'default'}>
136
+ {index === 1 ? 'Recommended' : 'Flexible'}
137
+ </Badge>
138
+ <CardTitle className='pt-4 text-2xl'>{name}</CardTitle>
139
+ </CardHeader>
140
+ <CardContent className='space-y-5 text-sm text-muted-foreground'>
141
+ <p>A reusable layout demonstrating pricing, comparison and conversion patterns.</p>
142
+ <Button className='w-full' variant={index === 1 ? 'primary' : 'outline'}>
143
+ Choose {name}
144
+ </Button>
145
+ </CardContent>
146
+ </Card>
147
+ ))}
148
+ </section>
149
+ );
150
+ }
151
+
152
+ export function LandingFooter() {
153
+ return (
154
+ <footer className='flex flex-col gap-3 border-t border-border py-8 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between'>
155
+ <span>Stitchkit Starter · Built with Stitchkit</span>
156
+ <span>Contracts · UI · Realtime · Tools</span>
157
+ </footer>
158
+ );
159
+ }
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import {
5
+ AnimatedCounter,
6
+ Button,
7
+ Card,
8
+ CardContent,
9
+ CardDeck,
10
+ CardHeader,
11
+ CardTitle,
12
+ SlotDrum,
13
+ } from '@/components/ui';
14
+ import { StorySection } from './catalogue-shell';
15
+
16
+ const deckItems = [
17
+ { title: 'Contracts', copy: 'One source of truth' },
18
+ { title: 'Transport', copy: 'HTTP, MCP and agents' },
19
+ { title: 'Interface', copy: 'Typed data to polished UI' },
20
+ ];
21
+ const stages = ['Idea', 'Building', 'Shipping'];
22
+
23
+ export function MotionStory() {
24
+ const [count, setCount] = useState(24);
25
+ const [stage, setStage] = useState(0);
26
+ return (
27
+ <div>
28
+ <StorySection title='Meaningful motion'>
29
+ <div className='grid gap-4 md:grid-cols-2'>
30
+ <Card>
31
+ <CardHeader>
32
+ <CardTitle>Delivery count</CardTitle>
33
+ </CardHeader>
34
+ <CardContent className='space-y-4'>
35
+ <AnimatedCounter className='text-4xl font-medium' value={count} suffix='%' />
36
+ <Button onClick={() => setCount((value) => (value + 13) % 101)}>Advance</Button>
37
+ </CardContent>
38
+ </Card>
39
+ <Card>
40
+ <CardHeader>
41
+ <CardTitle>Current stage</CardTitle>
42
+ </CardHeader>
43
+ <CardContent className='space-y-4'>
44
+ <SlotDrum
45
+ className='text-2xl font-medium'
46
+ items={stages}
47
+ activeIndex={stage}
48
+ getItemKey={(item) => item}
49
+ renderItem={(item) => item}
50
+ />
51
+ <Button onClick={() => setStage((value) => (value + 1) % stages.length)}>
52
+ Next stage
53
+ </Button>
54
+ </CardContent>
55
+ </Card>
56
+ </div>
57
+ </StorySection>
58
+ <StorySection title='Composed card deck'>
59
+ <div className='py-8'>
60
+ <CardDeck
61
+ items={deckItems}
62
+ overlap={-24}
63
+ rotationStep={4}
64
+ keyExtractor={(item) => item.title}
65
+ renderCard={(item) => (
66
+ <Card className='w-52 bg-card'>
67
+ <CardHeader>
68
+ <CardTitle>{item.title}</CardTitle>
69
+ </CardHeader>
70
+ <CardContent className='text-sm text-muted-foreground'>{item.copy}</CardContent>
71
+ </Card>
72
+ )}
73
+ />
74
+ </div>
75
+ </StorySection>
76
+ </div>
77
+ );
78
+ }
@@ -0,0 +1,134 @@
1
+ 'use client';
2
+
3
+ import { IconDots, IconSettings, IconTrash } from '@tabler/icons-react';
4
+ import { useState } from 'react';
5
+ import {
6
+ AdaptiveModal,
7
+ Button,
8
+ ConfirmationModal,
9
+ Drawer,
10
+ DrawerClose,
11
+ DrawerContent,
12
+ DrawerDescription,
13
+ DrawerFooter,
14
+ DrawerHeader,
15
+ DrawerTitle,
16
+ DrawerTrigger,
17
+ DropdownMenu,
18
+ DropdownMenuCheckboxItem,
19
+ DropdownMenuContent,
20
+ DropdownMenuItem,
21
+ DropdownMenuLabel,
22
+ DropdownMenuSeparator,
23
+ DropdownMenuShortcut,
24
+ DropdownMenuTrigger,
25
+ Tooltip,
26
+ TooltipContent,
27
+ TooltipProvider,
28
+ TooltipTrigger,
29
+ toast,
30
+ } from '@/components/ui';
31
+ import { StorySection } from './catalogue-shell';
32
+
33
+ export function OverlaysStory() {
34
+ const [modalOpen, setModalOpen] = useState(false);
35
+ const [confirmOpen, setConfirmOpen] = useState(false);
36
+ const [notifications, setNotifications] = useState(true);
37
+
38
+ return (
39
+ <div>
40
+ <StorySection title='Adaptive surfaces'>
41
+ <div className='flex flex-wrap gap-3'>
42
+ <Button onClick={() => setModalOpen(true)}>Adaptive modal</Button>
43
+ <Button variant='destructive' onClick={() => setConfirmOpen(true)}>
44
+ Confirmation
45
+ </Button>
46
+ <Drawer>
47
+ <DrawerTrigger asChild>
48
+ <Button variant='outline'>Drawer</Button>
49
+ </DrawerTrigger>
50
+ <DrawerContent>
51
+ <DrawerHeader>
52
+ <DrawerTitle>Project settings</DrawerTitle>
53
+ <DrawerDescription>A composable mobile-first surface.</DrawerDescription>
54
+ </DrawerHeader>
55
+ <div className='px-4 text-sm text-muted-foreground'>
56
+ Use drawers for contextual actions without replacing the current page.
57
+ </div>
58
+ <DrawerFooter>
59
+ <DrawerClose asChild>
60
+ <Button>Done</Button>
61
+ </DrawerClose>
62
+ </DrawerFooter>
63
+ </DrawerContent>
64
+ </Drawer>
65
+ </div>
66
+ <AdaptiveModal
67
+ isOpen={modalOpen}
68
+ onClose={() => setModalOpen(false)}
69
+ title='Adaptive modal'
70
+ mobileVariant='sheet'
71
+ footer={
72
+ <Button className='w-full' onClick={() => setModalOpen(false)}>
73
+ Save changes
74
+ </Button>
75
+ }
76
+ >
77
+ <div className='px-4 pb-6 text-sm text-muted-foreground'>
78
+ Centered on desktop, touch-friendly sheet on compact screens.
79
+ </div>
80
+ </AdaptiveModal>
81
+ <ConfirmationModal
82
+ isOpen={confirmOpen}
83
+ onClose={() => setConfirmOpen(false)}
84
+ onConfirm={() => {
85
+ setConfirmOpen(false);
86
+ toast.success('Action confirmed');
87
+ }}
88
+ title='Archive project?'
89
+ description='The project remains available in your history.'
90
+ confirmText='Archive'
91
+ variant='destructive'
92
+ />
93
+ </StorySection>
94
+
95
+ <StorySection title='Menus and contextual help'>
96
+ <div className='flex items-center gap-3'>
97
+ <DropdownMenu>
98
+ <DropdownMenuTrigger asChild>
99
+ <Button size='icon' variant='outline' aria-label='Open project menu'>
100
+ <IconDots />
101
+ </Button>
102
+ </DropdownMenuTrigger>
103
+ <DropdownMenuContent align='start'>
104
+ <DropdownMenuLabel>Project</DropdownMenuLabel>
105
+ <DropdownMenuItem>
106
+ <IconSettings /> Settings <DropdownMenuShortcut>⌘,</DropdownMenuShortcut>
107
+ </DropdownMenuItem>
108
+ <DropdownMenuCheckboxItem
109
+ checked={notifications}
110
+ onCheckedChange={setNotifications}
111
+ >
112
+ Notifications
113
+ </DropdownMenuCheckboxItem>
114
+ <DropdownMenuSeparator />
115
+ <DropdownMenuItem className='text-destructive'>
116
+ <IconTrash /> Archive
117
+ </DropdownMenuItem>
118
+ </DropdownMenuContent>
119
+ </DropdownMenu>
120
+ <TooltipProvider>
121
+ <Tooltip>
122
+ <TooltipTrigger asChild>
123
+ <Button variant='ghost'>Why server cache?</Button>
124
+ </TooltipTrigger>
125
+ <TooltipContent>
126
+ It protects external rate limits and keeps rendering predictable.
127
+ </TooltipContent>
128
+ </Tooltip>
129
+ </TooltipProvider>
130
+ </div>
131
+ </StorySection>
132
+ </div>
133
+ );
134
+ }