diff --git a/.architecture-rules.yaml b/.architecture-rules.yaml
index 6c858f24..32d425f2 100644
--- a/.architecture-rules.yaml
+++ b/.architecture-rules.yaml
@@ -686,6 +686,122 @@ frontend_component_rules:
encouraged_patterns:
- "{% from 'shared/macros/forms.html' import"
+ - id: "FE-008"
+ name: "Use number_stepper macro for quantity inputs"
+ severity: "warning"
+ description: |
+ Use the shared number_stepper macro instead of raw .
+ This ensures consistent styling, proper dark mode support, and hides
+ native browser spinners that render inconsistently.
+
+ WRONG (raw number input):
+
+
+ RIGHT (use macro):
+ {% from 'shared/macros/inputs.html' import number_stepper %}
+ {{ number_stepper(model='quantity', min=1, max=99) }}
+
+ Exceptions (use noqa):
+ - ID fields where users type specific values (add {# noqa: FE-008 #})
+ - Fields that are better as regular text inputs
+
+ Suppress with:
+ - {# noqa: FE-008 #} on the line or at file level
+ pattern:
+ file_pattern: "app/templates/**/*.html"
+ anti_patterns:
+ - 'type="number"'
+ - "type='number'"
+ exceptions:
+ - "shared/macros/inputs.html" # The macro file itself
+ - "components.html" # Showcase page
+
+ - id: "FE-009"
+ name: "Use product_card macro for product displays"
+ severity: "info"
+ description: |
+ Use the shared product_card macro for consistent product presentation.
+ Import from shared/macros/shop/product-card.html.
+
+ Features:
+ - Size variants (sm, md, lg)
+ - Sale badge, new badge, out-of-stock badge
+ - Star ratings with review count
+ - Wishlist toggle button
+ - Quick add to cart button
+
+ Usage:
+ {% from 'shared/macros/shop/product-card.html' import product_card %}
+ {{ product_card(product_var='item', size='md') }}
+ pattern:
+ file_pattern: "app/templates/shop/**/*.html"
+ encouraged_patterns:
+ - "{% from 'shared/macros/shop/product-card.html' import"
+
+ - id: "FE-010"
+ name: "Use product_grid macro for product listings"
+ severity: "info"
+ description: |
+ Use the shared product_grid macro for responsive product grids.
+ Import from shared/macros/shop/product-grid.html.
+
+ Features:
+ - Responsive column count (auto or fixed)
+ - Loading state with skeleton placeholders
+ - Empty state with customizable icon/message
+ - Integrated product_card component
+
+ Usage:
+ {% from 'shared/macros/shop/product-grid.html' import product_grid %}
+ {{ product_grid(products_var='products', loading_var='loading') }}
+ pattern:
+ file_pattern: "app/templates/shop/**/*.html"
+ encouraged_patterns:
+ - "{% from 'shared/macros/shop/product-grid.html' import"
+
+ - id: "FE-011"
+ name: "Use add_to_cart macros for cart interactions"
+ severity: "info"
+ description: |
+ Use the shared add-to-cart macros for consistent cart functionality.
+ Import from shared/macros/shop/add-to-cart.html.
+
+ Available macros:
+ - add_to_cart_button() - Simple add to cart button
+ - add_to_cart_form() - Complete form with quantity selector
+ - buy_now_button() - Direct checkout button
+ - shop_quantity_selector() - Stock-aware quantity selector
+
+ Usage:
+ {% from 'shared/macros/shop/add-to-cart.html' import add_to_cart_form %}
+ {{ add_to_cart_form(product_var='product') }}
+ pattern:
+ file_pattern: "app/templates/shop/**/*.html"
+ encouraged_patterns:
+ - "{% from 'shared/macros/shop/add-to-cart.html' import"
+
+ - id: "FE-012"
+ name: "Use mini_cart macro for cart dropdown"
+ severity: "info"
+ description: |
+ Use the shared mini_cart macros for header cart functionality.
+ Import from shared/macros/shop/mini-cart.html.
+
+ Available macros:
+ - mini_cart_icon() - Cart icon with item count badge
+ - mini_cart_dropdown() - Expandable cart preview
+ - mini_cart() - Combined icon + dropdown
+ - cart_item() - Individual cart item display
+ - cart_summary() - Subtotal, shipping, total
+
+ Usage:
+ {% from 'shared/macros/shop/mini-cart.html' import mini_cart %}
+ {{ mini_cart(cart_var='cart', show_var='showCart') }}
+ pattern:
+ file_pattern: "app/templates/shop/**/*.html"
+ encouraged_patterns:
+ - "{% from 'shared/macros/shop/mini-cart.html' import"
+
# ============================================================================
# FRONTEND STYLING RULES
# ============================================================================
diff --git a/docs/development/architecture-rules.md b/docs/development/architecture-rules.md
index edfc6dbe..7f73d8d1 100644
--- a/docs/development/architecture-rules.md
+++ b/docs/development/architecture-rules.md
@@ -663,6 +663,124 @@ Show empty state when lists have no items.
---
+### Jinja Macro Rules (app/templates/**/*.html)
+
+#### MAC-001: Use Shared Macros for Repeated Patterns
+**Severity:** Error
+
+Never copy-paste HTML patterns. Use or create shared macros in `app/templates/shared/macros/`.
+
+```jinja
+{# ✅ Good - Using shared macro #}
+{% from 'shared/macros/tables.html' import table_wrapper, table_header %}
+{% call table_wrapper() %}
+ {{ table_header(['Name', 'Email', 'Status']) }}
+
...
+{% endcall %}
+
+{# ❌ Bad - Copy-pasting table HTML #}
+
+```
+
+#### MAC-002: Import Macros at Template Top
+**Severity:** Warning
+
+All macro imports should be at the top of the template, after `{% extends %}`.
+
+```jinja
+{# ✅ Good #}
+{% extends "admin/base.html" %}
+{% from 'shared/macros/pagination.html' import pagination %}
+{% from 'shared/macros/tables.html' import table_wrapper %}
+{% from 'shared/macros/alerts.html' import loading_state %}
+
+{% block content %}
+...
+{% endblock %}
+```
+
+#### MAC-003: Use {% call %} for Wrapper Macros
+**Severity:** Error
+
+Wrapper macros that use `{{ caller() }}` must be invoked with `{% call %}`.
+
+```jinja
+{# ✅ Good #}
+{% call table_wrapper() %}
+ {{ table_header(['Col1', 'Col2']) }}
+{% endcall %}
+
+{% call tabs_nav() %}
+ {{ tab_button('tab1', 'First') }}
+{% endcall %}
+
+{# ❌ Bad - Missing call block #}
+{{ table_wrapper() }} {# This won't render inner content! #}
+```
+
+#### MAC-004: Document New Macros
+**Severity:** Warning
+
+New macros must have JSDoc-style documentation comments.
+
+```jinja
+{# ✅ Good #}
+{#
+ Number Stepper
+ ==============
+ A number input with +/- buttons.
+
+ Parameters:
+ - model: Alpine.js x-model variable (required)
+ - min: Minimum value (default: 1)
+ - max: Maximum value (optional)
+
+ Usage:
+ {{ number_stepper(model='quantity', min=1, max=99) }}
+#}
+{% macro number_stepper(model, min=1, max=none) %}
+...
+{% endmacro %}
+```
+
+#### MAC-005: Add Components to Reference Page
+**Severity:** Warning
+
+New shared components should be added to `/admin/components` page with live demos.
+
+---
+
+### Frontend Component Rules (app/templates/**/*.html)
+
+#### FE-008: Use number_stepper Macro for Quantity Inputs
+**Severity:** Warning
+
+Use the shared `number_stepper` macro instead of raw `` for consistent styling and dark mode support.
+
+```jinja
+{# ✅ Good - Using number_stepper macro #}
+{% from 'shared/macros/inputs.html' import number_stepper %}
+{{ number_stepper(model='quantity', min=1, max=99) }}
+
+{# ❌ Bad - Raw number input #}
+
+```
+
+**Suppress with `noqa` for ID fields:**
+```jinja
+{# noqa: FE-008 - User ID is typed directly, not incremented #}
+
+```
+
+---
+
### Styling Rules (app/templates/**/*.html)
#### CSS-001: Use Tailwind Utility Classes
@@ -885,13 +1003,15 @@ All rules are defined in `.architecture-rules.yaml`. To modify rules:
| Backend | 20 | 15 | 5 |
| Frontend JS | 7 | 6 | 1 |
| Frontend Templates | 7 | 3 | 4 |
+| Frontend Macros | 5 | 2 | 3 |
+| Frontend Components | 1 | 0 | 1 |
| Frontend Styling | 4 | 1 | 3 |
| Naming | 5 | 3 | 2 |
| Security | 5 | 5 | 0 |
| Quality | 3 | 2 | 1 |
-| **Total** | **51** | **35** | **16** |
+| **Total** | **57** | **37** | **20** |
---
-**Last Updated:** 2025-12-04
-**Version:** 2.2
+**Last Updated:** 2025-12-07
+**Version:** 2.3
diff --git a/docs/frontend/shared/component-standards.md b/docs/frontend/shared/component-standards.md
new file mode 100644
index 00000000..8ee239ef
--- /dev/null
+++ b/docs/frontend/shared/component-standards.md
@@ -0,0 +1,461 @@
+# Frontend Component Standards
+
+**Version:** 1.0
+**Last Updated:** December 2025
+**Audience:** Frontend Developers
+
+---
+
+## Overview
+
+This document defines mandatory standards for all frontend components across Admin, Vendor, and Shop frontends. Following these standards ensures consistency, maintainability, and a unified user experience.
+
+---
+
+## Golden Rules
+
+1. **Use Jinja Macros** - Never copy-paste HTML patterns; use shared macros
+2. **Alpine.js for Interactivity** - All client-side logic uses Alpine.js
+3. **Dark Mode Required** - Every component must support dark mode
+4. **Accessibility First** - ARIA labels, keyboard navigation, focus states
+5. **Mobile Responsive** - Mobile-first design with Tailwind breakpoints
+
+---
+
+## Jinja Macro System
+
+### Available Macro Files
+
+All macros are located in `app/templates/shared/macros/`:
+
+| File | Purpose | Key Macros |
+|------|---------|------------|
+| `pagination.html` | Table pagination | `pagination()`, `pagination_simple()` |
+| `alerts.html` | Alerts and toasts | `loading_state()`, `error_state()`, `alert_dynamic()` |
+| `badges.html` | Status indicators | `badge()`, `status_badge()`, `role_badge()` |
+| `buttons.html` | Button variants | `btn_primary()`, `btn_secondary()`, `action_button()` |
+| `forms.html` | Form inputs | `form_input()`, `form_select()`, `form_textarea()` |
+| `tables.html` | Table components | `table_wrapper()`, `table_header()`, `table_empty_state()` |
+| `cards.html` | Card layouts | `stat_card()`, `card()`, `info_card()` |
+| `headers.html` | Page headers | `page_header()`, `page_header_flex()`, `refresh_button()` |
+| `modals.html` | Modal dialogs | `modal()`, `confirm_modal()`, `job_details_modal()` |
+| `tabs.html` | Tab navigation | `tabs_nav()`, `tabs_inline()`, `tab_button()` |
+| `inputs.html` | Specialized inputs | `search_autocomplete()`, `number_stepper()` |
+
+### Macro Usage Rules
+
+#### RULE 1: Always Import Before Use
+
+```jinja
+{# ✅ CORRECT - Import at top of template #}
+{% from 'shared/macros/tables.html' import table_wrapper, table_header %}
+{% from 'shared/macros/pagination.html' import pagination %}
+
+{% block content %}
+ {% call table_wrapper() %}
+ {{ table_header(['Name', 'Email', 'Status']) }}
+ ...
+ {% endcall %}
+ {{ pagination() }}
+{% endblock %}
+
+{# ❌ WRONG - Inline HTML instead of macro #}
+{% block content %}
+
+{% endblock %}
+```
+
+#### RULE 2: Use Macros for Repeated Patterns
+
+If you find yourself copying HTML more than once, create or use a macro.
+
+```jinja
+{# ✅ CORRECT - Using badge macro #}
+{{ status_badge(status='active') }}
+{{ status_badge(status='pending') }}
+{{ status_badge(status='inactive') }}
+
+{# ❌ WRONG - Copy-pasting badge HTML #}
+Active
+Pending
+```
+
+#### RULE 3: Use `{% call %}` for Wrapper Macros
+
+Macros that wrap content use the `caller()` pattern:
+
+```jinja
+{# ✅ CORRECT - Using call block #}
+{% call table_wrapper() %}
+ {{ table_header(['Column 1', 'Column 2']) }}
+ ...
+{% endcall %}
+
+{% call tabs_nav() %}
+ {{ tab_button('tab1', 'First Tab', icon='home') }}
+ {{ tab_button('tab2', 'Second Tab', icon='cog') }}
+{% endcall %}
+```
+
+#### RULE 4: Prefer Macro Parameters Over Custom CSS
+
+```jinja
+{# ✅ CORRECT - Using size parameter #}
+{{ number_stepper(model='qty', size='sm') }}
+{{ number_stepper(model='qty', size='lg') }}
+
+{# ❌ WRONG - Adding custom classes #}
+{{ number_stepper(model='qty') }}
+
+```
+
+---
+
+## Component Categories
+
+### 1. Layout Components
+
+#### Page Headers
+```jinja
+{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
+
+{% call page_header_flex(title='Page Title', subtitle='Optional description') %}
+ {{ refresh_button(onclick='refreshData()') }}
+
+{% endcall %}
+```
+
+#### Cards
+```jinja
+{% from 'shared/macros/cards.html' import stat_card %}
+
+{{ stat_card(
+ label='Total Users',
+ value='1,234',
+ icon='user-group',
+ color='purple'
+) }}
+```
+
+### 2. Data Display Components
+
+#### Tables with Pagination
+```jinja
+{% from 'shared/macros/tables.html' import table_wrapper, table_header, table_empty_state %}
+{% from 'shared/macros/pagination.html' import pagination %}
+
+{% call table_wrapper() %}
+ {{ table_header(['Name', 'Email', 'Status', 'Actions']) }}
+
+
+
+ ...
+
+
+
+{% endcall %}
+
+{{ table_empty_state(
+ message='No items found',
+ icon='inbox',
+ show_condition='!loading && items.length === 0'
+) }}
+
+{{ pagination() }}
+```
+
+#### Badges
+```jinja
+{% from 'shared/macros/badges.html' import status_badge %}
+
+{{ status_badge(status='active') }} {# Green #}
+{{ status_badge(status='pending') }} {# Yellow #}
+{{ status_badge(status='inactive') }} {# Red #}
+{{ status_badge(status='processing') }} {# Blue #}
+```
+
+### 3. Form Components
+
+#### Standard Inputs
+```jinja
+{% from 'shared/macros/forms.html' import form_input, form_select %}
+
+{{ form_input(
+ name='email',
+ label='Email Address',
+ type='email',
+ model='formData.email',
+ required=true,
+ error_var='errors.email'
+) }}
+
+{{ form_select(
+ name='status',
+ label='Status',
+ model='formData.status',
+ options=[
+ {'value': 'active', 'label': 'Active'},
+ {'value': 'inactive', 'label': 'Inactive'}
+ ]
+) }}
+```
+
+#### Number Stepper
+```jinja
+{% from 'shared/macros/inputs.html' import number_stepper %}
+
+{# Cart quantity #}
+{{ number_stepper(model='quantity', min=1, max=99, size='md') }}
+
+{# Batch size with step #}
+{{ number_stepper(model='batchSize', min=100, max=5000, step=100, size='lg') }}
+```
+
+#### Search Autocomplete
+```jinja
+{% from 'shared/macros/inputs.html' import search_autocomplete, selected_item_display %}
+
+{{ search_autocomplete(
+ search_var='searchQuery',
+ results_var='searchResults',
+ show_dropdown_var='showDropdown',
+ loading_var='searching',
+ select_action='selectItem(item)',
+ display_field='name',
+ secondary_field='email',
+ placeholder='Search users...'
+) }}
+
+{{ selected_item_display(
+ selected_var='selectedUser',
+ display_field='name',
+ secondary_field='email',
+ clear_action='clearSelection()'
+) }}
+```
+
+### 4. Navigation Components
+
+#### Tabs
+```jinja
+{% from 'shared/macros/tabs.html' import tabs_nav, tabs_inline, tab_button %}
+
+{# Full-width navigation tabs #}
+{% call tabs_nav() %}
+ {{ tab_button('overview', 'Overview', icon='home') }}
+ {{ tab_button('details', 'Details', icon='document') }}
+ {{ tab_button('settings', 'Settings', icon='cog') }}
+{% endcall %}
+
+{# Inline tabs with counts #}
+{% call tabs_inline() %}
+ {{ tab_button('all', 'All', count_var='items.length') }}
+ {{ tab_button('active', 'Active', count_var='activeCount') }}
+ {{ tab_button('archived', 'Archived', count_var='archivedCount') }}
+{% endcall %}
+```
+
+### 5. Feedback Components
+
+#### Alerts
+```jinja
+{% from 'shared/macros/alerts.html' import alert_dynamic, loading_state, error_state %}
+
+{{ alert_dynamic(type='success', message_var='successMessage', show_condition='successMessage') }}
+{{ alert_dynamic(type='error', message_var='errorMessage', show_condition='errorMessage') }}
+
+{{ loading_state(message='Loading data...', show_condition='loading') }}
+{{ error_state(title='Error', show_condition='error') }}
+```
+
+#### Modals
+```jinja
+{% from 'shared/macros/modals.html' import confirm_modal %}
+
+{{ confirm_modal(
+ show_var='showDeleteModal',
+ title='Confirm Delete',
+ message='Are you sure you want to delete this item?',
+ confirm_action='deleteItem()',
+ confirm_text='Delete',
+ confirm_class='bg-red-600 hover:bg-red-700'
+) }}
+```
+
+---
+
+## Dark Mode Requirements
+
+Every component MUST support dark mode using Tailwind's `dark:` prefix.
+
+### Color Pairing Reference
+
+| Element | Light Mode | Dark Mode |
+|---------|------------|-----------|
+| Background | `bg-white` | `dark:bg-gray-800` |
+| Card Background | `bg-gray-50` | `dark:bg-gray-900` |
+| Text Primary | `text-gray-700` | `dark:text-gray-200` |
+| Text Secondary | `text-gray-500` | `dark:text-gray-400` |
+| Border | `border-gray-300` | `dark:border-gray-600` |
+| Hover Background | `hover:bg-gray-100` | `dark:hover:bg-gray-700` |
+| Input Background | `bg-white` | `dark:bg-gray-700` |
+| Focus Ring | `focus:ring-purple-500` | `dark:focus:ring-purple-400` |
+
+### Example
+```html
+
+```
+
+---
+
+## Accessibility Requirements
+
+### Required ARIA Attributes
+
+```html
+{# Button with loading state #}
+
+
+{# Icon-only button #}
+
+
+{# Number stepper group #}
+
+
+
+
+
+```
+
+### Focus States
+
+All interactive elements must have visible focus states:
+```html
+
+```
+
+---
+
+## Icon System
+
+### Usage
+```html
+{# Standard icon #}
+
+
+{# Icon with color #}
+
+
+{# Inline icon in button #}
+
+```
+
+### Common Icons Reference
+
+| Icon | Usage |
+|------|-------|
+| `home` | Dashboard/Home |
+| `user` | Users/Profile |
+| `cog` | Settings |
+| `plus` | Add/Create |
+| `edit` | Edit/Modify |
+| `trash` | Delete |
+| `check-circle` | Success/Verified |
+| `x-circle` | Error/Failed |
+| `clock` | Pending/Time |
+| `refresh` | Reload/Sync |
+| `eye` | View/Details |
+| `download` | Download/Export |
+| `upload` | Upload/Import |
+| `search` | Search |
+| `filter` | Filter |
+| `minus` | Decrease |
+
+---
+
+## Creating New Components
+
+### When to Create a Macro
+
+Create a new macro when:
+- Pattern is used 3+ times across templates
+- Component has configurable options
+- Component requires consistent styling
+
+### Macro Template
+```jinja
+{#
+ Component Name
+ ==============
+ Brief description of what the component does.
+
+ Parameters:
+ - param1: Description (required/optional, default: value)
+ - param2: Description
+
+ Usage:
+ {{ component_name(param1='value', param2='value') }}
+#}
+{% macro component_name(
+ param1,
+ param2='default'
+) %}
+
+ {# Component HTML #}
+
+{% endmacro %}
+```
+
+### Checklist for New Components
+
+- [ ] Supports dark mode (`dark:` variants)
+- [ ] Has ARIA labels for accessibility
+- [ ] Documented with JSDoc-style comment
+- [ ] Added to `/admin/components` page
+- [ ] Added to this documentation
+- [ ] Uses existing design tokens (colors, spacing)
+- [ ] Tested in both light and dark mode
+- [ ] Works on mobile viewport
+
+---
+
+## Component Reference Page
+
+For live examples and copy-paste code, visit:
+
+**Admin Panel:** `/admin/components`
+
+This page includes:
+- Interactive demos
+- Code snippets with copy button
+- All available macros
+- Usage examples
+
+---
+
+## Related Documentation
+
+- [UI Components Quick Reference](ui-components-quick-reference.md)
+- [Icons Guide](../../development/icons-guide.md)
+- [Tailwind CSS Guide](../tailwind-css.md)
+- [Frontend Overview](../overview.md)
+- [Architecture Rules](../../development/architecture-rules.md)
diff --git a/docs/frontend/shared/ui-components-quick-reference.md b/docs/frontend/shared/ui-components-quick-reference.md
index d9979497..9c13491f 100644
--- a/docs/frontend/shared/ui-components-quick-reference.md
+++ b/docs/frontend/shared/ui-components-quick-reference.md
@@ -116,6 +116,108 @@
```
+## Number Stepper
+
+A number input with +/- buttons for quantity selection. Ideal for cart quantities, batch sizes, and product pages.
+
+### Basic Number Stepper
+```html
+{% from 'shared/macros/inputs.html' import number_stepper %}
+
+{# Basic usage - cart quantity #}
+{{ number_stepper(model='quantity', min=1, max=99) }}
+```
+
+### Size Variants
+```html
+{# Small - compact for tables/lists #}
+{{ number_stepper(model='item.qty', min=1, max='item.stock', size='sm') }}
+
+{# Medium (default) #}
+{{ number_stepper(model='quantity', min=1, max=99) }}
+
+{# Large - prominent placement #}
+{{ number_stepper(model='batchSize', min=100, max=5000, step=100, size='lg') }}
+```
+
+### With Disabled State
+```html
+{{ number_stepper(model='qty', min=1, disabled_var='isLoading') }}
+```
+
+### Number Stepper Parameters
+| Parameter | Default | Description |
+|-----------|---------|-------------|
+| `model` | required | Alpine.js x-model variable |
+| `min` | `1` | Minimum allowed value |
+| `max` | `none` | Maximum allowed value (can be Alpine.js expression) |
+| `step` | `1` | Increment/decrement step |
+| `size` | `'md'` | Size variant: `'sm'`, `'md'`, `'lg'` |
+| `disabled_var` | `none` | Alpine.js variable for disabled state |
+| `name` | `none` | Input name for form submission |
+| `id` | `none` | Input id attribute |
+| `label` | `'Quantity'` | Accessible label for screen readers |
+
+---
+
+## Tabs
+
+Tab navigation components for switching between content sections.
+
+### 🗂️ Navigation Tabs (with icons)
+```html
+{% from 'shared/macros/tabs.html' import tabs_nav, tab_button %}
+
+{% call tabs_nav() %}
+ {{ tab_button('dashboard', 'Dashboard', icon='home') }}
+ {{ tab_button('settings', 'Settings', icon='cog') }}
+ {{ tab_button('profile', 'Profile', icon='user') }}
+{% endcall %}
+
+
+
+ Dashboard content...
+
+```
+
+### 🗂️ Inline Tabs (with count badges)
+```html
+{% from 'shared/macros/tabs.html' import tabs_inline, tab_button %}
+
+
+ {% call tabs_inline() %}
+ {{ tab_button('all', 'All Items', count_var='allItems.length') }}
+ {{ tab_button('active', 'Active', count_var='activeItems.length') }}
+ {{ tab_button('archived', 'Archived', count_var='archivedItems.length') }}
+ {% endcall %}
+
Search...
+
+```
+
+### 🗂️ Tabs with Custom Click Handlers
+```html
+{% call tabs_nav() %}
+ {{ tab_button('database', 'Database Logs',
+ tab_var='logSource',
+ icon='database',
+ onclick="logSource = 'database'; loadDatabaseLogs()") }}
+ {{ tab_button('file', 'File Logs',
+ tab_var='logSource',
+ icon='document',
+ onclick="logSource = 'file'; loadFileLogs()") }}
+{% endcall %}
+```
+
+### Tab Button Parameters
+| Parameter | Default | Description |
+|-----------|---------|-------------|
+| `id` | required | Tab identifier for comparison |
+| `label` | required | Display text |
+| `tab_var` | `'activeTab'` | Alpine.js variable for active state |
+| `icon` | `none` | Optional icon name |
+| `count_var` | `none` | Alpine.js variable for count badge |
+| `onclick` | `none` | Custom click handler (overrides default) |
+
## Grid Layouts
### 2 Columns (Desktop)
@@ -223,6 +325,138 @@
7. Helper text: `text-xs text-gray-600`
8. Error text: `text-xs text-red-600`
+---
+
+## E-commerce Components (Shop Frontend)
+
+Reusable macros for shop/storefront functionality. Located in `app/templates/shared/macros/shop/`.
+
+### 🛍️ Product Card
+```html
+{% from 'shared/macros/shop/product-card.html' import product_card %}
+
+{# Basic product card #}
+{{ product_card(product_var='product') }}
+
+{# With size and options #}
+{{ product_card(
+ product_var='item',
+ size='lg',
+ show_rating=true,
+ show_quick_add=true,
+ show_wishlist=true
+) }}
+```
+
+**Size variants:** `sm` (compact), `md` (default), `lg` (featured)
+
+**Features:**
+- Sale badge (when `sale_price` exists)
+- "New" badge (when `is_new` is true)
+- Out of stock overlay
+- Star ratings with review count
+- Wishlist toggle button
+- Quick add to cart
+
+### 🛍️ Product Grid
+```html
+{% from 'shared/macros/shop/product-grid.html' import product_grid %}
+
+{# Basic grid #}
+{{ product_grid(products_var='products', loading_var='loading') }}
+
+{# With empty state #}
+{{ product_grid(
+ products_var='searchResults',
+ loading_var='searching',
+ empty_message='No products found',
+ empty_icon='search'
+) }}
+```
+
+**Features:**
+- Responsive columns (auto-adjusts or fixed)
+- Loading skeleton placeholders
+- Empty state with customizable icon/message
+
+### 🛒 Add to Cart
+```html
+{% from 'shared/macros/shop/add-to-cart.html' import add_to_cart_button, add_to_cart_form, buy_now_button %}
+
+{# Simple button #}
+{{ add_to_cart_button(action='addToCart()') }}
+
+{# Complete form with quantity #}
+{{ add_to_cart_form(product_var='product', size='md') }}
+
+{# Buy now button #}
+{{ buy_now_button(action='buyNow()') }}
+```
+
+**Macros available:**
+- `add_to_cart_button()` - Simple button with loading state
+- `add_to_cart_form()` - Form with quantity selector + button
+- `buy_now_button()` - Direct checkout button
+- `shop_quantity_selector()` - Stock-aware quantity input
+
+### 🛒 Mini Cart (Header)
+```html
+{% from 'shared/macros/shop/mini-cart.html' import mini_cart, mini_cart_icon %}
+
+{# Complete mini cart with dropdown #}
+{{ mini_cart(cart_var='cart', show_var='showCart') }}
+
+{# Just the icon with badge #}
+{{ mini_cart_icon(cart_var='cart', size='md') }}
+```
+
+**Macros available:**
+- `mini_cart()` - Combined icon + dropdown
+- `mini_cart_icon()` - Icon with item count badge
+- `mini_cart_dropdown()` - Dropdown panel
+- `cart_item()` - Individual item display
+- `cart_summary()` - Subtotal, shipping, total
+
+### E-commerce Alpine.js State
+```javascript
+// Required state variables for e-commerce components
+{
+ // Products
+ products: [],
+ loading: true,
+
+ // Cart
+ cart: {
+ items: [],
+ item_count: 0,
+ subtotal: 0,
+ total: 0
+ },
+ showCart: false,
+
+ // Add to cart
+ quantity: 1,
+ addingToCart: false,
+ addedToCart: false,
+
+ // Wishlist
+ toggleWishlist(product) {
+ product.in_wishlist = !product.in_wishlist;
+ },
+
+ // Cart actions
+ addToCart() {
+ this.addingToCart = true;
+ // API call...
+ },
+ removeFromCart(itemId) {
+ this.cart.items = this.cart.items.filter(i => i.id !== itemId);
+ }
+}
+```
+
+---
+
## Reference Page
Visit `/admin/components` for full component library with live examples!
diff --git a/docs/frontend/shop/ecommerce-components-proposal.md b/docs/frontend/shop/ecommerce-components-proposal.md
new file mode 100644
index 00000000..b8b5ad7d
--- /dev/null
+++ b/docs/frontend/shop/ecommerce-components-proposal.md
@@ -0,0 +1,622 @@
+# E-commerce Components Proposal
+
+**Version:** 1.0
+**Created:** December 2025
+**Status:** Proposal
+**Author:** Development Team
+
+---
+
+## Overview
+
+This document proposes a comprehensive set of reusable Jinja macro components for the shop frontend. These components will standardize the e-commerce experience across all vendor shops while supporting vendor-specific theming via CSS variables.
+
+---
+
+## Design Principles
+
+1. **Vendor Theming** - Use CSS variables (`var(--color-primary)`) for brand colors
+2. **Mobile First** - Responsive design starting from mobile
+3. **Performance** - Lazy loading, optimized images, minimal JS
+4. **Accessibility** - WCAG 2.1 AA compliant
+5. **Consistency** - Same UX patterns across all shops
+
+---
+
+## Proposed Components
+
+### Priority 1: Core Shopping Components
+
+#### 1.1 Product Card
+**File:** `app/templates/shared/macros/shop/product-card.html`
+
+A versatile product card for grids, carousels, and lists.
+
+```jinja
+{{ product_card(
+ product=product,
+ show_vendor=false,
+ show_rating=true,
+ show_quick_add=true,
+ size='md', {# sm, md, lg #}
+ layout='vertical' {# vertical, horizontal #}
+) }}
+```
+
+**Features:**
+- Product image with lazy loading
+- Sale badge / "New" badge
+- Product title (truncated)
+- Price (with strikethrough for sales)
+- Star rating
+- Quick "Add to Cart" button
+- Wishlist heart icon
+- Hover effects
+
+**Variants:**
+| Size | Use Case |
+|------|----------|
+| `sm` | Mobile grids, sidebars |
+| `md` | Standard product grids |
+| `lg` | Featured products, hero sections |
+
+---
+
+#### 1.2 Product Grid
+**File:** `app/templates/shared/macros/shop/product-grid.html`
+
+Responsive grid layout for product listings.
+
+```jinja
+{{ product_grid(
+ products=products,
+ columns={'sm': 2, 'md': 3, 'lg': 4},
+ gap='md',
+ show_empty_state=true
+) }}
+```
+
+**Features:**
+- Responsive column configuration
+- Loading skeleton state
+- Empty state with CTA
+- "Load more" or pagination integration
+
+---
+
+#### 1.3 Add to Cart Button
+**File:** `app/templates/shared/macros/shop/add-to-cart.html`
+
+Standardized add-to-cart functionality.
+
+```jinja
+{{ add_to_cart_button(
+ product_id='product.id',
+ variant_id='selectedVariant?.id',
+ quantity_model='quantity',
+ show_quantity=true,
+ size='md',
+ full_width=false
+) }}
+```
+
+**Features:**
+- Loading state during add
+- Success animation
+- Out of stock state
+- Quantity selector integration (uses `number_stepper`)
+- "Added!" feedback
+
+---
+
+#### 1.4 Quantity Selector (Shop Version)
+**File:** Already exists as `number_stepper` in `inputs.html`
+
+Shop-specific wrapper with stock validation:
+
+```jinja
+{{ shop_quantity_selector(
+ model='quantity',
+ max='product.stock',
+ disabled_var='addingToCart',
+ out_of_stock_var='product.stock === 0'
+) }}
+```
+
+---
+
+### Priority 2: Cart Components
+
+#### 2.1 Mini Cart (Header Dropdown)
+**File:** `app/templates/shared/macros/shop/mini-cart.html`
+
+Dropdown cart preview in the header.
+
+```jinja
+{{ mini_cart(
+ cart_var='cart',
+ max_items=3,
+ show_checkout_button=true
+) }}
+```
+
+**Features:**
+- Cart item count badge
+- Item thumbnails
+- Quick quantity adjust
+- Remove item button
+- Subtotal display
+- "View Cart" and "Checkout" CTAs
+
+---
+
+#### 2.2 Cart Item Row
+**File:** `app/templates/shared/macros/shop/cart-item.html`
+
+Individual cart line item.
+
+```jinja
+{{ cart_item(
+ item='item',
+ index='index',
+ show_image=true,
+ editable=true,
+ compact=false
+) }}
+```
+
+**Features:**
+- Product thumbnail
+- Product name + variant info
+- Unit price
+- Quantity stepper (using `number_stepper` with `size='sm'`)
+- Line total
+- Remove button
+- Stock warning if low
+
+---
+
+#### 2.3 Cart Summary
+**File:** `app/templates/shared/macros/shop/cart-summary.html`
+
+Order summary sidebar/section.
+
+```jinja
+{{ cart_summary(
+ cart_var='cart',
+ show_promo_code=true,
+ show_shipping_estimate=true,
+ checkout_url='/checkout'
+) }}
+```
+
+**Features:**
+- Subtotal
+- Discount code input
+- Applied discounts
+- Estimated shipping
+- Tax display
+- Order total
+- Checkout CTA
+- Secure payment badges
+
+---
+
+### Priority 3: Product Detail Components
+
+#### 3.1 Product Gallery
+**File:** `app/templates/shared/macros/shop/product-gallery.html`
+
+Image gallery with thumbnails and zoom.
+
+```jinja
+{{ product_gallery(
+ images='product.images',
+ show_thumbnails=true,
+ enable_zoom=true,
+ enable_fullscreen=true
+) }}
+```
+
+**Features:**
+- Main image display
+- Thumbnail navigation
+- Image zoom on hover
+- Fullscreen lightbox
+- Swipe support on mobile
+- Video support
+
+---
+
+#### 3.2 Variant Selector
+**File:** `app/templates/shared/macros/shop/variant-selector.html`
+
+Product variant selection (size, color, etc.).
+
+```jinja
+{{ variant_selector(
+ variants='product.variants',
+ selected_var='selectedVariant',
+ type='buttons' {# buttons, dropdown, swatches #}
+) }}
+```
+
+**Variant Types:**
+- **Buttons** - Size selection (S, M, L, XL)
+- **Dropdown** - Many options
+- **Swatches** - Color selection with preview
+
+**Features:**
+- Out of stock variants disabled
+- Low stock warning
+- Price change on selection
+- Image change on color selection
+
+---
+
+#### 3.3 Product Info Block
+**File:** `app/templates/shared/macros/shop/product-info.html`
+
+Product details section.
+
+```jinja
+{{ product_info(
+ product='product',
+ show_sku=true,
+ show_stock=true,
+ show_vendor=false
+) }}
+```
+
+**Features:**
+- Product title (H1)
+- Price display (sale price handling)
+- Rating stars + review count
+- Short description
+- SKU display
+- Stock status
+- Vendor name (marketplace)
+
+---
+
+#### 3.4 Product Tabs
+**File:** `app/templates/shared/macros/shop/product-tabs.html`
+
+Tabbed product information.
+
+```jinja
+{{ product_tabs(
+ product='product',
+ tabs=['description', 'specifications', 'reviews', 'shipping']
+) }}
+```
+
+**Tab Options:**
+- Description (HTML content)
+- Specifications (key-value table)
+- Reviews (review list + form)
+- Shipping info
+- Returns policy
+
+---
+
+### Priority 4: Navigation & Discovery
+
+#### 4.1 Category Navigation
+**File:** `app/templates/shared/macros/shop/category-nav.html`
+
+Category browsing sidebar/menu.
+
+```jinja
+{{ category_nav(
+ categories='categories',
+ current_category='currentCategory',
+ show_count=true,
+ collapsible=true
+) }}
+```
+
+**Features:**
+- Nested category tree
+- Product counts
+- Active state highlighting
+- Collapsible sections
+- Mobile drawer version
+
+---
+
+#### 4.2 Breadcrumbs
+**File:** `app/templates/shared/macros/shop/breadcrumbs.html`
+
+Navigation breadcrumb trail.
+
+```jinja
+{{ shop_breadcrumbs(
+ items=[
+ {'label': 'Home', 'url': '/'},
+ {'label': 'Electronics', 'url': '/category/electronics'},
+ {'label': 'Headphones', 'url': none}
+ ]
+) }}
+```
+
+---
+
+#### 4.3 Search Bar
+**File:** `app/templates/shared/macros/shop/search-bar.html`
+
+Product search with autocomplete.
+
+```jinja
+{{ search_bar(
+ placeholder='Search products...',
+ show_suggestions=true,
+ show_categories=true
+) }}
+```
+
+**Features:**
+- Autocomplete suggestions
+- Recent searches
+- Category filter
+- Voice search (optional)
+- Mobile fullscreen mode
+
+---
+
+#### 4.4 Filter Sidebar
+**File:** `app/templates/shared/macros/shop/filter-sidebar.html`
+
+Product filtering panel.
+
+```jinja
+{{ filter_sidebar(
+ filters='availableFilters',
+ active_filters='activeFilters',
+ show_price_range=true,
+ show_rating_filter=true
+) }}
+```
+
+**Filter Types:**
+- Checkbox (brand, size)
+- Color swatches
+- Price range slider
+- Rating stars
+- In-stock toggle
+
+---
+
+### Priority 5: Social Proof & Trust
+
+#### 5.1 Star Rating
+**File:** `app/templates/shared/macros/shop/star-rating.html`
+
+Reusable star rating display.
+
+```jinja
+{{ star_rating(
+ rating=4.5,
+ max=5,
+ size='md',
+ show_count=true,
+ count=127
+) }}
+```
+
+---
+
+#### 5.2 Review Card
+**File:** `app/templates/shared/macros/shop/review-card.html`
+
+Individual product review.
+
+```jinja
+{{ review_card(
+ review='review',
+ show_verified_badge=true,
+ show_helpful_buttons=true
+) }}
+```
+
+**Features:**
+- Reviewer name/avatar
+- Star rating
+- Review date
+- Review text
+- Verified purchase badge
+- Helpful/Not helpful buttons
+- Review images
+
+---
+
+#### 5.3 Trust Badges
+**File:** `app/templates/shared/macros/shop/trust-badges.html`
+
+Trust and security indicators.
+
+```jinja
+{{ trust_badges(
+ badges=['secure_payment', 'free_shipping', 'returns', 'support']
+) }}
+```
+
+**Badge Options:**
+- Secure payment
+- Free shipping over X
+- Easy returns
+- 24/7 support
+- Money back guarantee
+- SSL secured
+
+---
+
+### Priority 6: Promotional Components
+
+#### 6.1 Sale Banner
+**File:** `app/templates/shared/macros/shop/sale-banner.html`
+
+Promotional banner with countdown.
+
+```jinja
+{{ sale_banner(
+ title='Summer Sale',
+ subtitle='Up to 50% off',
+ end_date='2025-08-31',
+ show_countdown=true,
+ cta_text='Shop Now',
+ cta_url='/sale'
+) }}
+```
+
+---
+
+#### 6.2 Product Badge
+**File:** `app/templates/shared/macros/shop/product-badge.html`
+
+Product overlay badges.
+
+```jinja
+{{ product_badge(type='sale', value='-20%') }}
+{{ product_badge(type='new') }}
+{{ product_badge(type='bestseller') }}
+{{ product_badge(type='low_stock', value='Only 3 left') }}
+```
+
+---
+
+#### 6.3 Recently Viewed
+**File:** `app/templates/shared/macros/shop/recently-viewed.html`
+
+Recently viewed products carousel.
+
+```jinja
+{{ recently_viewed(
+ products='recentlyViewed',
+ max_items=6,
+ title='Recently Viewed'
+) }}
+```
+
+---
+
+## Implementation Phases
+
+### Phase 1: Core Components (Week 1-2)
+- [ ] Product Card
+- [ ] Product Grid
+- [ ] Add to Cart Button
+- [ ] Mini Cart
+- [ ] Cart Item Row
+- [ ] Cart Summary
+
+### Phase 2: Product Detail (Week 3-4)
+- [ ] Product Gallery
+- [ ] Variant Selector
+- [ ] Product Info Block
+- [ ] Product Tabs
+- [ ] Star Rating
+
+### Phase 3: Navigation (Week 5-6)
+- [ ] Category Navigation
+- [ ] Breadcrumbs
+- [ ] Search Bar
+- [ ] Filter Sidebar
+
+### Phase 4: Social & Promo (Week 7-8)
+- [ ] Review Card
+- [ ] Trust Badges
+- [ ] Sale Banner
+- [ ] Product Badge
+- [ ] Recently Viewed
+
+---
+
+## CSS Variables for Theming
+
+All shop components will use these CSS variables set by the vendor theme:
+
+```css
+:root {
+ /* Primary brand colors */
+ --color-primary: #7c3aed;
+ --color-primary-hover: #6d28d9;
+ --color-primary-light: #8b5cf6;
+
+ /* Secondary colors */
+ --color-secondary: #f3f4f6;
+ --color-secondary-hover: #e5e7eb;
+
+ /* Text colors */
+ --color-text-primary: #111827;
+ --color-text-secondary: #6b7280;
+
+ /* Status colors */
+ --color-success: #10b981;
+ --color-warning: #f59e0b;
+ --color-error: #ef4444;
+ --color-sale: #dc2626;
+
+ /* UI colors */
+ --color-border: #e5e7eb;
+ --color-background: #ffffff;
+ --color-surface: #f9fafb;
+
+ /* Spacing */
+ --spacing-unit: 0.25rem;
+
+ /* Border radius */
+ --radius-sm: 0.25rem;
+ --radius-md: 0.375rem;
+ --radius-lg: 0.5rem;
+ --radius-full: 9999px;
+}
+```
+
+---
+
+## File Structure
+
+```
+app/templates/shared/macros/shop/
+├── product-card.html
+├── product-grid.html
+├── add-to-cart.html
+├── mini-cart.html
+├── cart-item.html
+├── cart-summary.html
+├── product-gallery.html
+├── variant-selector.html
+├── product-info.html
+├── product-tabs.html
+├── category-nav.html
+├── breadcrumbs.html
+├── search-bar.html
+├── filter-sidebar.html
+├── star-rating.html
+├── review-card.html
+├── trust-badges.html
+├── sale-banner.html
+├── product-badge.html
+└── recently-viewed.html
+```
+
+---
+
+## Next Steps
+
+1. **Review & Approve** - Team reviews this proposal
+2. **Prioritize** - Confirm component priority order
+3. **Design Mockups** - Create visual designs for key components
+4. **Implementation** - Build components in priority order
+5. **Documentation** - Add to component reference page
+6. **Testing** - Test across vendors and themes
+
+---
+
+## Related Documentation
+
+- [Component Standards](../shared/component-standards.md)
+- [Shop Architecture](architecture.md)
+- [Theme System](../../architecture/theme-system/overview.md)
+- [UI Components Quick Reference](../shared/ui-components-quick-reference.md)
diff --git a/mkdocs.yml b/mkdocs.yml
index 0ec413f1..602f46e0 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -86,6 +86,7 @@ nav:
- CDN Fallback Strategy: frontend/cdn-fallback-strategy.md
- Tailwind CSS Build: frontend/tailwind-css.md
- Shared Components:
+ - Component Standards: frontend/shared/component-standards.md
- Jinja Macros Library: frontend/shared/jinja-macros.md
- UI Components: frontend/shared/ui-components.md
- UI Components Quick Reference: frontend/shared/ui-components-quick-reference.md
@@ -102,6 +103,7 @@ nav:
- Shop Frontend:
- Architecture: frontend/shop/architecture.md
- Page Templates: frontend/shop/page-templates.md
+ - E-commerce Components Proposal: frontend/shop/ecommerce-components-proposal.md
- Authentication Pages: frontend/shop/authentication-pages.md
- Navigation Flow: frontend/shop/navigation-flow.md
diff --git a/scripts/validate_architecture.py b/scripts/validate_architecture.py
index a9e5cb44..29759768 100755
--- a/scripts/validate_architecture.py
+++ b/scripts/validate_architecture.py
@@ -397,6 +397,10 @@ class ArchitectureValidator:
if not is_components_page and not is_macro:
self._check_icon_helper_usage(file_path, content, lines)
+ # FE-008: Check for raw number inputs (should use number_stepper)
+ if not is_components_page and not is_macro:
+ self._check_number_stepper_macro_usage(file_path, content, lines)
+
# Only check admin templates for extends
if "/admin/" not in file_path_str and "\\admin\\" not in file_path_str:
return
@@ -630,12 +634,19 @@ class ArchitectureValidator:
# Look for dropdown patterns: @click.outside or @click.away with menu positioning
for i, line in enumerate(lines, 1):
- if ("@click.outside=" in line or "@click.away=" in line) and "false" in line:
- # Check context for dropdown menu indicators
- context_lines = "\n".join(lines[max(0, i-3):min(len(lines), i+10)])
- # Look for dropdown menu styling
- if "absolute" in context_lines and ("right-0" in context_lines or "left-0" in context_lines):
- if "py-2" in context_lines or "py-1" in context_lines:
+ # Match @click.away="something = false" or @click.outside="..."
+ if "@click.away=" in line or "@click.outside=" in line:
+ # Skip if this is a modal (modals also use @click.away but have different structure)
+ context_lines = "\n".join(lines[max(0, i-5):min(len(lines), i+10)])
+
+ # Skip if it looks like a modal (fixed inset-0)
+ if "fixed inset-0" in context_lines:
+ continue
+
+ # Look for dropdown menu styling: absolute positioning with z-index
+ if "absolute" in context_lines and ("z-10" in context_lines or "z-20" in context_lines or "z-50" in context_lines):
+ # Additional indicators: shadow, border, bg-white/dark, rounded
+ if "shadow" in context_lines or "border" in context_lines:
self._add_violation(
rule_id="FE-006",
rule_name="Use dropdowns macro",
@@ -678,6 +689,56 @@ class ArchitectureValidator:
)
return
+ def _check_number_stepper_macro_usage(self, file_path: Path, content: str, lines: list[str]):
+ """FE-008: Check for raw number inputs that should use number_stepper macro
+
+ Detects that should use the number_stepper macro for
+ consistent styling and dark mode support.
+
+ Exceptions:
+ - ID fields (placeholder contains 'id' or 'ID')
+ - Files already importing number_stepper
+ - Lines with noqa: FE-008 comment
+ """
+ # Check if already using the number_stepper macro
+ uses_macro = any("number_stepper" in line for line in lines)
+ if uses_macro:
+ return
+
+ # Check for file-level noqa comment
+ has_noqa = any("noqa: fe-008" in line.lower() for line in lines)
+ if has_noqa:
+ return
+
+ # Look for raw number inputs
+ for i, line in enumerate(lines, 1):
+ if 'type="number"' in line or "type='number'" in line:
+ # Skip if line has noqa comment
+ if "noqa" in line.lower():
+ continue
+
+ # Skip if it looks like an ID field (check surrounding lines for context)
+ context_lines = "\n".join(lines[max(0, i-3):min(len(lines), i+2)]).lower()
+ if "user id" in context_lines or "placeholder" in context_lines and "id" in context_lines:
+ continue
+
+ # Skip if it's in a comment
+ stripped = line.strip()
+ if stripped.startswith("{#") or stripped.startswith("