@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,520 @@
1
+ # Product Listing Page Layout
2
+
3
+ ## Contents
4
+
5
+ - [Overview](#overview)
6
+ - [Reusable Component Architecture](#reusable-component-architecture-recommended)
7
+ - [Decision: Pagination vs Infinite Scroll vs Load More](#decision-pagination-vs-infinite-scroll-vs-load-more)
8
+ - [Decision: Filter Pattern Selection](#decision-filter-pattern-selection)
9
+ - [Product Grid Layout](#product-grid-layout)
10
+ - [Filtering Strategy](#filtering-strategy)
11
+ - [Sorting Strategy](#sorting-strategy)
12
+ - [Backend Integration](#backend-integration)
13
+ - [Empty and No Results States](#empty-and-no-results-states)
14
+ - [Performance Optimization](#performance-optimization)
15
+ - [Mobile Optimization](#mobile-optimization)
16
+ - [Checklist](#checklist)
17
+
18
+ ## Overview
19
+
20
+ Primary browsing interface where users compare products, apply filters, and navigate to product details. Critical for product discovery and conversion.
21
+
22
+ ### Key Requirements
23
+
24
+ - Responsive product grid (3-4 columns desktop, 2 mobile)
25
+ - Filtering (categories, price, attributes)
26
+ - Sorting options (price, popularity, newest)
27
+ - Pagination, infinite scroll, or load more
28
+ - Results count and active filter indicators
29
+ - Clear "no results" state with suggestions
30
+ - Fast loading and filtering (<1s filter updates)
31
+ - Backend integration for dynamic filtering
32
+
33
+ ### Reusable Component Architecture (RECOMMENDED)
34
+
35
+ **Build product listing as a reusable component that works across multiple pages:**
36
+
37
+ ✅ **Use the same product listing component for:**
38
+ - "Shop All" page (all products, no category filter)
39
+ - Category pages (filtered by specific category)
40
+ - Search results page (filtered by search query)
41
+ - Sale/Promotion pages (filtered by discount/promotion)
42
+ - Collection pages (curated product sets)
43
+ - Brand pages (filtered by brand)
44
+
45
+ **Benefits of reusable approach:**
46
+ - Single source of truth for product browsing UI
47
+ - Consistent filtering, sorting, and pagination behavior across entire site
48
+ - Easier maintenance (fix bugs once, applies everywhere)
49
+ - Better user experience (familiar interface on every product browsing page)
50
+ - Significantly less code duplication
51
+
52
+ **What to make configurable:**
53
+ - Initial filter parameters (category ID, search query, promotion ID, brand, etc.)
54
+ - Page title and breadcrumbs
55
+ - Whether to show filters sidebar (some pages may hide certain filters)
56
+ - Default sort order (category: featured, search: relevance, sale: discount %)
57
+ - Number of products per page
58
+ - Filter options available (hide category filter on category pages, etc.)
59
+
60
+ **Common mistake:**
61
+ - ❌ Creating separate components/pages for "Shop All", category pages, and search results with duplicated filtering/sorting/pagination logic
62
+ - ✅ Build one reusable ProductListing component that accepts filter parameters and reuse it across all product browsing pages
63
+
64
+ ### Routing Pattern
65
+
66
+ **CRITICAL: Always use dynamic routes for category pages, NEVER static pages.**
67
+
68
+ Category/listing pages must use dynamic routes that accept a parameter (handle, slug, or category ID):
69
+
70
+ **Correct examples:**
71
+ - Next.js App Router: `app/categories/[handle]/page.tsx`
72
+ - Next.js Pages Router: `pages/categories/[handle].tsx`
73
+ - SvelteKit: `routes/categories/[handle]/+page.svelte`
74
+ - TanStack Start: `routes/categories/$handle.tsx`
75
+ - Remix: `routes/categories.$handle.tsx`
76
+
77
+ **Wrong examples:**
78
+ - ❌ `pages/categories/women.tsx` (static file per category)
79
+ - ❌ `pages/categories/men.tsx` (doesn't scale)
80
+
81
+ Fetch category products in the dynamic route based on the handle/ID parameter from the URL.
82
+
83
+ ## Decision: Pagination vs Infinite Scroll vs Load More
84
+
85
+ This is a critical ecommerce decision that affects user experience, SEO, and technical implementation.
86
+
87
+ ### Use Pagination When:
88
+
89
+ **User needs:**
90
+ - Return to specific result pages
91
+ - Precise control over browsing
92
+ - Professional/research shopping (compare systematically)
93
+ - B2B shoppers (procurement, large orders)
94
+
95
+ **Product characteristics:**
96
+ - Position matters (rankings, bestsellers)
97
+ - Large catalog with stable ordering
98
+ - Products require careful comparison
99
+
100
+ **Technical benefits:**
101
+ - SEO-friendly (unique URL per page)
102
+ - Better for indexing and crawling
103
+ - Easier back button support
104
+ - Lower memory usage
105
+
106
+ **Implementation:**
107
+ ```typescript
108
+ // URL structure: /products?page=2&category=shirts
109
+ // Each page has unique URL for SEO
110
+ ```
111
+
112
+ **Best for:**
113
+ - Desktop-heavy audience
114
+ - B2B ecommerce
115
+ - Product comparison shopping
116
+ - Catalog with 100+ products
117
+
118
+ ### Use Infinite Scroll When:
119
+
120
+ **User needs:**
121
+ - Exploratory browsing behavior
122
+ - Mobile-first experience
123
+ - Seamless discovery flow
124
+ - Fashion/visual shopping
125
+
126
+ **Product characteristics:**
127
+ - Visual-heavy products (fashion, art, photography)
128
+ - Impulse purchases
129
+ - Discovery-focused (Pinterest-style)
130
+
131
+ **Technical considerations:**
132
+ - More complex to implement
133
+ - Requires careful SEO handling (pagination URLs still needed)
134
+ - Higher memory usage (all loaded products stay in DOM)
135
+ - Need to handle browser back button carefully
136
+
137
+ **Implementation:**
138
+ ```typescript
139
+ // Load more when user scrolls to bottom
140
+ // Keep pagination in URL for SEO: /products?page=2
141
+ // Use Intersection Observer API for detection
142
+ ```
143
+
144
+ **Best for:**
145
+ - Mobile-first stores (>60% mobile traffic)
146
+ - Fashion, home decor, visual products
147
+ - Younger demographic (18-34)
148
+ - Discovery-focused shopping
149
+
150
+ ### Use "Load More" Button When:
151
+
152
+ **Benefits of compromise:**
153
+ - User controls when to load (not automatic)
154
+ - Footer remains accessible (important for policies, contact)
155
+ - Better for slower connections (international users)
156
+ - Accessibility friendly (no automatic loading)
157
+ - Lower memory usage than infinite scroll
158
+
159
+ **Implementation:**
160
+ ```typescript
161
+ // Button triggers next page load
162
+ // Append products to existing grid
163
+ // Show count: "Load 24 More Products"
164
+ ```
165
+
166
+ **Best for:**
167
+ - International audience (varying connection speeds)
168
+ - Footer content is important (legal, policies, contact)
169
+ - Accessibility concerns with infinite scroll
170
+ - Compromise between pagination and infinite scroll
171
+
172
+ ### Hybrid Approach (Recommended):
173
+
174
+ Combine patterns based on context:
175
+ - Pagination for SEO (canonical URLs)
176
+ - Infinite scroll for UX (on user interaction)
177
+ - Load more for control (user-triggered)
178
+
179
+ **Example:**
180
+ ```typescript
181
+ // Desktop: Pagination at bottom + infinite scroll option
182
+ // Mobile: Infinite scroll with pagination URLs for SEO
183
+ // All: Preserve scroll position on back button
184
+ ```
185
+
186
+ ## Decision: Filter Pattern Selection
187
+
188
+ ### Sidebar Filters (Desktop)
189
+
190
+ **Use when:**
191
+ - Many filter options (5+ categories)
192
+ - Complex product attributes
193
+ - Power users (B2B, professional shoppers)
194
+ - Desktop-heavy traffic
195
+
196
+ **Layout:**
197
+ - Left sidebar (250-320px wide)
198
+ - Sticky position (scrolls with page)
199
+ - Collapsible sections (accordion)
200
+ - Apply immediately (no "Apply" button)
201
+
202
+ ### Top Filters (Desktop)
203
+
204
+ **Use when:**
205
+ - Few filter options (2-4 key filters)
206
+ - Maximize grid space (full-width layout)
207
+ - Simple product categories
208
+ - Visual-first products (fashion)
209
+
210
+ **Layout:**
211
+ - Horizontal filter bar above grid
212
+ - Dropdowns or button toggles
213
+ - Limited options (price, category, brand)
214
+ - Compact design
215
+
216
+ ### Drawer Filters (Mobile - Always)
217
+
218
+ **Pattern:**
219
+ - "Filters" button at top (shows active count badge)
220
+ - Slide-out drawer (full-screen or 80% width)
221
+ - Accordion sections
222
+ - "Apply" button at bottom (batch filtering)
223
+ - "Clear All" option
224
+
225
+ **Why batch filtering on mobile:**
226
+ - Prevents multiple re-renders on slow connections
227
+ - User can adjust multiple filters before applying
228
+ - Better mobile UX (less disruptive)
229
+
230
+ ## Product Grid Layout
231
+
232
+ **Responsive columns:**
233
+ - Large desktop (>1440px): 4 columns
234
+ - Desktop (1024-1440px): 3-4 columns
235
+ - Tablet (768-1024px): 3 columns
236
+ - Mobile (< 768px): 2 columns
237
+
238
+ **Adjust based on product type:**
239
+ - Fashion/lifestyle: 3-4 columns (more visible at once)
240
+ - Electronics/detailed: 2-3 columns (larger cards, more detail)
241
+ - Furniture/large items: 2-3 columns (showcase details)
242
+
243
+ **Product card essentials:**
244
+ - Product image (primary)
245
+ - Title (truncated to 2 lines)
246
+ - Price (Medusa: display as-is, don't divide by 100)
247
+ - Optional: Rating, badges, wishlist
248
+ - See product-card.md for detailed guidelines
249
+
250
+ **Grid spacing:**
251
+ - 16-24px gap (desktop)
252
+ - 12-16px gap (mobile)
253
+ - Equal height rows (optional, improves visual consistency)
254
+
255
+ ## Filtering Strategy
256
+
257
+ ### Filter Types by Purpose
258
+
259
+ **Category filters:**
260
+ - Multi-select checkboxes
261
+ - Hierarchical (parent-child categories)
262
+ - Show product count per category
263
+ - Example: "Shirts (24)" "T-Shirts (12)"
264
+
265
+ **Price range filter:**
266
+ - Range slider (drag min/max)
267
+ - Or: Predefined ranges ("$0-$50", "$50-$100")
268
+ - Update dynamically as products filtered
269
+ - Show min/max from current results
270
+
271
+ **Attribute filters (Size, Color, Brand):**
272
+ - Multi-select checkboxes
273
+ - Visual swatches for colors
274
+ - Show available options based on current filters
275
+ - Gray out unavailable combinations
276
+
277
+ **Availability filters:**
278
+ - "In Stock" checkbox
279
+ - "On Sale" checkbox
280
+ - "New Arrivals" checkbox
281
+ - Single purpose, clear value
282
+
283
+ ### Filter Behavior
284
+
285
+ **Filter persistence:**
286
+ - Save in URL parameters (shareable, bookmarkable)
287
+ - Example: `/products?category=shirts&price=0-50&color=blue`
288
+ - Restore filters on page reload
289
+ - Clear all filters should reset URL
290
+
291
+ ### Active Filters Display
292
+
293
+ **Show active filters:**
294
+ - Above product grid
295
+ - Pill/tag format: "Blue ✕" "Under $50 ✕"
296
+ - Click X to remove individual filter
297
+ - "Clear All" link to remove all filters
298
+ - Count: "3 filters active"
299
+
300
+ ## Sorting Strategy
301
+
302
+ ### Common Sort Options
303
+
304
+ **Essential options:**
305
+ - **Featured** (default): Store's recommended order (bestsellers, promoted)
306
+ - **Price: Low to High**: Budget-conscious shoppers
307
+ - **Price: High to Low**: Premium product seekers
308
+ - **Newest**: Fashion, tech, time-sensitive products
309
+ - **Best Selling**: Social proof, popular choices
310
+ - **Top Rated**: Quality-focused shoppers
311
+
312
+ **Advanced options:**
313
+ - Name: A-Z (alphabetical)
314
+ - Discount: Highest % off (sale hunters)
315
+ - Reviews: Most reviewed (validation seekers)
316
+
317
+ ### Sort Implementation
318
+
319
+ **Display:**
320
+ - Dropdown above product grid (right-aligned)
321
+ - Label: "Sort by:" or just dropdown
322
+ - Update products immediately on selection
323
+ - Show current sort in URL: `/products?order=-created_at`
324
+
325
+ **Backend integration:**
326
+ - Pass sort parameter to API (check backend docs for parameter name)
327
+ - Common parameters: `order`, `sort`, `sort_by`
328
+ - Common values: `-created_at` (desc), `+price` (asc), `-price` (desc)
329
+
330
+ **Preserve filters:**
331
+ - Sorting doesn't clear filters
332
+ - Maintains all active filters
333
+ - Updates URL with sort parameter
334
+
335
+ ## Backend Integration
336
+
337
+ ### Fetching Products
338
+
339
+ **Query parameters to include:**
340
+ - Category/collection filter (if applicable)
341
+ - Pagination (limit, offset or cursor)
342
+ - Sort order
343
+ - Filter values (price, attributes, etc.)
344
+ - For Medusa: `region_id` (required for correct pricing)
345
+
346
+ Check backend API documentation for exact parameter names and formats.
347
+
348
+ ### Available Filters
349
+
350
+ **Dynamic filter updates:**
351
+ - Show only relevant filters for current category
352
+ - Display product count per filter option
353
+ - Gray out options with 0 products
354
+ - Update available options when filters change
355
+
356
+ ### URL State Management
357
+
358
+ **Filter URL structure pattern:**
359
+ `/products?category_id=123&order=-created_at&page=2&price=0-50`
360
+
361
+ **Benefits:**
362
+ - Shareable links
363
+ - Bookmarkable searches
364
+ - Browser back/forward works correctly
365
+ - SEO-friendly (crawlable filter combinations)
366
+
367
+ **Implementation approach:**
368
+ - Read filters from URL query parameters on page load
369
+ - Update URL when filters change using URLSearchParams and history.pushState
370
+ - Parse URL parameters to reconstruct filter state
371
+
372
+ ## Empty and No Results States
373
+
374
+ ### No Products in Category
375
+
376
+ **When category is empty:**
377
+ - Message: "No products available yet"
378
+ - Subtext: "Check back soon for new arrivals"
379
+ - CTA: "Browse all products" or "Go to home"
380
+ - Alternative: Show related categories
381
+ - Optional: Newsletter signup for notifications
382
+
383
+ ### No Results from Filters
384
+
385
+ **When filters too restrictive:**
386
+ - Message: "No products match your filters"
387
+ - Subtext: "Try removing some filters or adjusting your criteria"
388
+ - **Prominent "Clear All Filters" button**
389
+ - Show which filters might be too restrictive
390
+ - Suggestions: "Try expanding price range" or "Remove brand filter"
391
+
392
+ **Example:**
393
+ ```
394
+ No products found
395
+
396
+ You filtered by:
397
+ - Color: Blue
398
+ - Size: XXL
399
+ - Price: $0-$20
400
+
401
+ Try:
402
+ • Removing size filter (only 2 XXL products)
403
+ • Expanding price range
404
+ • [Clear All Filters]
405
+ ```
406
+
407
+ ### No Results from Search
408
+
409
+ **When search query returns nothing:**
410
+ - Message: "No results for '[query]'"
411
+ - Suggestions: Check spelling, try different keywords
412
+ - CTA: Browse popular categories
413
+ - Show search suggestions (similar queries)
414
+ - Display popular or trending products
415
+
416
+ ## Performance Optimization
417
+
418
+ ### Lazy Loading Images
419
+
420
+ **Implementation:**
421
+ - Load images as they come into viewport
422
+ - Use Intersection Observer API
423
+ - Show placeholder or blur-up effect
424
+ - Improves initial page load significantly
425
+
426
+ **Critical for ecommerce:**
427
+ - Product listings have 24-100+ images per page
428
+ - Lazy loading reduces initial load by 60-80%
429
+ - Faster perceived performance
430
+
431
+ ### Virtual Scrolling (Advanced)
432
+
433
+ **When to use:**
434
+ - Very large catalogs (500+ products visible)
435
+ - Infinite scroll with memory concerns
436
+ - Performance issues with many DOM elements
437
+
438
+ **How it works:**
439
+ - Only render visible products + buffer
440
+ - Reuse DOM elements as user scrolls
441
+ - Maintains scroll position
442
+ - Libraries: react-window, react-virtuoso
443
+
444
+ **Tradeoff:**
445
+ - Complex implementation
446
+ - Better performance for large lists
447
+ - Required for catalogs with 1000+ products loaded
448
+
449
+ ### Filter Performance
450
+
451
+ **Optimistic UI:**
452
+ - Update grid immediately (predicted results)
453
+ - Show loading overlay briefly
454
+ - Replace with real results
455
+ - Better perceived performance
456
+
457
+ ## Mobile Optimization
458
+
459
+ **Critical mobile patterns:**
460
+
461
+ **2-column grid:**
462
+ - Maximum 2 products per row
463
+ - Larger touch targets
464
+ - Simplified cards (essential info only)
465
+ - Remove hover effects
466
+
467
+ **Filter drawer:**
468
+ - Full-screen or 80% width drawer
469
+ - "Filters" button with badge count
470
+ - Batch apply (don't re-fetch on each change)
471
+ - Clear all at top
472
+
473
+ **Sticky filter/sort bar:**
474
+ - Fixed at top while scrolling
475
+ - Quick access to filters and sorting
476
+ - Shows active filter count
477
+ - Higher engagement rates
478
+
479
+ **Infinite scroll default:**
480
+ - Better mobile UX than pagination
481
+ - Natural scrolling behavior
482
+ - Keep pagination URLs for SEO
483
+ - Handle back button correctly
484
+
485
+ **Performance:**
486
+ - Lazy load images (critical on mobile)
487
+ - Limit initial products (12-24)
488
+ - Optimize image sizes for mobile
489
+ - Fast filter updates (<1s)
490
+
491
+ ## Checklist
492
+
493
+ **Essential product listing features:**
494
+
495
+ - [ ] **RECOMMENDED: Product listing built as reusable component**
496
+ - [ ] Reusable component works for: shop all, category pages, search results, sale pages
497
+ - [ ] Component accepts filter parameters (categoryId, searchQuery, promotionId, etc.)
498
+ - [ ] Responsive grid (3-4 columns desktop, 2 mobile)
499
+ - [ ] Decision made: Pagination vs infinite scroll vs load more
500
+ - [ ] Filter pattern selected: Sidebar (desktop) vs drawer (mobile)
501
+ - [ ] Filters fetched from backend dynamically
502
+ - [ ] Filter options show product count
503
+ - [ ] Active filters displayed above grid (removable pills)
504
+ - [ ] "Clear all filters" button prominent
505
+ - [ ] Sorting options (featured, price, newest, bestselling)
506
+ - [ ] Sort updates products without clearing filters
507
+ - [ ] Filters and sort persist in URL (shareable)
508
+ - [ ] Results count displayed ("Showing 1-24 of 156 products")
509
+ - [ ] Empty state: "No products match filters" with suggestions
510
+ - [ ] "Clear all filters" prominent when no results
511
+ - [ ] Product prices displayed correctly (Medusa: as-is, not divided)
512
+ - [ ] Lazy loading for images (Intersection Observer)
513
+ - [ ] Loading state for filter changes (< 1s)
514
+ - [ ] Mobile: Filter drawer with batch apply
515
+ - [ ] Mobile: 2-column grid maximum
516
+ - [ ] Mobile: Sticky filter/sort button
517
+ - [ ] Pagination URLs for SEO (even with infinite scroll)
518
+ - [ ] Back button support (restore filters, scroll position)
519
+ - [ ] Keyboard accessible (tab through filters, enter to apply)
520
+ - [ ] ARIA labels on filters (role="group", aria-label)