@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.
Files changed (75) hide show
  1. package/README.md +119 -0
  2. package/bin/install.js +116 -0
  3. package/commands/sf-add-view.md +21 -0
  4. package/commands/sf-init.md +16 -0
  5. package/commands/sf-theme.md +15 -0
  6. package/commands/sf-view.md +20 -0
  7. package/package.json +40 -0
  8. package/recipes/archetype.schema.json +39 -0
  9. package/recipes/archetypes.json +148 -0
  10. package/recipes/recipe.schema.json +59 -0
  11. package/recipes/recipes.json +90 -0
  12. package/recipes/sections.json +46 -0
  13. package/recipes/validate.mjs +190 -0
  14. package/skills/building-storefronts/SKILL.md +178 -0
  15. package/skills/building-storefronts/references/frontend-integration.md +229 -0
  16. package/skills/json-render-core/SKILL.md +291 -0
  17. package/skills/json-render-next/SKILL.md +194 -0
  18. package/skills/json-render-react/SKILL.md +298 -0
  19. package/skills/json-render-remotion/SKILL.md +111 -0
  20. package/skills/json-render-shadcn/SKILL.md +159 -0
  21. package/skills/json-render-solid/SKILL.md +204 -0
  22. package/skills/nextjs-shadcn/SKILL.md +303 -0
  23. package/skills/nextjs-shadcn/references/architecture.md +499 -0
  24. package/skills/nextjs-shadcn/references/project-setup.md +127 -0
  25. package/skills/nextjs-shadcn/references/shadcn-platform.md +258 -0
  26. package/skills/nextjs-shadcn/references/sidebar.md +274 -0
  27. package/skills/nextjs-shadcn/references/styling.md +555 -0
  28. package/skills/sf-scaffold/SKILL.md +118 -0
  29. package/skills/sf-theme-gen/SKILL.md +44 -0
  30. package/skills/sf-view-gen/SKILL.md +94 -0
  31. package/skills/shadcn-component-discovery/SKILL.md +273 -0
  32. package/skills/shadcn-component-discovery/references/registries.md +226 -0
  33. package/skills/shadcn-theming/SKILL.md +104 -0
  34. package/skills/shadcn-theming/references/templates/theme-setup.md +109 -0
  35. package/skills/shadcn-theming/references/theming-guide.md +90 -0
  36. package/skills/storefront-best-practices/SKILL.md +421 -0
  37. package/skills/storefront-best-practices/reference/components/breadcrumbs.md +123 -0
  38. package/skills/storefront-best-practices/reference/components/cart-popup.md +189 -0
  39. package/skills/storefront-best-practices/reference/components/country-selector.md +298 -0
  40. package/skills/storefront-best-practices/reference/components/footer.md +112 -0
  41. package/skills/storefront-best-practices/reference/components/hero.md +241 -0
  42. package/skills/storefront-best-practices/reference/components/megamenu.md +239 -0
  43. package/skills/storefront-best-practices/reference/components/navbar.md +397 -0
  44. package/skills/storefront-best-practices/reference/components/popups.md +221 -0
  45. package/skills/storefront-best-practices/reference/components/product-card.md +125 -0
  46. package/skills/storefront-best-practices/reference/components/product-reviews.md +217 -0
  47. package/skills/storefront-best-practices/reference/components/product-slider.md +174 -0
  48. package/skills/storefront-best-practices/reference/components/search.md +101 -0
  49. package/skills/storefront-best-practices/reference/connecting-to-backend.md +391 -0
  50. package/skills/storefront-best-practices/reference/design.md +388 -0
  51. package/skills/storefront-best-practices/reference/features/promotions.md +307 -0
  52. package/skills/storefront-best-practices/reference/features/wishlist.md +230 -0
  53. package/skills/storefront-best-practices/reference/layouts/account.md +380 -0
  54. package/skills/storefront-best-practices/reference/layouts/cart.md +316 -0
  55. package/skills/storefront-best-practices/reference/layouts/checkout.md +486 -0
  56. package/skills/storefront-best-practices/reference/layouts/home-page.md +264 -0
  57. package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +231 -0
  58. package/skills/storefront-best-practices/reference/layouts/product-details.md +527 -0
  59. package/skills/storefront-best-practices/reference/layouts/product-listing.md +520 -0
  60. package/skills/storefront-best-practices/reference/layouts/static-pages.md +356 -0
  61. package/skills/storefront-best-practices/reference/medusa.md +307 -0
  62. package/skills/storefront-best-practices/reference/mobile-responsiveness.md +183 -0
  63. package/skills/storefront-best-practices/reference/seo.md +195 -0
  64. package/templates/app/app/[[...slug]]/page.tsx +17 -0
  65. package/templates/app/app/[[...slug]]/renderer.tsx +10 -0
  66. package/templates/app/app/globals.css +101 -0
  67. package/templates/app/app/layout.tsx +35 -0
  68. package/templates/app/lib/__STOREFRONT__/catalog.ts +132 -0
  69. package/templates/app/lib/__STOREFRONT__/handlers.ts +33 -0
  70. package/templates/app/lib/__STOREFRONT__/registry.tsx +134 -0
  71. package/templates/app/lib/__STOREFRONT__/runtime.ts +25 -0
  72. package/templates/app/lib/__STOREFRONT__/spec/home.ts +62 -0
  73. package/templates/app/lib/__STOREFRONT__/spec/index.ts +59 -0
  74. package/templates/app/lib/__STOREFRONT__/spec/types.ts +14 -0
  75. package/templates/app/lib/__STOREFRONT__/state.ts +35 -0
@@ -0,0 +1,397 @@
1
+ # Navbar Component
2
+
3
+ ## Contents
4
+
5
+ - [Overview](#overview)
6
+ - [Decision: Simple Dropdown vs Megamenu](#decision-simple-dropdown-vs-megamenu)
7
+ - [Key Ecommerce Patterns](#key-ecommerce-patterns)
8
+ - [Layout Structure](#layout-structure)
9
+ - [Accessibility Essentials](#accessibility-essentials)
10
+ - [Common Ecommerce Mistakes](#common-ecommerce-mistakes)
11
+ - [Backend Integration](#backend-integration)
12
+ - [Checklist](#checklist)
13
+
14
+ ## Overview
15
+
16
+ Primary navigation for ecommerce storefronts. Desktop: horizontal menu with category links. Mobile: hamburger drawer with accordion subcategories.
17
+
18
+ ### ⚠️ CRITICAL: NEVER Hardcode Categories
19
+
20
+ **ALWAYS fetch categories dynamically from the backend. NEVER hardcode static category arrays.**
21
+
22
+ ❌ **WRONG - DO NOT DO THIS:**
23
+ ```typescript
24
+ // WRONG - Static hardcoded categories
25
+ const categories = [
26
+ { name: "Women", href: "/categories/women" },
27
+ { name: "Men", href: "/categories/men" },
28
+ { name: "Accessories", href: "/categories/accessories" }
29
+ ]
30
+ ```
31
+
32
+ ✅ **CORRECT - Fetch from backend:**
33
+ ```typescript
34
+ // CORRECT - Fetch categories dynamically
35
+ const [categories, setCategories] = useState([])
36
+
37
+ useEffect(() => {
38
+ fetch(`${apiUrl}/store/product-categories`)
39
+ .then(res => res.json())
40
+ .then(data => setCategories(data.product_categories))
41
+ }, [])
42
+ ```
43
+
44
+ **Why this matters:**
45
+ - Categories change frequently (new categories, renamed, reordered)
46
+ - Hardcoded categories become outdated immediately
47
+ - Requires code changes every time categories change
48
+ - Cannot scale to stores with dynamic catalogs
49
+ - Defeats the purpose of headless commerce
50
+
51
+ ### Key Requirements
52
+
53
+ - Desktop: Horizontal category links, cart/account/search right-aligned
54
+ - Mobile: Hamburger drawer, cart stays visible in header (not hidden in drawer)
55
+ - **CRITICAL: Fetch categories from backend dynamically (NEVER hardcode static arrays)**
56
+ - Sticky: Recommended for easy cart access while browsing
57
+ - Real-time updates: Cart count, login state, category changes
58
+
59
+ ## Decision: Simple Dropdown vs Megamenu
60
+
61
+ **Use Simple Dropdown when:**
62
+ - <10 top-level categories
63
+ - Flat or shallow hierarchy (1-2 levels deep)
64
+ - Minimal subcategories per parent
65
+ - Focused/specialized product catalog
66
+
67
+ **Use Megamenu when:**
68
+ - 10+ top-level categories
69
+ - Deep hierarchy (3+ levels)
70
+ - Need to showcase featured products in navigation
71
+ - Complex product catalog
72
+ - Fashion, electronics, or large inventory
73
+
74
+ **Mobile**: Always use drawer with accordion pattern, never megamenu on mobile.
75
+
76
+ See [megamenu.md](megamenu.md) for megamenu implementation details.
77
+
78
+ ## Key Ecommerce Patterns
79
+
80
+ ### Cart Indicator (CRITICAL)
81
+
82
+ **Always visible on both desktop and mobile:**
83
+ - Desktop: Top-right, cart icon + count badge
84
+ - Mobile: Top-right in header (NOT hidden in hamburger drawer)
85
+ - This is non-negotiable - users expect cart always accessible
86
+
87
+ **Badge display:**
88
+ - Shows item count (NOT price - confusing when variants change)
89
+ - Only visible when cart has items (count > 0)
90
+ - Show actual count up to 99, then "99+"
91
+ - Position: Top-right corner of cart icon
92
+ - ARIA label: `aria-label="Shopping cart with 3 items"`
93
+
94
+ **Real-time updates:**
95
+ - Update count immediately when items added (optimistic UI)
96
+ - No page refresh required
97
+ - Sync with backend cart state
98
+ - Handle errors gracefully (restore count if add fails)
99
+
100
+ **Click behavior:**
101
+ - Option 1: Navigate to cart page
102
+ - Option 2: Open cart popup/drawer (see cart-popup.md)
103
+ - Choice depends on store type (see cart-popup.md for decision criteria)
104
+
105
+ ✅ **CORRECT:**
106
+ - Cart icon visible in mobile header
107
+ - Badge shows count (not price)
108
+ - Updates in real-time without page refresh
109
+ - 44x44px touch target
110
+ - Links to cart or opens cart popup
111
+
112
+ ❌ **WRONG:**
113
+ - Hiding cart in mobile hamburger drawer (users can't find it)
114
+ - Showing price in badge (€25.99) instead of count
115
+ - Cart count doesn't update until page refresh
116
+ - No visual feedback when items added
117
+
118
+ ### Category Navigation
119
+
120
+ **CRITICAL: Fetch dynamically from backend (NEVER hardcode):**
121
+
122
+ ❌ **WRONG - These are all incorrect approaches:**
123
+ ```typescript
124
+ // WRONG - Hardcoded array
125
+ const categories = ["Women", "Men", "Kids", "Accessories"]
126
+
127
+ // WRONG - Static object array
128
+ const categories = [
129
+ { id: 1, name: "Women", slug: "women" },
130
+ { id: 2, name: "Men", slug: "men" }
131
+ ]
132
+
133
+ // WRONG - Importing static data
134
+ import { categories } from "./categories.ts"
135
+ ```
136
+
137
+ ✅ **CORRECT - Fetch from backend API:**
138
+ - Medusa: Use SDK category list method (verify exact method with docs/MCP)
139
+ - Other backends: Call categories endpoint (check API documentation)
140
+ - Fetch on component mount or during server-side rendering
141
+
142
+ **Why dynamic fetching is mandatory:**
143
+ - Store owners add/remove/rename categories frequently
144
+ - Category order and hierarchy changes
145
+ - Multi-language stores need translated category names
146
+ - Featured categories rotate (seasonal, promotions)
147
+ - Hardcoded values require developer intervention for simple changes
148
+
149
+ **Caching strategy:**
150
+ - Cache categories (revalidate on interval or manual trigger)
151
+ - Use SWR, TanStack Query, or framework-level caching
152
+ - Revalidate every 5-10 minutes or on page navigation
153
+ - Update immediately when backend categories change
154
+
155
+ **Organization:**
156
+ - 4-7 top-level categories ideal (max 10 on desktop)
157
+ - Order comes from backend (respects admin's ordering)
158
+ - Keep "Sale" or "New Arrivals" prominent if backend provides it
159
+ - Maximum 2 levels in simple dropdown (category → subcategory)
160
+ - Deeper hierarchies: Use megamenu or separate category pages
161
+
162
+ **Desktop behavior:**
163
+ - Horizontal links with hover dropdowns for subcategories
164
+ - Slight hover delay to prevent accidental triggers
165
+ - Click parent to navigate to category page
166
+ - Click child to navigate to subcategory
167
+
168
+ **Mobile behavior:**
169
+ - All categories in hamburger drawer
170
+ - Accordion pattern for subcategories (expand/collapse)
171
+ - Close drawer on category click (except expanding accordion)
172
+ - Scrollable drawer if categories exceed viewport height
173
+
174
+ ✅ **CORRECT:**
175
+ - Categories fetched from backend API on mount
176
+ - Cache with revalidation strategy
177
+ - Respects backend ordering and hierarchy
178
+ - 4-7 top-level items on desktop (based on what backend returns)
179
+ - Accordion for mobile subcategories
180
+ - Consistent ordering across devices
181
+
182
+ ❌ **WRONG:**
183
+ - Hardcoded category array in component (NEVER DO THIS)
184
+ - Static categories imported from file (NEVER DO THIS)
185
+ - No cache invalidation (stale categories)
186
+ - Too many top-level items (>10, overwhelming)
187
+ - Different category order on desktop vs mobile
188
+ - Categories don't update when backend changes
189
+
190
+ ### User Account Indicator
191
+
192
+ **Two states based on authentication:**
193
+
194
+ **Logged out:**
195
+ - Desktop: "Sign In" or "Log In" text + user icon
196
+ - Mobile: User icon only
197
+ - Click navigates to login page
198
+ - Clear call-to-action
199
+
200
+ **Logged in:**
201
+ - Desktop: User name, initials, or email + dropdown
202
+ - Mobile: User name/initials or icon → account page
203
+ - Dropdown menu (desktop): My Account, Orders, Wishlist, Sign Out
204
+ - Fetch current user from backend authentication state
205
+
206
+ **Authentication state management:**
207
+ - Check auth state from backend (not just localStorage)
208
+ - Update immediately on login/logout events
209
+ - Handle session expiration gracefully
210
+ - Sync across tabs if possible
211
+
212
+ ✅ **CORRECT:**
213
+ - Shows "Sign In" when logged out
214
+ - Shows user identifier when logged in
215
+ - Dropdown with account actions
216
+ - Checks backend auth state (not just client state)
217
+
218
+ ❌ **WRONG:**
219
+ - No indication of login state
220
+ - Relies solely on localStorage (can be stale)
221
+ - No dropdown for account actions when logged in
222
+ - Missing logout option
223
+
224
+ ### Mobile Navigation Pattern
225
+
226
+ **Hamburger drawer:**
227
+ - Trigger: Hamburger icon (top-left)
228
+ - Drawer: Slides from left, 80-85% width, full height, scrollable
229
+ - Backdrop: Semi-transparent overlay, click to close
230
+ - Content: All categories with accordion subcategories
231
+
232
+ **CRITICAL: Keep cart in header:**
233
+ - Cart icon stays in mobile header (top-right)
234
+ - Don't hide cart inside drawer
235
+ - Users expect cart always accessible
236
+ - Same for search icon if using icon-only search
237
+
238
+ **Account in drawer:**
239
+ - Logged out: "Sign In" link in drawer header or top of menu
240
+ - Logged in: User name/initials in drawer header with link to account
241
+
242
+ **Close behavior:**
243
+ - Close button (X) in drawer header
244
+ - Click backdrop overlay
245
+ - Navigate to category (drawer closes)
246
+ - Escape key
247
+
248
+ ✅ **CORRECT:**
249
+ - Cart stays in mobile header (visible)
250
+ - Hamburger opens drawer from left
251
+ - Backdrop overlay dims background
252
+ - Close on navigation or backdrop click
253
+ - Scrollable drawer for long menus
254
+
255
+ ❌ **WRONG:**
256
+ - Cart hidden inside hamburger drawer (cardinal sin)
257
+ - Full-screen drawer (no backdrop)
258
+ - Drawer doesn't close on navigation
259
+ - Not scrollable (categories cut off)
260
+
261
+ ### Bottom Navigation (Alternative for Mobile)
262
+
263
+ **When to use:**
264
+ - Store has 3-5 key sections (Home, Browse, Cart, Account, Search)
265
+ - App-like experience desired
266
+ - Frequent switching between sections
267
+ - Not suitable for complex category hierarchies
268
+
269
+ **Pattern:**
270
+ - Fixed bar at bottom of screen (mobile only)
271
+ - Icon + label for each section
272
+ - Highlight active section
273
+ - 5 items maximum
274
+ - Direct navigation, no dropdowns
275
+
276
+ ## Layout Structure
277
+
278
+ **Desktop:**
279
+ - Left: Logo → Homepage
280
+ - Center: Category links (horizontal)
281
+ - Right: Search, Account, Cart
282
+
283
+ **Mobile:**
284
+ - Left: Hamburger
285
+ - Center: Logo
286
+ - Right: Cart (+ Search icon optional)
287
+
288
+ **Sticky recommended:**
289
+ - Keeps cart/account accessible while scrolling
290
+ - Use `position: sticky` or `position: fixed`
291
+ - Solid background color (hide scrolling content)
292
+ - Adequate z-index to stay above content
293
+
294
+ ## Accessibility Essentials
295
+
296
+ **Ecommerce-specific ARIA:**
297
+ - Cart count: `aria-live="polite"` to announce changes (e.g., "3 items in cart")
298
+ - Mobile drawer: `role="dialog"`, `aria-modal="true"`
299
+ - Hamburger button: `aria-label="Open navigation menu"`, `aria-expanded="false"`
300
+ - Active page: `aria-current="page"` on current category link
301
+ - Dropdown indicators: `aria-expanded`, `aria-controls` for megamenu relationships
302
+
303
+ **Keyboard navigation:**
304
+ - Tab through all links/buttons
305
+ - Enter/Space to activate
306
+ - Escape to close mobile menu or dropdowns
307
+ - Visible focus indicators (outline/ring)
308
+
309
+ **Generic accessibility applies:**
310
+ - Semantic HTML (`<header>`, `<nav>`)
311
+ - Icon buttons need ARIA labels
312
+ - 4.5:1 color contrast minimum
313
+ - 44x44px touch targets on mobile
314
+
315
+ ## Common Ecommerce Mistakes
316
+
317
+ ❌ **CRITICAL: Hardcoded static categories** - NEVER create static category arrays like `const categories = ["Women", "Men"]` or import from static files. ALWAYS fetch from backend API. Categories change constantly - new categories added, names changed, ordering updated. Hardcoded categories require developer intervention for simple changes and defeat the purpose of dynamic commerce platforms. This is the #1 most common mistake.
318
+
319
+ ❌ **Hiding cart in mobile drawer** - Users expect cart always visible. Keep cart icon in header (top-right), not hidden inside hamburger menu.
320
+
321
+ ❌ **No real-time cart updates** - Update count immediately when items added (optimistic UI). Don't require page refresh.
322
+
323
+ ❌ **Showing price in cart badge** - Show item count (number), not total price. Price display confuses when variants have different quantities.
324
+
325
+ ❌ **No cache invalidation** - Categories become stale when backend changes. Revalidate periodically (5-10 min) or on manual trigger.
326
+
327
+ ❌ **Hover-only dropdowns on mobile** - Use click/tap interactions. Hover doesn't work on touch devices.
328
+
329
+ ❌ **Desktop navigation on mobile** - Use hamburger drawer pattern, not horizontal menu that doesn't fit.
330
+
331
+ ❌ **Inconsistent category order** - Same order on desktop and mobile for consistency. Respect backend's category ordering.
332
+
333
+ ## Backend Integration
334
+
335
+ ### Category Fetching (CRITICAL - NEVER Hardcode)
336
+
337
+ **Implementation patterns:**
338
+
339
+ **Client-side fetching:**
340
+ - Fetch categories in useEffect on mount
341
+ - Store in state (use appropriate types for Medusa: StoreProductCategory)
342
+ - Handle loading and error states
343
+ - Map categories to navigation links
344
+ - Use category.id as key, category.handle for URL, category.name for display
345
+
346
+ **With caching (RECOMMENDED):**
347
+ - Use TanStack Query with queryKey ['categories']
348
+ - Set staleTime: 5-10 minutes (categories rarely change)
349
+ - Automatic loading/error states
350
+ - Request deduplication if multiple components need categories
351
+
352
+ **Server-side fetching:**
353
+ - Fetch in server component or load function
354
+ - No loading state needed (rendered on server)
355
+ - Better for SEO
356
+
357
+ **Cart state synchronization pattern:**
358
+ - Subscribe to global cart state (Context)
359
+ - Update navbar cart count when cart changes
360
+ - Handle optimistic updates (show new count immediately on add to cart)
361
+ - Sync with backend on events or interval
362
+
363
+ **Authentication state pattern:**
364
+ - Check auth state from backend on mount
365
+ - Listen for login/logout events
366
+ - Update account indicator immediately
367
+ - Handle session expiration gracefully
368
+
369
+ **Category update triggers:**
370
+ - On page load/navigation
371
+ - On manual refresh trigger
372
+ - On revalidation interval (5-10 minutes)
373
+ - After admin updates categories (webhook or polling)
374
+
375
+ ## Checklist
376
+
377
+ **Essential navbar features:**
378
+
379
+ - [ ] **CRITICAL: Categories fetched dynamically from backend API (NOT hardcoded arrays)**
380
+ - [ ] **CRITICAL: No static category imports or hardcoded category lists**
381
+ - [ ] Desktop: Horizontal category links
382
+ - [ ] Mobile: Hamburger drawer with accordion
383
+ - [ ] Cart icon visible on both desktop and mobile header (NOT hidden in drawer)
384
+ - [ ] Cart badge shows item count (not price)
385
+ - [ ] Cart count updates in real-time
386
+ - [ ] Categories use backend ordering (not manual ordering)
387
+ - [ ] Account indicator shows login state
388
+ - [ ] Logo links to homepage
389
+ - [ ] 4-7 top-level categories displayed (max 10)
390
+ - [ ] Mobile drawer closes on navigation
391
+ - [ ] Sticky navigation (recommended)
392
+ - [ ] 44x44px minimum touch targets
393
+ - [ ] ARIA labels on icon buttons
394
+ - [ ] `aria-live` on cart count for screen readers
395
+ - [ ] Keyboard accessible with visible focus states
396
+ - [ ] Categories cached with revalidation strategy (5-10 min)
397
+ - [ ] Error handling for failed category fetch
@@ -0,0 +1,221 @@
1
+ # Popups Component
2
+
3
+ ## Contents
4
+
5
+ - [Overview](#overview)
6
+ - [When to Use Popups](#when-to-use-popups)
7
+ - [Ecommerce Popup Types](#ecommerce-popup-types)
8
+ - [Timing and Triggers](#timing-and-triggers)
9
+ - [Frequency Management](#frequency-management)
10
+ - [Mobile Considerations](#mobile-considerations)
11
+ - [Checklist](#checklist)
12
+
13
+ ## Overview
14
+
15
+ Popups (modals/overlays) appear over main content to capture attention for specific actions: newsletter signups, promotional offers, exit-intent offers.
16
+
17
+ **Assumed knowledge**: AI agents know how to build modals with close buttons and backdrop overlays. This focuses on ecommerce popup patterns.
18
+
19
+ **Critical balance**: Effective for conversions when used sparingly, intrusive and annoying when overused. Timing and frequency are critical for ecommerce.
20
+
21
+ ## When to Use Popups
22
+
23
+ **Use popups when:**
24
+
25
+ - Offering significant value (10-20% first-purchase discount, free shipping)
26
+ - Time-sensitive promotions (flash sale ending soon)
27
+ - Exit-intent to recover abandoning visitors (last chance offer)
28
+ - First-time visitor welcome (one-time only)
29
+ - Important announcements (shipping delays, policy changes)
30
+
31
+ **Don't use popups for:**
32
+
33
+ - Every page visit (extremely annoying)
34
+ - Multiple popups per session
35
+ - Immediate page load (users haven't seen site yet)
36
+ - Mobile users (especially full-screen takeovers - very disruptive)
37
+ - Users who already signed up or dismissed
38
+
39
+ **Consider alternatives:**
40
+
41
+ - Top banner: Less intrusive, always visible, good for ongoing promotions
42
+ - Inline forms: Homepage or footer newsletter signup, non-blocking
43
+ - Slide-in (corner): From bottom-right, less disruptive than center popup
44
+ - Post-purchase: Ask for email after successful order (high conversion)
45
+
46
+ **Popups are best when:** Need immediate attention, high-value offer justifies interruption, exit-intent (last chance).
47
+
48
+ ## Ecommerce Popup Types
49
+
50
+ ### 1. First-Purchase Discount
51
+
52
+ **Purpose**: Convert first-time visitors with discount incentive.
53
+
54
+ **Content:**
55
+ - Headline: "Welcome! Get 10% Off Your First Order"
56
+ - Email input
57
+ - Discount code or automatic application
58
+ - Subscribe button: "Get My Discount"
59
+
60
+ **Timing**: After 30-60 seconds on site OR after viewing 2-3 products (engagement signal).
61
+
62
+ **Frequency**: Once per user (cookie/localStorage). Don't show to returning customers.
63
+
64
+ ### 2. Newsletter Signup
65
+
66
+ **Purpose**: Grow email list for marketing.
67
+
68
+ **Content:**
69
+ - Value proposition: "Get exclusive deals and early access"
70
+ - Email input
71
+ - Subscribe button
72
+ - Optional: First-purchase discount incentive (10-15% off)
73
+
74
+ **Timing**: After 50% scroll OR 60 seconds on site.
75
+
76
+ **Frequency**: Once per session. If dismissed, don't show for 30 days.
77
+
78
+ ### 3. Exit-Intent Popup
79
+
80
+ **Purpose**: Recover abandoning visitors with last-chance offer.
81
+
82
+ **Trigger**: Mouse moves toward browser close/back button (desktop only).
83
+
84
+ **Content:**
85
+ - Urgency: "Wait! Don't Miss Out"
86
+ - Offer: "Take 10% Off Your Order" or "Free Shipping Today Only"
87
+ - Email capture (optional): "Send me the code"
88
+ - CTA: "Claim Offer" or "Continue Shopping"
89
+
90
+ **Best for**: Cart abandoners, product page exits, first-time visitors.
91
+
92
+ **Frequency**: Once per session. Don't show if user already added to cart or on checkout.
93
+
94
+ ### 4. Cart Abandonment Reminder
95
+
96
+ **Purpose**: Remind user of items in cart before leaving.
97
+
98
+ **Trigger**: Exit-intent when cart has items but user navigating away.
99
+
100
+ **Content:**
101
+ - "Your Cart is Waiting"
102
+ - Show cart summary (items, total)
103
+ - CTA: "Complete Your Order" or "View Cart"
104
+ - Optional incentive: "Complete in 10 minutes for free shipping"
105
+
106
+ **Frequency**: Once per session with items in cart.
107
+
108
+ ### 5. Promotional Announcement
109
+
110
+ **Purpose**: Announce sales, new arrivals, or site-wide events.
111
+
112
+ **Content:**
113
+ - Headline: "Flash Sale: 40% Off Everything"
114
+ - Subtext: "Ends in 3 hours"
115
+ - CTA: "Shop Now"
116
+
117
+ **Timing**: Immediate on page load (if major event), OR after 30 seconds.
118
+
119
+ **Frequency**: Once per day during promotion period.
120
+
121
+ ## Timing and Triggers
122
+
123
+ **Time-based:**
124
+ - 30-60 seconds after page load (enough time to browse)
125
+ - Never immediate (0 seconds) - users need to see site first
126
+
127
+ **Engagement-based:**
128
+ - After 50% scroll (shows interest)
129
+ - After viewing 2-3 products (qualified visitor)
130
+ - After adding to cart (exit-intent only)
131
+
132
+ **Exit-intent:**
133
+ - Mouse moves toward close/back button (desktop)
134
+ - Scroll-up toward address bar (mobile - less reliable)
135
+ - Only trigger once per session
136
+ - Don't trigger on checkout pages (interrupts purchase)
137
+
138
+ **Page-specific:**
139
+ - Homepage: Welcome/discount popup
140
+ - Product pages: Exit-intent with product-specific offer
141
+ - Cart page: Don't use popups (already engaged)
142
+ - Checkout: Never use popups (critical flow)
143
+
144
+ ## Frequency Management
145
+
146
+ **Critical for UX**: Don't show same popup repeatedly to same user.
147
+
148
+ **Implementation:**
149
+
150
+ 1. **Cookie/localStorage tracking**: Store dismissal/signup with timestamp
151
+ 2. **Respect dismissals**: If user closes popup, don't show for 30 days
152
+ 3. **Signed-up users**: Never show newsletter popup again
153
+ 4. **Session limits**: Max 1 popup per session
154
+ 5. **Time cooldown**: If dismissed, wait 30 days before showing again
155
+
156
+ **Example tracking:**
157
+ ```javascript
158
+ // On popup dismiss
159
+ localStorage.setItem('popup_dismissed', Date.now())
160
+ localStorage.setItem('popup_type', 'welcome_discount')
161
+
162
+ // Before showing popup
163
+ const dismissedTime = localStorage.getItem('popup_dismissed')
164
+ const daysSince = (Date.now() - dismissedTime) / (1000 * 60 * 60 * 24)
165
+ if (daysSince < 30) {
166
+ // Don't show popup
167
+ }
168
+ ```
169
+
170
+ **Progressive disclosure:**
171
+ - Session 1: Welcome discount popup
172
+ - Session 2+: Exit-intent only (if applicable)
173
+ - Never stack multiple popups
174
+
175
+ ## Mobile Considerations
176
+
177
+ **Mobile popups are MORE intrusive:**
178
+ - Smaller screen = popup takes more space
179
+ - Harder to close (small X button)
180
+ - Disrupts mobile browsing flow
181
+ - Can hurt mobile SEO (Google penalty for intrusive interstitials)
182
+
183
+ **Mobile best practices:**
184
+
185
+ 1. **Use sparingly**: Consider top banner or inline forms instead
186
+ 2. **Make easily dismissable**: Large close button (44x44px), tap outside to close
187
+ 3. **Delay longer**: 60+ seconds instead of 30 seconds
188
+ 4. **Smaller size**: 90% width max, not full-screen
189
+ 5. **Exit-intent**: Less reliable on mobile, avoid
190
+ 6. **Google penalty**: Avoid full-screen popups on mobile (hurts rankings)
191
+
192
+ **Mobile alternative**: Sticky bottom bar (less intrusive)
193
+ - "Get 10% Off - Sign Up" with email input
194
+ - Always visible but doesn't block content
195
+ - Better mobile UX than popup
196
+
197
+ ## Checklist
198
+
199
+ **Essential features:**
200
+
201
+ - [ ] Clear value proposition (discount, benefit)
202
+ - [ ] Single focused CTA
203
+ - [ ] Easy to close (X button, backdrop click, Escape key)
204
+ - [ ] Delayed timing (30-60s, not immediate)
205
+ - [ ] Frequency management (localStorage/cookie tracking)
206
+ - [ ] Respect dismissals (30-day cooldown)
207
+ - [ ] Never show to signed-up users
208
+ - [ ] Max 1 popup per session
209
+ - [ ] Exit-intent for cart abandoners (desktop only)
210
+ - [ ] Don't show on checkout pages
211
+ - [ ] Mobile: Use sparingly, consider alternatives
212
+ - [ ] Mobile: Large close button (44x44px)
213
+ - [ ] Mobile: Not full-screen (90% width max)
214
+ - [ ] Email validation before submit
215
+ - [ ] Loading state on submit
216
+ - [ ] Success message or redirect
217
+ - [ ] Keyboard accessible (Tab, Escape, Enter)
218
+ - [ ] `role="dialog"` and `aria-modal="true"`
219
+ - [ ] Focus trap (keep focus within popup)
220
+ - [ ] ARIA label on close button
221
+ - [ ] Screen reader announcements on open