@thorprovider/storefront 0.4.0-mvp.0 → 0.4.0-mvp.2
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 +173 -153
- package/VENDOR.md +69 -0
- package/assets/openspec/config.yaml +38 -0
- package/assets/templates/AGENTS.md +76 -0
- package/assets/templates/app/app/[[...slug]]/page.tsx +42 -1
- package/assets/templates/app/app/[[...slug]]/renderer.tsx +13 -2
- package/assets/templates/app/app/globals.css +5 -8
- package/assets/templates/app/app/layout.tsx +1 -5
- package/assets/templates/app/app/robots.ts +9 -3
- package/assets/templates/app/app/sitemap.ts +64 -5
- package/assets/templates/app/lib/__STOREFRONT__/actions.ts +70 -0
- package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +15 -5
- package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +71 -0
- package/assets/templates/app/lib/__STOREFRONT__/composition.ts +26 -0
- package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +36 -4
- package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +82 -50
- package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +3 -0
- package/assets/templates/app/lib/__STOREFRONT__/schema.ts +29 -14
- package/assets/templates/app/lib/__STOREFRONT__/section-observation.ts +182 -0
- package/assets/templates/app/lib/__STOREFRONT__/section-sources.ts +130 -0
- package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +31 -7
- package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +4 -0
- package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +56 -26
- package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +165 -0
- package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +26 -7
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +2 -1
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.client.tsx +100 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.tsx +19 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.products.ts +39 -0
- package/assets/templates/features/checkout/handlers.ts +6 -0
- package/assets/templates/features/customer-auth/state.ts +10 -1
- package/assets/templates/views/account/index.tsx +1 -1
- package/assets/templates/views/account/registry.tsx +80 -19
- package/assets/templates/views/account/schema.ts +1 -0
- package/assets/templates/views/cart/content.ts +11 -0
- package/assets/templates/views/cart/data.ts +23 -0
- package/assets/templates/views/cart/index.tsx +53 -0
- package/assets/templates/views/cart/props.ts +12 -0
- package/assets/templates/views/cart/registry.tsx +81 -0
- package/assets/templates/views/cart/schema.ts +52 -0
- package/assets/templates/views/category/assembly/assembly.json +1 -0
- package/assets/templates/views/category/assembly/index.skeleton.tsx +64 -6
- package/assets/templates/views/category/assembly/registry.skeleton.tsx +2 -1
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/binding.json +5 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/catalog.part +4 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/element.part +7 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/registry.part +18 -0
- package/assets/templates/views/category/index.tsx +76 -7
- package/assets/templates/views/category/props.ts +7 -0
- package/assets/templates/views/category/registry.tsx +19 -0
- package/assets/templates/views/category/schema.ts +13 -0
- package/assets/templates/views/home/assembly/assembly.json +9 -0
- package/assets/templates/views/home/assembly/index.skeleton.tsx +22 -4
- package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +1 -1
- package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +2 -2
- package/assets/templates/views/home/data.ts +9 -0
- package/assets/templates/views/home/index.tsx +23 -5
- package/assets/templates/views/home/registry.tsx +2 -2
- package/assets/templates/views/product/data.ts +4 -2
- package/assets/templates/views/product/index.tsx +49 -5
- package/assets/templates/views/product/props.ts +7 -0
- package/assets/templates/views/product/registry.tsx +25 -0
- package/assets/templates/views/product/schema.ts +13 -0
- package/assets/templates/views/static/content.ts +4 -4
- package/assets/templates/views/static/index.tsx +1 -1
- package/bin/storefront.js +1231 -154
- package/examples/merchandising/category-led.json +37 -0
- package/examples/merchandising/collection-led.json +37 -0
- package/package.json +11 -7
- package/recipes/archetypes.json +9 -22
- package/recipes/catalog.json +40 -1
- package/recipes/features.json +25 -2
- package/recipes/recipe.schema.json +1 -1
- package/recipes/recipes.json +32 -30
- package/recipes/sections.json +10 -0
- package/recipes/validate.mjs +54 -0
- package/scripts/candidate.mjs +4 -4
- package/scripts/composers.mjs +25 -5
- package/scripts/env-file.mjs +86 -0
- package/scripts/file-classes.mjs +5 -0
- package/scripts/migrate-english-routes.mjs +276 -0
- package/scripts/pinning.mjs +74 -5
- package/scripts/plan-apply.mjs +149 -5
- package/scripts/reconcile.mjs +49 -11
- package/scripts/render-templates.mjs +97 -44
- package/scripts/section-config.mjs +253 -0
- package/scripts/starter.mjs +17 -0
- package/scripts/theme/color.mjs +185 -0
- package/scripts/theme/css-theme.mjs +233 -0
- package/scripts/theme/fonts.mjs +123 -0
- package/scripts/theme/import-export.mjs +125 -0
- package/scripts/theme/preset.mjs +93 -0
- package/scripts/theme/theme-plan.mjs +292 -0
- package/scripts/theme/tokens.mjs +137 -0
- package/scripts/view-assembly.mjs +47 -1
- package/skills/sf-feature/SKILL.md +32 -19
- package/skills/sf-setup/SKILL.md +79 -0
- package/skills/sf-theme-gen/SKILL.md +99 -36
- package/skills/sf-theme-gen/references/shadcn-theming.md +87 -0
- package/skills/{shadcn-theming/references/templates → sf-theme-gen/references}/theme-setup.md +15 -19
- package/skills/{shadcn-theming → sf-theme-gen}/references/theming-guide.md +6 -11
- package/skills/sf-view-gen/SKILL.md +63 -40
- package/skills/sf-view-gen/assets/recipes/archetypes.json +9 -22
- package/skills/sf-view-gen/assets/recipes/catalog.json +40 -1
- package/skills/sf-view-gen/assets/recipes/features.json +25 -2
- package/skills/sf-view-gen/assets/recipes/recipes.json +32 -30
- package/skills/sf-view-gen/assets/recipes/sections.json +10 -0
- package/skills/sf-view-gen/assets/templates/views/sections/products.client.tsx +100 -0
- package/skills/sf-view-gen/assets/templates/views/sections/products.tsx +19 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.products.ts +39 -0
- package/skills/{json-render-core/SKILL.md → sf-view-gen/references/json-render-core.md} +4 -27
- package/skills/{json-render-next/SKILL.md → sf-view-gen/references/json-render-next.md} +5 -8
- package/skills/{json-render-react/SKILL.md → sf-view-gen/references/json-render-react.md} +3 -5
- package/skills/{json-render-shadcn/SKILL.md → sf-view-gen/references/json-render-shadcn.md} +3 -5
- package/skills/sf-view-gen/references/views.md +28 -37
- package/skills/sf-view-gen/scripts/validate.mjs +54 -0
- package/skills/storefront-workflow/SKILL.md +94 -31
- package/skills/{storefront-best-practices/reference/design.md → storefront-workflow/references/design-consistency.md} +13 -29
- package/CHANGELOG.md +0 -84
- package/assets/templates/views/user-panel/index.tsx +0 -106
- package/assets/templates/views/user-panel/registry.tsx +0 -54
- package/assets/templates/views/user-panel/schema.ts +0 -79
- package/commands/sf-add-view.md +0 -57
- package/commands/sf-feature.md +0 -28
- package/commands/sf-theme.md +0 -26
- package/commands/sf-view.md +0 -67
- package/skills/building-storefronts/SKILL.md +0 -178
- package/skills/building-storefronts/references/frontend-integration.md +0 -229
- package/skills/json-render-remotion/SKILL.md +0 -111
- package/skills/json-render-solid/SKILL.md +0 -204
- package/skills/nextjs-shadcn/SKILL.md +0 -367
- package/skills/nextjs-shadcn/references/architecture.md +0 -499
- package/skills/nextjs-shadcn/references/project-setup.md +0 -162
- package/skills/nextjs-shadcn/references/shadcn-platform.md +0 -261
- package/skills/nextjs-shadcn/references/sidebar.md +0 -274
- package/skills/nextjs-shadcn/references/styling.md +0 -555
- package/skills/shadcn-component-discovery/SKILL.md +0 -273
- package/skills/shadcn-component-discovery/references/registries.md +0 -226
- package/skills/shadcn-theming/SKILL.md +0 -104
- package/skills/storefront-best-practices/SKILL.md +0 -421
- package/skills/storefront-best-practices/reference/components/breadcrumbs.md +0 -123
- package/skills/storefront-best-practices/reference/components/cart-popup.md +0 -189
- package/skills/storefront-best-practices/reference/components/country-selector.md +0 -298
- package/skills/storefront-best-practices/reference/components/footer.md +0 -112
- package/skills/storefront-best-practices/reference/components/hero.md +0 -241
- package/skills/storefront-best-practices/reference/components/megamenu.md +0 -239
- package/skills/storefront-best-practices/reference/components/navbar.md +0 -397
- package/skills/storefront-best-practices/reference/components/popups.md +0 -221
- package/skills/storefront-best-practices/reference/components/product-card.md +0 -125
- package/skills/storefront-best-practices/reference/components/product-reviews.md +0 -217
- package/skills/storefront-best-practices/reference/components/product-slider.md +0 -174
- package/skills/storefront-best-practices/reference/components/search.md +0 -101
- package/skills/storefront-best-practices/reference/connecting-to-backend.md +0 -391
- package/skills/storefront-best-practices/reference/features/promotions.md +0 -307
- package/skills/storefront-best-practices/reference/features/wishlist.md +0 -230
- package/skills/storefront-best-practices/reference/layouts/account.md +0 -380
- package/skills/storefront-best-practices/reference/layouts/cart.md +0 -316
- package/skills/storefront-best-practices/reference/layouts/checkout.md +0 -486
- package/skills/storefront-best-practices/reference/layouts/home-page.md +0 -264
- package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +0 -231
- package/skills/storefront-best-practices/reference/layouts/product-details.md +0 -527
- package/skills/storefront-best-practices/reference/layouts/product-listing.md +0 -520
- package/skills/storefront-best-practices/reference/layouts/static-pages.md +0 -356
- package/skills/storefront-best-practices/reference/medusa.md +0 -307
- /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/mobile-responsiveness.md +0 -0
- /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/seo.md +0 -0
|
@@ -1,555 +0,0 @@
|
|
|
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) - `yarn add lucide-react`
|
|
127
|
-
2. **tabler** - `yarn add @tabler/icons-react`
|
|
128
|
-
3. **hugeicons** - `yarn add @hugeicons/react @hugeicons/core-free-icons` (the old `hugeicons-react` package is deprecated)
|
|
129
|
-
4. **phosphor** - `yarn add @phosphor-icons/react`
|
|
130
|
-
5. **remixicon** - `yarn 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
|
-
yarn 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
|
-
yarn 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
|
-
yarn add tailwind-scrollbar-hide
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
```css
|
|
553
|
-
/* globals.css (Tailwind v4 — no config file needed) */
|
|
554
|
-
@import "tailwind-scrollbar-hide/v4";
|
|
555
|
-
```
|