@thorprovider/create-storefront 0.1.1
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/README.md +119 -0
- package/bin/install.js +116 -0
- package/commands/sf-add-view.md +21 -0
- package/commands/sf-init.md +16 -0
- package/commands/sf-theme.md +15 -0
- package/commands/sf-view.md +20 -0
- package/package.json +40 -0
- package/recipes/archetype.schema.json +39 -0
- package/recipes/archetypes.json +148 -0
- package/recipes/recipe.schema.json +59 -0
- package/recipes/recipes.json +90 -0
- package/recipes/sections.json +46 -0
- package/recipes/validate.mjs +190 -0
- package/skills/building-storefronts/SKILL.md +178 -0
- package/skills/building-storefronts/references/frontend-integration.md +229 -0
- package/skills/json-render-core/SKILL.md +291 -0
- package/skills/json-render-next/SKILL.md +194 -0
- package/skills/json-render-react/SKILL.md +298 -0
- package/skills/json-render-remotion/SKILL.md +111 -0
- package/skills/json-render-shadcn/SKILL.md +159 -0
- package/skills/json-render-solid/SKILL.md +204 -0
- package/skills/nextjs-shadcn/SKILL.md +303 -0
- package/skills/nextjs-shadcn/references/architecture.md +499 -0
- package/skills/nextjs-shadcn/references/project-setup.md +127 -0
- package/skills/nextjs-shadcn/references/shadcn-platform.md +258 -0
- package/skills/nextjs-shadcn/references/sidebar.md +274 -0
- package/skills/nextjs-shadcn/references/styling.md +555 -0
- package/skills/sf-scaffold/SKILL.md +118 -0
- package/skills/sf-theme-gen/SKILL.md +44 -0
- package/skills/sf-view-gen/SKILL.md +94 -0
- package/skills/shadcn-component-discovery/SKILL.md +273 -0
- package/skills/shadcn-component-discovery/references/registries.md +226 -0
- package/skills/shadcn-theming/SKILL.md +104 -0
- package/skills/shadcn-theming/references/templates/theme-setup.md +109 -0
- package/skills/shadcn-theming/references/theming-guide.md +90 -0
- package/skills/storefront-best-practices/SKILL.md +421 -0
- package/skills/storefront-best-practices/reference/components/breadcrumbs.md +123 -0
- package/skills/storefront-best-practices/reference/components/cart-popup.md +189 -0
- package/skills/storefront-best-practices/reference/components/country-selector.md +298 -0
- package/skills/storefront-best-practices/reference/components/footer.md +112 -0
- package/skills/storefront-best-practices/reference/components/hero.md +241 -0
- package/skills/storefront-best-practices/reference/components/megamenu.md +239 -0
- package/skills/storefront-best-practices/reference/components/navbar.md +397 -0
- package/skills/storefront-best-practices/reference/components/popups.md +221 -0
- package/skills/storefront-best-practices/reference/components/product-card.md +125 -0
- package/skills/storefront-best-practices/reference/components/product-reviews.md +217 -0
- package/skills/storefront-best-practices/reference/components/product-slider.md +174 -0
- package/skills/storefront-best-practices/reference/components/search.md +101 -0
- package/skills/storefront-best-practices/reference/connecting-to-backend.md +391 -0
- package/skills/storefront-best-practices/reference/design.md +388 -0
- package/skills/storefront-best-practices/reference/features/promotions.md +307 -0
- package/skills/storefront-best-practices/reference/features/wishlist.md +230 -0
- package/skills/storefront-best-practices/reference/layouts/account.md +380 -0
- package/skills/storefront-best-practices/reference/layouts/cart.md +316 -0
- package/skills/storefront-best-practices/reference/layouts/checkout.md +486 -0
- package/skills/storefront-best-practices/reference/layouts/home-page.md +264 -0
- package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +231 -0
- package/skills/storefront-best-practices/reference/layouts/product-details.md +527 -0
- package/skills/storefront-best-practices/reference/layouts/product-listing.md +520 -0
- package/skills/storefront-best-practices/reference/layouts/static-pages.md +356 -0
- package/skills/storefront-best-practices/reference/medusa.md +307 -0
- package/skills/storefront-best-practices/reference/mobile-responsiveness.md +183 -0
- package/skills/storefront-best-practices/reference/seo.md +195 -0
- package/templates/app/app/[[...slug]]/page.tsx +17 -0
- package/templates/app/app/[[...slug]]/renderer.tsx +10 -0
- package/templates/app/app/globals.css +101 -0
- package/templates/app/app/layout.tsx +35 -0
- package/templates/app/lib/__STOREFRONT__/catalog.ts +132 -0
- package/templates/app/lib/__STOREFRONT__/handlers.ts +33 -0
- package/templates/app/lib/__STOREFRONT__/registry.tsx +134 -0
- package/templates/app/lib/__STOREFRONT__/runtime.ts +25 -0
- package/templates/app/lib/__STOREFRONT__/spec/home.ts +62 -0
- package/templates/app/lib/__STOREFRONT__/spec/index.ts +59 -0
- package/templates/app/lib/__STOREFRONT__/spec/types.ts +14 -0
- package/templates/app/lib/__STOREFRONT__/state.ts +35 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
# Styling
|
|
2
|
+
|
|
3
|
+
## Theme System
|
|
4
|
+
|
|
5
|
+
### globals.css Structure
|
|
6
|
+
|
|
7
|
+
shadcn generates base variables automatically based on your chosen preset. Customize for your project:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
@import "tailwindcss";
|
|
11
|
+
|
|
12
|
+
/* Note: Tailwind v3 projects use @tailwind base; @tailwind components; @tailwind utilities; instead */
|
|
13
|
+
|
|
14
|
+
/* :root and .dark live OUTSIDE @layer base and hold full color values (OKLCH preferred) */
|
|
15
|
+
:root {
|
|
16
|
+
/* shadcn base variables come from preset */
|
|
17
|
+
--background: ...;
|
|
18
|
+
--foreground: ...;
|
|
19
|
+
--primary: ...;
|
|
20
|
+
--secondary: ...;
|
|
21
|
+
/* etc. */
|
|
22
|
+
|
|
23
|
+
/* Add your own variables as needed */
|
|
24
|
+
--brand: oklch(0.55 0.2 260);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.dark {
|
|
28
|
+
--brand: oklch(0.7 0.18 260);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@theme inline {
|
|
32
|
+
--color-brand: var(--brand);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The `@theme inline` mapping is what makes the `bg-brand` / `text-brand` utilities work — a variable without it generates no utility.
|
|
37
|
+
|
|
38
|
+
**Choose preset**: Use [ui.shadcn.com/create](https://ui.shadcn.com/create) to select named style (vega, nova, maia, lyra, mira, luma, sera, rhea), base color, font, icon library, and radius. The customizer outputs a single preset code that encodes all choices.
|
|
39
|
+
|
|
40
|
+
### Theme Customization
|
|
41
|
+
|
|
42
|
+
Quick customizations in `globals.css`:
|
|
43
|
+
|
|
44
|
+
```css
|
|
45
|
+
:root {
|
|
46
|
+
/* Typography - change fonts */
|
|
47
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
48
|
+
--font-serif: Georgia, serif;
|
|
49
|
+
--font-mono: "Fira Code", ui-monospace, monospace;
|
|
50
|
+
|
|
51
|
+
/* Border radius - affects all rounded corners */
|
|
52
|
+
--radius: 0.5rem; /* Default */
|
|
53
|
+
/* --radius: 0.25rem; /* Sharp */
|
|
54
|
+
/* --radius: 0.75rem; /* More rounded */
|
|
55
|
+
/* --radius: 1rem; /* Very rounded */
|
|
56
|
+
/* --radius: 1.3rem; /* Pill-like buttons */
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| Variable | Effect |
|
|
61
|
+
|----------|--------|
|
|
62
|
+
| `--font-sans` | Body text, buttons, inputs |
|
|
63
|
+
| `--font-mono` | Code blocks, technical content |
|
|
64
|
+
| `--radius` | All rounded corners (buttons, cards, inputs) |
|
|
65
|
+
|
|
66
|
+
**Tip**: Larger `--radius` values (1rem+) give a softer, more modern look. Smaller values (0.25rem) feel sharper and technical.
|
|
67
|
+
|
|
68
|
+
### Using Theme Colors
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
// ✅ Use CSS variables
|
|
72
|
+
<div className="bg-primary text-primary-foreground" />
|
|
73
|
+
<div className="border-border" />
|
|
74
|
+
<div className="text-muted-foreground" />
|
|
75
|
+
|
|
76
|
+
// ❌ Never hardcode colors
|
|
77
|
+
<div className="bg-blue-500" />
|
|
78
|
+
<div className="text-[#1a1a1a]" />
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## shadcn/ui Presets
|
|
82
|
+
|
|
83
|
+
Available styles at ui.shadcn.com/create:
|
|
84
|
+
|
|
85
|
+
| Preset | Character |
|
|
86
|
+
|--------|-----------|
|
|
87
|
+
| vega | Classic shadcn/ui look. Clean, neutral, familiar |
|
|
88
|
+
| nova | Reduced padding and margins for compact layouts |
|
|
89
|
+
| maia | Soft and rounded, with generous spacing |
|
|
90
|
+
| lyra | Boxy and sharp. Pairs well with mono fonts |
|
|
91
|
+
| mira | Compact. Made for dense interfaces |
|
|
92
|
+
| luma | Newer official style — see ui.shadcn.com/create |
|
|
93
|
+
| sera | Newer official style — see ui.shadcn.com/create |
|
|
94
|
+
| rhea | A more compact Luma. Tighter spacing, smaller controls, denser surfaces — built for focused product interfaces |
|
|
95
|
+
|
|
96
|
+
### Fonts
|
|
97
|
+
|
|
98
|
+
Body/mono fonts the preset builder offers (these ids are what the preset code
|
|
99
|
+
encodes — `shadcn preset decode <code>` prints them back):
|
|
100
|
+
|
|
101
|
+
| Font | Type | Character |
|
|
102
|
+
|------|------|-----------|
|
|
103
|
+
| geist | Sans | Vercel's modern geometric sans |
|
|
104
|
+
| inter | Sans | Clean, versatile (classic default) |
|
|
105
|
+
| figtree | Sans | Friendly, geometric |
|
|
106
|
+
| dm-sans | Sans | Compact geometric with character |
|
|
107
|
+
| outfit | Sans | Modern, soft |
|
|
108
|
+
| noto-sans | Sans | Universal language support |
|
|
109
|
+
| nunito-sans | Sans | Rounded, approachable |
|
|
110
|
+
| roboto | Sans | Google's versatile sans |
|
|
111
|
+
| raleway | Sans | Elegant, thin-weight display |
|
|
112
|
+
| public-sans | Sans | US government standard, neutral |
|
|
113
|
+
| jetbrains-mono | Mono | Developer-focused monospace |
|
|
114
|
+
| geist-mono | Mono | Vercel's monospace |
|
|
115
|
+
|
|
116
|
+
Heading fonts (`fontHeading`) are a separate, longer list — lora, merriweather,
|
|
117
|
+
playfair-display, noto-serif, roboto-slab, oxanium, manrope, space-grotesk,
|
|
118
|
+
montserrat, ibm-plex-sans, source-sans-3, instrument-sans, eb-garamond,
|
|
119
|
+
instrument-serif. It defaults to `inherit` (same face as the body).
|
|
120
|
+
|
|
121
|
+
## Icon Libraries
|
|
122
|
+
|
|
123
|
+
Priority order (use first available). The names below are the CLI's library ids —
|
|
124
|
+
pass them verbatim to `shadcn migrate icons --from <id> --to <id>`:
|
|
125
|
+
|
|
126
|
+
1. **lucide** (default) - `bun add lucide-react`
|
|
127
|
+
2. **tabler** - `bun add @tabler/icons-react`
|
|
128
|
+
3. **hugeicons** - `bun add @hugeicons/react @hugeicons/core-free-icons` (the old `hugeicons-react` package is deprecated)
|
|
129
|
+
4. **phosphor** - `bun add @phosphor-icons/react`
|
|
130
|
+
5. **remixicon** - `bun add @remixicon/react`
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
// lucide example
|
|
134
|
+
import { ChevronRight, Menu, X } from "lucide-react"
|
|
135
|
+
|
|
136
|
+
<Button>
|
|
137
|
+
Next <ChevronRight data-icon="inline-end" />
|
|
138
|
+
</Button>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Animations
|
|
142
|
+
|
|
143
|
+
### CSS Page Transitions
|
|
144
|
+
|
|
145
|
+
Add to `globals.css`:
|
|
146
|
+
|
|
147
|
+
```css
|
|
148
|
+
@keyframes page-in {
|
|
149
|
+
from {
|
|
150
|
+
opacity: 0;
|
|
151
|
+
transform: translateY(8px);
|
|
152
|
+
}
|
|
153
|
+
to {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
transform: translateY(0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@layer utilities {
|
|
160
|
+
.animate-page-in {
|
|
161
|
+
animation: page-in 0.6s ease-out both;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Usage in layout or template:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
// template.tsx - animates on every navigation
|
|
170
|
+
export default function Template({ children }: { children: React.ReactNode }) {
|
|
171
|
+
return <main className="animate-page-in">{children}</main>
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### View Transitions API
|
|
176
|
+
|
|
177
|
+
No config needed. View transitions work in the App Router out of the box — it
|
|
178
|
+
runs React canary releases, which ship `ViewTransition`. The old
|
|
179
|
+
`experimental.viewTransition` flag is gone from Next.js 16's config type; adding
|
|
180
|
+
it now is a type error, not a no-op.
|
|
181
|
+
|
|
182
|
+
Wrap what should animate in React's `<ViewTransition>` — no extra install.
|
|
183
|
+
Without browser support it degrades gracefully: no animation, app still works.
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { ViewTransition } from "react"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`<ViewTransition>` animations fire on Transitions, `<Suspense>`, and
|
|
190
|
+
`useDeferredValue`. Route navigations are Transitions, so they activate
|
|
191
|
+
automatically on navigation; plain `setState` does not trigger them.
|
|
192
|
+
|
|
193
|
+
| Pattern | Communicates | Key API |
|
|
194
|
+
|---------|--------------|---------|
|
|
195
|
+
| Shared-element morph | "Same thing, going deeper" | Same `name` on both elements |
|
|
196
|
+
| Suspense reveal | "Data loaded" | `enter`/`exit` on fallback + content, `default="none"` |
|
|
197
|
+
| Directional slide | "Forward / back" | `<Link transitionTypes={["nav-forward"]}>` + `enter`/`exit` keyed by type |
|
|
198
|
+
| Same-route crossfade | "Same place, different content" | `key={slug}` + `share="auto"` `enter="auto"` |
|
|
199
|
+
|
|
200
|
+
Shared-element morph is the most common and works with zero CSS — wrap both the
|
|
201
|
+
source and destination element with the same `name`:
|
|
202
|
+
|
|
203
|
+
```tsx
|
|
204
|
+
// grid thumbnail
|
|
205
|
+
<ViewTransition name={`photo-${photo.id}`}>
|
|
206
|
+
<Image src={photo.src} alt={photo.title} />
|
|
207
|
+
</ViewTransition>
|
|
208
|
+
|
|
209
|
+
// detail page hero — same name
|
|
210
|
+
<ViewTransition name={`photo-${photo.id}`}>
|
|
211
|
+
<Image src={photo.src} alt={photo.title} fill />
|
|
212
|
+
</ViewTransition>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
React matches the names across the old/new route and animates size and
|
|
216
|
+
position. Customize with `share="morph"` and `::view-transition-group(.morph)`
|
|
217
|
+
CSS. Respect `prefers-reduced-motion` by zeroing animation durations on the
|
|
218
|
+
`::view-transition-*` pseudo-elements.
|
|
219
|
+
|
|
220
|
+
Patterns 2–4 (CSS keyframes, directional/Suspense examples):
|
|
221
|
+
[Designing view transitions](https://nextjs.org/docs/app/guides/view-transitions).
|
|
222
|
+
|
|
223
|
+
### Motion Library
|
|
224
|
+
|
|
225
|
+
For complex animations:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
bun add motion
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
"use client"
|
|
233
|
+
|
|
234
|
+
import { motion, HTMLMotionProps } from "motion/react"
|
|
235
|
+
|
|
236
|
+
interface FadeInProps extends HTMLMotionProps<"div"> {
|
|
237
|
+
delay?: number
|
|
238
|
+
duration?: number
|
|
239
|
+
direction?: "up" | "down" | "left" | "right" | "none"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function FadeIn({
|
|
243
|
+
children,
|
|
244
|
+
className,
|
|
245
|
+
delay = 0,
|
|
246
|
+
duration = 0.5,
|
|
247
|
+
direction = "up",
|
|
248
|
+
...props
|
|
249
|
+
}: FadeInProps) {
|
|
250
|
+
const directions = {
|
|
251
|
+
up: { y: 20, x: 0 },
|
|
252
|
+
down: { y: -20, x: 0 },
|
|
253
|
+
left: { x: 20, y: 0 },
|
|
254
|
+
right: { x: -20, y: 0 },
|
|
255
|
+
none: { x: 0, y: 0 },
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<motion.div
|
|
260
|
+
initial={{ opacity: 0, ...directions[direction] }}
|
|
261
|
+
whileInView={{ opacity: 1, x: 0, y: 0 }}
|
|
262
|
+
viewport={{ once: true, margin: "-50px" }}
|
|
263
|
+
transition={{ duration, delay, ease: "easeOut" }}
|
|
264
|
+
className={className}
|
|
265
|
+
{...props}
|
|
266
|
+
>
|
|
267
|
+
{children}
|
|
268
|
+
</motion.div>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### GSAP
|
|
274
|
+
|
|
275
|
+
For scroll-triggered and complex sequences:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
bun add gsap @gsap/react
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
"use client"
|
|
283
|
+
|
|
284
|
+
import { useRef } from "react"
|
|
285
|
+
import { useGSAP } from "@gsap/react"
|
|
286
|
+
import gsap from "gsap"
|
|
287
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger"
|
|
288
|
+
|
|
289
|
+
gsap.registerPlugin(ScrollTrigger)
|
|
290
|
+
|
|
291
|
+
export function ScrollReveal({ children }) {
|
|
292
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
293
|
+
|
|
294
|
+
useGSAP(() => {
|
|
295
|
+
gsap.from(containerRef.current, {
|
|
296
|
+
opacity: 0,
|
|
297
|
+
y: 50,
|
|
298
|
+
scrollTrigger: {
|
|
299
|
+
trigger: containerRef.current,
|
|
300
|
+
start: "top 80%",
|
|
301
|
+
},
|
|
302
|
+
})
|
|
303
|
+
}, [])
|
|
304
|
+
|
|
305
|
+
return <div ref={containerRef}>{children}</div>
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Animation Decision Tree
|
|
310
|
+
|
|
311
|
+
```text
|
|
312
|
+
Simple fade/slide on mount?
|
|
313
|
+
├── Yes → CSS animation in globals.css
|
|
314
|
+
└── No ↓
|
|
315
|
+
|
|
316
|
+
Page/route transitions?
|
|
317
|
+
├── Yes → View Transitions API or template.tsx
|
|
318
|
+
└── No ↓
|
|
319
|
+
|
|
320
|
+
Interactive hover/tap states?
|
|
321
|
+
├── Yes → Tailwind transitions + Motion
|
|
322
|
+
└── No ↓
|
|
323
|
+
|
|
324
|
+
Scroll-triggered sequences?
|
|
325
|
+
├── Yes → GSAP + ScrollTrigger
|
|
326
|
+
└── No → Evaluate if animation needed
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Performance Tips
|
|
330
|
+
|
|
331
|
+
1. **Prefer CSS** - GPU-accelerated, no JS bundle
|
|
332
|
+
2. **Use `will-change` sparingly** - Only for known animations
|
|
333
|
+
3. **Avoid layout thrashing** - Animate `transform` and `opacity`
|
|
334
|
+
4. **Lazy load Motion/GSAP** - Dynamic imports for non-critical animations
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
// Lazy load animation library
|
|
338
|
+
const MotionDiv = dynamic(
|
|
339
|
+
() => import("motion/react").then((mod) => mod.motion.div),
|
|
340
|
+
{ ssr: false }
|
|
341
|
+
)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Decorative Backgrounds
|
|
345
|
+
|
|
346
|
+
Reusable patterns for visual atmosphere and section hierarchy.
|
|
347
|
+
|
|
348
|
+
### Grid Pattern
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
import { cn } from "@/lib/utils"
|
|
352
|
+
|
|
353
|
+
export function GridBackground({
|
|
354
|
+
children,
|
|
355
|
+
className,
|
|
356
|
+
size = 20
|
|
357
|
+
}: {
|
|
358
|
+
children: React.ReactNode
|
|
359
|
+
className?: string
|
|
360
|
+
size?: number
|
|
361
|
+
}) {
|
|
362
|
+
return (
|
|
363
|
+
<div className={cn("relative", className)}>
|
|
364
|
+
<div
|
|
365
|
+
className={cn(
|
|
366
|
+
"absolute inset-0 -z-10",
|
|
367
|
+
"[background-image:linear-gradient(to_right,var(--border)_1px,transparent_1px),linear-gradient(to_bottom,var(--border)_1px,transparent_1px)]"
|
|
368
|
+
)}
|
|
369
|
+
style={{ backgroundSize: `${size}px ${size}px` }}
|
|
370
|
+
/>
|
|
371
|
+
{children}
|
|
372
|
+
</div>
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Dot Pattern
|
|
378
|
+
|
|
379
|
+
```tsx
|
|
380
|
+
export function DotBackground({
|
|
381
|
+
children,
|
|
382
|
+
className
|
|
383
|
+
}: {
|
|
384
|
+
children: React.ReactNode
|
|
385
|
+
className?: string
|
|
386
|
+
}) {
|
|
387
|
+
return (
|
|
388
|
+
<div className={cn("relative", className)}>
|
|
389
|
+
<div
|
|
390
|
+
className={cn(
|
|
391
|
+
"absolute inset-0 -z-10",
|
|
392
|
+
"[background-size:20px_20px]",
|
|
393
|
+
"[background-image:radial-gradient(color-mix(in_oklab,var(--muted-foreground)_30%,transparent)_1px,transparent_1px)]"
|
|
394
|
+
)}
|
|
395
|
+
/>
|
|
396
|
+
{children}
|
|
397
|
+
</div>
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Radial Gradient Hero
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
export function GradientHero({ children }: { children: React.ReactNode }) {
|
|
406
|
+
return (
|
|
407
|
+
<div className="relative min-h-screen">
|
|
408
|
+
<div
|
|
409
|
+
aria-hidden
|
|
410
|
+
className="fixed inset-0 -z-10"
|
|
411
|
+
style={{
|
|
412
|
+
background: "radial-gradient(125% 125% at 50% 10%, var(--background) 40%, var(--primary) 100%)"
|
|
413
|
+
}}
|
|
414
|
+
/>
|
|
415
|
+
{children}
|
|
416
|
+
</div>
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Faded Edge Effect
|
|
422
|
+
|
|
423
|
+
Combine with grid/dot for vignette:
|
|
424
|
+
|
|
425
|
+
```tsx
|
|
426
|
+
<div className="relative">
|
|
427
|
+
<GridBackground className="absolute inset-0" />
|
|
428
|
+
<div className="pointer-events-none absolute inset-0 bg-background [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]" />
|
|
429
|
+
{/* Content */}
|
|
430
|
+
</div>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Animated Spotlight
|
|
434
|
+
|
|
435
|
+
For premium hero sections. Requires `motion`:
|
|
436
|
+
|
|
437
|
+
```tsx
|
|
438
|
+
"use client"
|
|
439
|
+
|
|
440
|
+
import { motion } from "motion/react"
|
|
441
|
+
import { cn } from "@/lib/utils"
|
|
442
|
+
|
|
443
|
+
export function Spotlight({ className }: { className?: string }) {
|
|
444
|
+
return (
|
|
445
|
+
<motion.div
|
|
446
|
+
className={cn(
|
|
447
|
+
"pointer-events-none fixed inset-0 -z-10 overflow-hidden",
|
|
448
|
+
className
|
|
449
|
+
)}
|
|
450
|
+
aria-hidden
|
|
451
|
+
>
|
|
452
|
+
<motion.div
|
|
453
|
+
className="absolute top-0 left-1/2 h-[60vh] w-[80vw] -translate-x-1/2 rounded-full opacity-20 blur-3xl"
|
|
454
|
+
style={{
|
|
455
|
+
background:
|
|
456
|
+
"radial-gradient(ellipse, color-mix(in oklab, var(--primary) 30%, transparent), transparent 70%)",
|
|
457
|
+
}}
|
|
458
|
+
animate={{ x: ["-10%", "10%", "-10%"] }}
|
|
459
|
+
transition={{ duration: 8, repeat: Infinity, ease: "easeInOut" }}
|
|
460
|
+
/>
|
|
461
|
+
</motion.div>
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Combine with DotBackground for depth:
|
|
467
|
+
|
|
468
|
+
```tsx
|
|
469
|
+
<div className="relative min-h-screen bg-background dark:bg-black">
|
|
470
|
+
<Spotlight />
|
|
471
|
+
<DotBackground className="absolute inset-0 opacity-30" />
|
|
472
|
+
<div className="relative z-10">{children}</div>
|
|
473
|
+
</div>
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
For more creative direction (custom textures, particles, dramatic effects), apply `/frontend-design` thinking.
|
|
477
|
+
|
|
478
|
+
### Section Wrapper
|
|
479
|
+
|
|
480
|
+
For sections that need different theme context:
|
|
481
|
+
|
|
482
|
+
```tsx
|
|
483
|
+
type SectionProps = {
|
|
484
|
+
children: React.ReactNode
|
|
485
|
+
variant?: "default" | "muted" | "inverted"
|
|
486
|
+
className?: string
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export function Section({ children, variant = "default", className }: SectionProps) {
|
|
490
|
+
return (
|
|
491
|
+
<section
|
|
492
|
+
className={cn(
|
|
493
|
+
"relative py-24",
|
|
494
|
+
variant === "muted" && "bg-muted",
|
|
495
|
+
variant === "inverted" && "bg-foreground text-background [&_*]:border-background/20",
|
|
496
|
+
className
|
|
497
|
+
)}
|
|
498
|
+
>
|
|
499
|
+
{children}
|
|
500
|
+
</section>
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Background Decision Tree
|
|
506
|
+
|
|
507
|
+
```text
|
|
508
|
+
Full-page ambient effect?
|
|
509
|
+
├── Static → Fixed radial gradient (GradientHero)
|
|
510
|
+
├── Animated → Spotlight + DotBackground
|
|
511
|
+
├── Premium → Apply /frontend-design thinking
|
|
512
|
+
└── No ↓
|
|
513
|
+
|
|
514
|
+
Subtle texture for depth?
|
|
515
|
+
├── Grid → Technical/dashboard feel
|
|
516
|
+
├── Dots → Softer/organic feel
|
|
517
|
+
└── No ↓
|
|
518
|
+
|
|
519
|
+
Section contrast needed?
|
|
520
|
+
├── Yes → Section wrapper with variant
|
|
521
|
+
└── No → Standard bg-background
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### File Organization
|
|
525
|
+
|
|
526
|
+
```text
|
|
527
|
+
components/
|
|
528
|
+
├── ui/ # shadcn primitives
|
|
529
|
+
├── backgrounds/ # Grid, Dot, Gradient patterns
|
|
530
|
+
└── animations/ # FadeIn, ScrollReveal
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Optional Utilities
|
|
534
|
+
|
|
535
|
+
### Scrollbar Hide
|
|
536
|
+
|
|
537
|
+
`shadcn/tailwind.css` already ships a `no-scrollbar` utility, so a shadcn project
|
|
538
|
+
needs no extra package:
|
|
539
|
+
|
|
540
|
+
```tsx
|
|
541
|
+
<div className="overflow-y-auto no-scrollbar">
|
|
542
|
+
{/* Scrollable content without visible scrollbar */}
|
|
543
|
+
</div>
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Only reach for a package in a project that doesn't import `shadcn/tailwind.css`:
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
bun add tailwind-scrollbar-hide
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
```css
|
|
553
|
+
/* globals.css (Tailwind v4 — no config file needed) */
|
|
554
|
+
@import "tailwind-scrollbar-hide/v4";
|
|
555
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sf-scaffold
|
|
3
|
+
description: Scaffold a new Thor Commerce storefront from this package's static templates using @thorprovider/blocks and @thorprovider/adapters (Medusa)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sf-scaffold
|
|
7
|
+
|
|
8
|
+
Scaffold a new Thor Commerce storefront. This skill is **self-contained**: it generates from this package's `templates/` — it NEVER reads `projects/electrostore` or any machine-global skill.
|
|
9
|
+
|
|
10
|
+
## Package resources (use these)
|
|
11
|
+
|
|
12
|
+
- `templates/app/` — the static storefront templates (see below)
|
|
13
|
+
- `recipes/recipes.json`, `recipes/sections.json` — view recipes (for `/sf-add-view`)
|
|
14
|
+
- `skills/nextjs-shadcn/` — vendored Next.js + shadcn setup skill
|
|
15
|
+
- `skills/shadcn-theming/` — vendored theming skill
|
|
16
|
+
- `skills/json-render-next/` — vendored json-render Next.js reference (NextAppSpec, `createNextApp`, `PageRenderer`, `useNextApp`)
|
|
17
|
+
- `skills/json-render-core/` — vendored json-render core reference (catalogs, schemas, spec shape)
|
|
18
|
+
|
|
19
|
+
The json-render references are consult-when-needed: templates are the primary source. When you must adapt or extend the generated `runtime.ts`, `catalog.ts`, or `spec/` beyond the templates, consult `json-render-next` (runtime/app wiring) and `json-render-core` (catalog/spec) instead of working from memory.
|
|
20
|
+
|
|
21
|
+
## Resolving package assets
|
|
22
|
+
|
|
23
|
+
These assets live **inside the installed package**, not in the target project. Resolve the package root in this order:
|
|
24
|
+
|
|
25
|
+
1. If `.opencode/.create-storefront-root` exists in the target project, read it — the installer writes the absolute package root there.
|
|
26
|
+
2. Otherwise, if this skill was loaded from a path under `.opencode/skills/sf-scaffold/`, walk upward from there to the package root.
|
|
27
|
+
3. Read templates from `<package-root>/templates/app/`.
|
|
28
|
+
|
|
29
|
+
If the package root cannot be resolved, STOP and tell the user to re-run the installer.
|
|
30
|
+
|
|
31
|
+
## Template layout
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
templates/app/
|
|
35
|
+
app/
|
|
36
|
+
globals.css
|
|
37
|
+
layout.tsx
|
|
38
|
+
[[...slug]]/page.tsx
|
|
39
|
+
[[...slug]]/renderer.tsx
|
|
40
|
+
lib/__STOREFRONT__/
|
|
41
|
+
catalog.ts
|
|
42
|
+
handlers.ts
|
|
43
|
+
registry.tsx
|
|
44
|
+
runtime.ts
|
|
45
|
+
state.ts
|
|
46
|
+
spec/home.ts
|
|
47
|
+
spec/index.ts
|
|
48
|
+
spec/types.ts
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Placeholders (substitute everywhere)
|
|
52
|
+
|
|
53
|
+
| Placeholder | Meaning | Example |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `__STOREFRONT__` | kebab-case storefront dir name | `my-store` |
|
|
56
|
+
| `__STOREFRONT_CAMEL__` | PascalCase identifier | `myStore` |
|
|
57
|
+
| `__SITE_NAME__` | human site name | `My Store` |
|
|
58
|
+
|
|
59
|
+
## Stack constraints (enforce)
|
|
60
|
+
|
|
61
|
+
- UI: only `@thorprovider/blocks`
|
|
62
|
+
- Data: only `@thorprovider/adapters` + `@thorprovider/storefronts/commerce`
|
|
63
|
+
- Never import `@thorprovider/medusa-extended`
|
|
64
|
+
- Do NOT create custom blocks unless the user explicitly authorizes it
|
|
65
|
+
|
|
66
|
+
## Workflow
|
|
67
|
+
|
|
68
|
+
### 1. Wizard
|
|
69
|
+
Use the `question` tool to collect:
|
|
70
|
+
1. Storefront name (required, kebab-case) — becomes `lib/<name>/`
|
|
71
|
+
2. Medusa backend URL (optional, default `http://localhost:9000`)
|
|
72
|
+
3. Site name (optional, default = storefront name)
|
|
73
|
+
4. Primary brand color (optional)
|
|
74
|
+
|
|
75
|
+
### 2. Preconditions (BEFORE writing anything)
|
|
76
|
+
- Empty dir or existing Next.js project? Unrelated files → WARN + confirm.
|
|
77
|
+
- If `lib/<name>/` exists → ERROR, stop, suggest `/sf-add-view` or `/sf-view`.
|
|
78
|
+
- Confirm `.opencode/` exists in the project.
|
|
79
|
+
Report each check before proceeding.
|
|
80
|
+
|
|
81
|
+
### 3. Next.js + shadcn base
|
|
82
|
+
- Empty dir → follow `skills/nextjs-shadcn/SKILL.md` to set up Next.js + shadcn/ui.
|
|
83
|
+
- Existing Next.js project → skip.
|
|
84
|
+
|
|
85
|
+
### 4. Dependencies
|
|
86
|
+
```bash
|
|
87
|
+
pnpm add @thorprovider/blocks @thorprovider/adapters @thorprovider/storefronts @json-render/core @json-render/react @json-render/next motion
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 5. Copy templates
|
|
91
|
+
Copy every file from `templates/app/` into the target project:
|
|
92
|
+
- rename the `lib/__STOREFRONT__/` directory to `lib/<name>/`
|
|
93
|
+
- replace `__STOREFRONT__`, `__STOREFRONT_CAMEL__`, `__SITE_NAME__` in all files.
|
|
94
|
+
|
|
95
|
+
### 6. Environment
|
|
96
|
+
Create `.env.local`:
|
|
97
|
+
```
|
|
98
|
+
MEDUSA_BACKEND_URL=<url or http://localhost:9000>
|
|
99
|
+
MEDUSA_REGION_ID=
|
|
100
|
+
MEDUSA_PUBLISHABLE_KEY=
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 7. Theme (optional)
|
|
104
|
+
If a brand color was given, follow `skills/shadcn-theming/SKILL.md` to update OKLCH light+dark pairs in `app/globals.css`.
|
|
105
|
+
|
|
106
|
+
### 8. Success
|
|
107
|
+
Report created files and next steps (`pnpm dev`, then `/sf-add-view`, `/sf-theme`).
|
|
108
|
+
|
|
109
|
+
## Error handling
|
|
110
|
+
- Invalid name → re-prompt.
|
|
111
|
+
- `lib/<name>/` exists → stop, suggest `/sf-add-view` / `/sf-view`.
|
|
112
|
+
- List exactly what was written and what failed; never leave partial scaffolding silently.
|
|
113
|
+
|
|
114
|
+
## Generation headers
|
|
115
|
+
Prepend to each copied file:
|
|
116
|
+
```
|
|
117
|
+
// Generated by @thorprovider/create-storefront (sf-scaffold) from templates/app/<path>
|
|
118
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sf-theme-gen
|
|
3
|
+
description: Customize a Thor Commerce storefront's OKLCH color tokens (light/dark), typography, and identity/SEO metadata
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sf-theme-gen
|
|
7
|
+
|
|
8
|
+
Customize a storefront's theme and identity. Self-contained: uses the vendored theming skill, never a machine-global skill.
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
| Area | File |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Design tokens (shadcn) | `app/globals.css` |
|
|
15
|
+
| Typography + metadata | `app/layout.tsx` |
|
|
16
|
+
|
|
17
|
+
## Vendored skill
|
|
18
|
+
Follow `skills/shadcn-theming/SKILL.md` for OKLCH pair rules and contrast.
|
|
19
|
+
|
|
20
|
+
## Color rules
|
|
21
|
+
- Colors are CSS variables, never raw Tailwind color utilities.
|
|
22
|
+
- OKLCH only; define complete background/foreground pairs.
|
|
23
|
+
- Lightness rule: bg `L < 0.55` → fg `L >= 0.93`; bg `L >= 0.55` → fg `L <= 0.20`.
|
|
24
|
+
- Named accents can use `accentColorToOklch(name)` from `@thorprovider/storefronts/render-core/theme`.
|
|
25
|
+
|
|
26
|
+
## Workflow
|
|
27
|
+
|
|
28
|
+
1. Use the `question` tool to ask what to customize: colors, typography, identity, SEO, Open Graph.
|
|
29
|
+
2. **Colors**: follow `skills/shadcn-theming/SKILL.md`; update BOTH `:root` (light) and `.dark` blocks in `app/globals.css`, keeping the `@theme inline` mapping intact.
|
|
30
|
+
3. **Pairs**: every changed color gets its foreground pair in both modes; verify the lightness rule.
|
|
31
|
+
4. **Typography**: load the font with `next/font` in `app/layout.tsx`; point `--font-sans` in `globals.css` at the new variable; keep body >= 1rem / line-height 1.5.
|
|
32
|
+
5. **Identity**: update the `metadata` object in `app/layout.tsx` (title, description) and add Open Graph + Twitter Card fields. Research the current Next.js Metadata API before writing.
|
|
33
|
+
6. **Verify**: run the dev server; confirm both color modes, font, and head metadata. `pnpm typecheck` passes.
|
|
34
|
+
|
|
35
|
+
## Error handling
|
|
36
|
+
- Invalid color input → re-prompt (accept `<name>`, `#hex`, `oklch(...)`).
|
|
37
|
+
- No `lib/<name>/` → suggest `/sf-init`, stop.
|
|
38
|
+
- Unknown token key → check `themeTokenKeys` in `@thorprovider/storefronts/contracts` first.
|
|
39
|
+
|
|
40
|
+
## Generation headers
|
|
41
|
+
```
|
|
42
|
+
// Themed by @thorprovider/create-storefront (sf-theme-gen)
|
|
43
|
+
```
|
|
44
|
+
Never remove existing tokens; only override values.
|