diff --git a/app/templates/shared/macros/shop/breadcrumbs.html b/app/templates/shared/macros/shop/breadcrumbs.html
new file mode 100644
index 00000000..0f37893f
--- /dev/null
+++ b/app/templates/shared/macros/shop/breadcrumbs.html
@@ -0,0 +1,262 @@
+{#
+ Breadcrumbs Components
+ ======================
+ Navigation breadcrumb trail for shop pages.
+
+ Usage:
+ {% from 'shared/macros/shop/breadcrumbs.html' import shop_breadcrumbs, auto_breadcrumbs %}
+#}
+
+
+{#
+ Shop Breadcrumbs
+ ================
+ Breadcrumb navigation trail.
+
+ Parameters:
+ - items: List of breadcrumb items (static)
+ - items_var: Alpine.js expression for items (dynamic)
+ - separator: Separator icon (default: 'chevron-right')
+ - show_home: Show home link (default: true)
+ - home_url: URL for home (default: '/')
+ - home_label: Label for home (default: 'Home')
+
+ Item structure:
+ {
+ label: 'Category Name',
+ url: '/category/...', // Optional, last item typically has no URL
+ icon: 'folder' // Optional icon
+ }
+
+ Usage (static):
+ {{ shop_breadcrumbs(items=[
+ {'label': 'Electronics', 'url': '/category/electronics'},
+ {'label': 'Headphones', 'url': '/category/headphones'},
+ {'label': 'Wireless'}
+ ]) }}
+
+ Usage (dynamic):
+ {{ shop_breadcrumbs(items_var='breadcrumbs') }}
+#}
+{% macro shop_breadcrumbs(
+ items=none,
+ items_var=none,
+ separator='chevron-right',
+ show_home=true,
+ home_url='/',
+ home_label='Home'
+) %}
+
+{% endmacro %}
+
+
+{#
+ Auto Breadcrumbs
+ ================
+ Automatically generates breadcrumbs from category hierarchy.
+
+ Parameters:
+ - product_var: Alpine.js expression for product (optional)
+ - category_var: Alpine.js expression for current category
+ - show_home: Show home link (default: true)
+
+ Usage:
+ {{ auto_breadcrumbs(category_var='currentCategory') }}
+ {{ auto_breadcrumbs(product_var='product') }}
+#}
+{% macro auto_breadcrumbs(
+ product_var=none,
+ category_var='currentCategory',
+ show_home=true
+) %}
+
+{% endmacro %}
+
+
+{#
+ Compact Breadcrumbs
+ ===================
+ Mobile-friendly breadcrumbs showing only parent and current.
+
+ Parameters:
+ - parent: Parent item (static)
+ - parent_var: Alpine.js expression for parent
+ - current: Current page label
+ - current_var: Alpine.js expression for current
+
+ Usage:
+ {{ compact_breadcrumbs(parent={'label': 'Electronics', 'url': '/electronics'}, current='Headphones') }}
+#}
+{% macro compact_breadcrumbs(
+ parent=none,
+ parent_var=none,
+ current=none,
+ current_var=none
+) %}
+
+{% endmacro %}
diff --git a/app/templates/shared/macros/shop/category-nav.html b/app/templates/shared/macros/shop/category-nav.html
new file mode 100644
index 00000000..4ece2441
--- /dev/null
+++ b/app/templates/shared/macros/shop/category-nav.html
@@ -0,0 +1,391 @@
+{#
+ Category Navigation Components
+ ==============================
+ Category browsing sidebar and menu for shop navigation.
+
+ Usage:
+ {% from 'shared/macros/shop/category-nav.html' import category_nav, category_tree, category_menu %}
+#}
+
+
+{#
+ Category Navigation
+ ===================
+ Sidebar category navigation with nested categories.
+
+ Parameters:
+ - categories_var: Alpine.js expression for categories array (default: 'categories')
+ - current_var: Alpine.js expression for current category (default: 'currentCategory')
+ - show_count: Show product counts (default: true)
+ - collapsible: Make nested categories collapsible (default: true)
+ - max_depth: Maximum nesting depth to show (default: 3)
+
+ Expected category object:
+ {
+ id: 1,
+ name: 'Electronics',
+ slug: 'electronics',
+ url: '/category/electronics',
+ product_count: 150,
+ children: [...]
+ }
+
+ Usage:
+ {{ category_nav(categories_var='categories', show_count=true) }}
+#}
+{% macro category_nav(
+ categories_var='categories',
+ current_var='currentCategory',
+ show_count=true,
+ collapsible=true,
+ max_depth=3
+) %}
+
+{% endmacro %}
+
+
+{#
+ Internal: Category Item (recursive)
+#}
+{% macro _category_item(current_var, show_count, collapsible, depth, max_depth) %}
+
+
+
+ {# Nested Categories #}
+ {% if depth < max_depth - 1 %}
+
+
+
+ {{ _category_item(current_var, show_count, collapsible, depth + 1, max_depth) }}
+
+
+
+ {% endif %}
+
+{% endmacro %}
+
+
+{#
+ Category Tree (Flat List)
+ =========================
+ Simple flat list of categories without nesting.
+
+ Parameters:
+ - categories_var: Alpine.js expression for categories
+ - current_var: Alpine.js expression for current category
+ - show_count: Show product counts (default: true)
+ - layout: 'vertical' | 'horizontal' (default: 'vertical')
+
+ Usage:
+ {{ category_tree(categories_var='topCategories', layout='horizontal') }}
+#}
+{% macro category_tree(
+ categories_var='categories',
+ current_var='currentCategory',
+ show_count=true,
+ layout='vertical'
+) %}
+
+{% endmacro %}
+
+
+{#
+ Category Menu (Dropdown/Mega Menu)
+ ==================================
+ Horizontal category menu for header navigation.
+
+ Parameters:
+ - categories_var: Alpine.js expression for categories
+ - show_all_link: Show "All Categories" link (default: true)
+ - all_link_url: URL for all categories (default: '/categories')
+
+ Usage:
+ {{ category_menu(categories_var='mainCategories') }}
+#}
+{% macro category_menu(
+ categories_var='categories',
+ show_all_link=true,
+ all_link_url='/categories'
+) %}
+
+{% endmacro %}
+
+
+{#
+ Mobile Category Drawer
+ ======================
+ Full-screen category navigation for mobile devices.
+
+ Parameters:
+ - categories_var: Alpine.js expression for categories
+ - show_var: Alpine.js variable for drawer visibility (default: 'showCategoryDrawer')
+
+ Usage:
+ {{ category_drawer(categories_var='categories', show_var='showCategoryDrawer') }}
+#}
+{% macro category_drawer(
+ categories_var='categories',
+ show_var='showCategoryDrawer'
+) %}
+
+ {# Backdrop #}
+
+
+ {# Drawer Panel #}
+
+ {# Header #}
+
+
+
+
+
+
+ {# Category List #}
+
+
+
+{% endmacro %}
diff --git a/app/templates/shared/macros/shop/filter-sidebar.html b/app/templates/shared/macros/shop/filter-sidebar.html
new file mode 100644
index 00000000..bbf0eaee
--- /dev/null
+++ b/app/templates/shared/macros/shop/filter-sidebar.html
@@ -0,0 +1,786 @@
+{#
+ Filter Sidebar Components
+ =========================
+ Product filtering panel for category and search pages.
+
+ Usage:
+ {% from 'shared/macros/shop/filter-sidebar.html' import filter_sidebar, filter_group, price_filter, rating_filter %}
+#}
+
+
+{#
+ Filter Sidebar
+ ==============
+ Complete filter sidebar with multiple filter types.
+
+ Parameters:
+ - filters_var: Alpine.js expression for filter configuration (default: 'filters')
+ - active_filters_var: Alpine.js expression for active filters (default: 'activeFilters')
+ - on_change: JavaScript callback when filters change (default: 'filterProducts()')
+ - show_clear: Show clear all button (default: true)
+ - collapsible: Make filter groups collapsible (default: true)
+
+ Expected filters structure:
+ {
+ categories: [{ id, name, count }],
+ brands: [{ id, name, count }],
+ priceRange: { min: 0, max: 1000 },
+ attributes: {
+ color: [{ value, label, count, hex }],
+ size: [{ value, label, count }]
+ },
+ ratings: [{ value: 5, count: 10 }, ...]
+ }
+
+ Usage:
+ {{ filter_sidebar(filters_var='filters', on_change='applyFilters()') }}
+#}
+{% macro filter_sidebar(
+ filters_var='filters',
+ active_filters_var='activeFilters',
+ on_change='filterProducts()',
+ show_clear=true,
+ collapsible=true
+) %}
+
+{% endmacro %}
+
+
+{#
+ Internal: Filter Group for Categories/Brands
+#}
+{% macro _filter_group(title, type, collapsible) %}
+
+ {% if collapsible %}
+
+ {% else %}
+
{{ title }}
+ {% endif %}
+
+
+
+
+
+
+
+{% endmacro %}
+
+
+{#
+ Internal: Price Range Group
+#}
+{% macro _price_range_group(filters_var, active_filters_var, on_change, collapsible) %}
+
+ {% if collapsible %}
+
+ {% else %}
+
Price Range
+ {% endif %}
+
+
+ {# Dual Range Slider #}
+
+
+ {# Price Inputs #}
+
+
+
+{% endmacro %}
+
+
+{#
+ Internal: Rating Group
+#}
+{% macro _rating_group(filters_var, active_filters_var, on_change, collapsible) %}
+
+ {% if collapsible %}
+
+ {% else %}
+
Rating
+ {% endif %}
+
+
+
+
+
+
+
+
+{% endmacro %}
+
+
+{#
+ Internal: Dynamic Attribute Group (color, size, etc.)
+#}
+{% macro _attribute_group(filters_var, active_filters_var, on_change, collapsible) %}
+
+ {% if collapsible %}
+
+ {% else %}
+
+ {% endif %}
+
+
+ {# Color Swatches #}
+
+
+
+
+
+
+
+
+ {# Size/Other Buttons #}
+
+
+
+
+
+
+
+
+ {# Checkbox List for other attributes #}
+
+
+
+
+
+
+
+
+
+{% endmacro %}
+
+
+{#
+ Price Filter (Standalone)
+ =========================
+ Standalone price range filter component.
+
+ Parameters:
+ - min: Minimum price (default: 0)
+ - max: Maximum price (default: 1000)
+ - value_min_var: Alpine.js var for min value (default: 'priceMin')
+ - value_max_var: Alpine.js var for max value (default: 'priceMax')
+ - on_change: Callback on change (default: 'updateFilters()')
+ - currency: Currency symbol (default: '$')
+
+ Usage:
+ {{ price_filter(min=0, max=500, on_change='filterByPrice()') }}
+#}
+{% macro price_filter(
+ min=0,
+ max=1000,
+ value_min_var='priceMin',
+ value_max_var='priceMax',
+ on_change='updateFilters()',
+ currency='$'
+) %}
+
+{% endmacro %}
+
+
+{#
+ Rating Filter (Standalone)
+ ==========================
+ Standalone star rating filter.
+
+ Parameters:
+ - value_var: Alpine.js var for selected rating (default: 'minRating')
+ - on_change: Callback on change (default: 'updateFilters()')
+ - show_count: Show product counts (default: false)
+ - counts: List of counts per rating level
+
+ Usage:
+ {{ rating_filter(value_var='minRating', on_change='filterByRating()') }}
+#}
+{% macro rating_filter(
+ value_var='minRating',
+ on_change='updateFilters()',
+ show_count=false,
+ counts=none
+) %}
+
+ {% for i in range(5, 0, -1) %}
+
+ {% endfor %}
+
+{% endmacro %}
+
+
+{#
+ Mobile Filter Drawer
+ ====================
+ Full-screen filter panel for mobile devices.
+
+ Parameters:
+ - show_var: Alpine.js variable for visibility (default: 'showFilters')
+ - filters_var: Alpine.js expression for filters (default: 'filters')
+ - active_filters_var: Alpine.js expression for active filters (default: 'activeFilters')
+ - on_apply: Callback on apply (default: 'applyFilters()')
+ - result_count_var: Alpine.js var showing result count (default: 'productCount')
+
+ Usage:
+ {{ mobile_filter_drawer(show_var='showMobileFilters') }}
+#}
+{% macro mobile_filter_drawer(
+ show_var='showFilters',
+ filters_var='filters',
+ active_filters_var='activeFilters',
+ on_apply='applyFilters()',
+ result_count_var='productCount'
+) %}
+
+ {# Backdrop #}
+
+
+ {# Drawer Panel #}
+
+ {# Header #}
+
+
Filters
+
+
+
+ {# Filter Content #}
+
+ {{ filter_sidebar(filters_var=filters_var, active_filters_var=active_filters_var, on_change=on_apply, show_clear=false, collapsible=true) }}
+
+
+ {# Footer with Apply Button #}
+
+
+
+
+
+{% endmacro %}
+
+
+{#
+ Filter Trigger Button
+ ====================
+ Button to open mobile filter drawer.
+
+ Parameters:
+ - show_var: Alpine.js variable to toggle (default: 'showFilters')
+ - active_count_var: Alpine.js var for active filter count (default: none)
+
+ Usage:
+ {{ filter_trigger(show_var='showMobileFilters') }}
+#}
+{% macro filter_trigger(
+ show_var='showFilters',
+ active_count_var=none
+) %}
+
+{% endmacro %}
+
+
+{#
+ Sort Dropdown
+ =============
+ Product sorting dropdown.
+
+ Parameters:
+ - value_var: Alpine.js var for sort value (default: 'sortBy')
+ - options: Sort options list (default: common options)
+ - on_change: Callback on change (default: 'sortProducts()')
+
+ Usage:
+ {{ sort_dropdown(value_var='currentSort') }}
+#}
+{% macro sort_dropdown(
+ value_var='sortBy',
+ options=none,
+ on_change='sortProducts()'
+) %}
+{% set default_options = [
+ {'value': 'relevance', 'label': 'Relevance'},
+ {'value': 'price_asc', 'label': 'Price: Low to High'},
+ {'value': 'price_desc', 'label': 'Price: High to Low'},
+ {'value': 'newest', 'label': 'Newest First'},
+ {'value': 'rating', 'label': 'Highest Rated'},
+ {'value': 'popular', 'label': 'Most Popular'}
+] %}
+{% set sort_options = options if options else default_options %}
+
+
+
+
+ {% for opt in sort_options %}
+
+ {% endfor %}
+
+
+{% endmacro %}
diff --git a/app/templates/shared/macros/shop/search-bar.html b/app/templates/shared/macros/shop/search-bar.html
new file mode 100644
index 00000000..a8a3fdb3
--- /dev/null
+++ b/app/templates/shared/macros/shop/search-bar.html
@@ -0,0 +1,627 @@
+{#
+ Search Bar Components
+ =====================
+ Product search with autocomplete and suggestions for shop pages.
+
+ Usage:
+ {% from 'shared/macros/shop/search-bar.html' import search_bar, search_autocomplete, mobile_search %}
+#}
+
+
+{#
+ Search Bar
+ ==========
+ Basic search input with icon and optional button.
+
+ Parameters:
+ - placeholder: Placeholder text (default: 'Search products...')
+ - action: Form action URL (default: '/search')
+ - method: Form method (default: 'get')
+ - name: Input name (default: 'q')
+ - value: Initial value (default: '')
+ - show_button: Show search button (default: false)
+ - button_label: Button text (default: 'Search')
+ - size: 'sm' | 'md' | 'lg' (default: 'md')
+ - variant: 'default' | 'filled' | 'minimal' (default: 'default')
+
+ Usage:
+ {{ search_bar(placeholder='Search for products...') }}
+ {{ search_bar(show_button=true, size='lg') }}
+#}
+{% macro search_bar(
+ placeholder='Search products...',
+ action='/search',
+ method='get',
+ name='q',
+ value='',
+ show_button=false,
+ button_label='Search',
+ size='md',
+ variant='default'
+) %}
+{% set sizes = {
+ 'sm': {'input': 'py-1.5 pl-8 pr-3 text-sm', 'icon': 'w-4 h-4 left-2.5', 'button': 'px-3 py-1.5 text-sm'},
+ 'md': {'input': 'py-2.5 pl-10 pr-4 text-sm', 'icon': 'w-5 h-5 left-3', 'button': 'px-4 py-2.5 text-sm'},
+ 'lg': {'input': 'py-3 pl-12 pr-4 text-base', 'icon': 'w-6 h-6 left-3.5', 'button': 'px-5 py-3 text-base'}
+} %}
+{% set variants = {
+ 'default': 'bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 focus:border-purple-500 dark:focus:border-purple-400 focus:ring-2 focus:ring-purple-500/20',
+ 'filled': 'bg-gray-100 dark:bg-gray-700 border border-transparent focus:bg-white dark:focus:bg-gray-800 focus:border-purple-500 dark:focus:border-purple-400 focus:ring-2 focus:ring-purple-500/20',
+ 'minimal': 'bg-transparent border-b border-gray-300 dark:border-gray-600 rounded-none focus:border-purple-500 dark:focus:border-purple-400'
+} %}
+
+{% endmacro %}
+
+
+{#
+ Search Autocomplete
+ ===================
+ Search input with dropdown suggestions and autocomplete.
+
+ Parameters:
+ - placeholder: Placeholder text (default: 'Search products...')
+ - action: Form action URL (default: '/search')
+ - search_endpoint: API endpoint for suggestions (default: '/api/search/suggest')
+ - min_chars: Minimum characters to trigger search (default: 2)
+ - debounce: Debounce delay in ms (default: 300)
+ - show_recent: Show recent searches (default: true)
+ - show_popular: Show popular searches (default: true)
+ - max_suggestions: Maximum suggestions to show (default: 5)
+ - size: 'sm' | 'md' | 'lg' (default: 'md')
+
+ Usage:
+ {{ search_autocomplete(search_endpoint='/api/products/search') }}
+#}
+{% macro search_autocomplete(
+ placeholder='Search products...',
+ action='/search',
+ search_endpoint='/api/search/suggest',
+ min_chars=2,
+ debounce=300,
+ show_recent=true,
+ show_popular=true,
+ max_suggestions=5,
+ size='md'
+) %}
+{% set sizes = {
+ 'sm': {'input': 'py-1.5 pl-8 pr-8 text-sm', 'icon': 'w-4 h-4', 'dropdown': 'mt-1'},
+ 'md': {'input': 'py-2.5 pl-10 pr-10 text-sm', 'icon': 'w-5 h-5', 'dropdown': 'mt-2'},
+ 'lg': {'input': 'py-3 pl-12 pr-12 text-base', 'icon': 'w-6 h-6', 'dropdown': 'mt-2'}
+} %}
+
+
+
+ {# Dropdown #}
+
+ {# Search Suggestions #}
+
+
+
+ -
+
+
+
+
+
+
+ {# Recent & Popular Searches #}
+
+
+ {% if show_recent %}
+
+
+
+ Recent Searches
+
+
+
+
+
+
+
+ {% endif %}
+
+ {% if show_popular %}
+
+
+ Popular Searches
+
+
+
+
+
+ {% endif %}
+
+
+
+
+{% endmacro %}
+
+
+{#
+ Mobile Search
+ =============
+ Full-screen search overlay for mobile devices.
+
+ Parameters:
+ - show_var: Alpine.js variable for visibility (default: 'showMobileSearch')
+ - placeholder: Placeholder text (default: 'Search products...')
+ - action: Form action URL (default: '/search')
+ - search_endpoint: API endpoint for suggestions (default: '/api/search/suggest')
+
+ Usage:
+ {{ mobile_search(show_var='showSearch') }}
+#}
+{% macro mobile_search(
+ show_var='showMobileSearch',
+ placeholder='Search products...',
+ action='/search',
+ search_endpoint='/api/search/suggest'
+) %}
+
+ {# Header #}
+
+
+ {# Content #}
+
+ {# Loading State #}
+
+
+
+
+ {# Search Results #}
+
+
+
+ -
+
+
+
+
+
+
+ {# No Results #}
+
+
+
+
No results found for ""
+
+
+
+ {# Recent Searches #}
+
+
+
+ Recent Searches
+
+
+
+
+
+
+
+
+
+{% endmacro %}
+
+
+{#
+ Search Trigger Button
+ ====================
+ Button to open mobile search or focus desktop search.
+
+ Parameters:
+ - show_var: Alpine.js variable to toggle (default: 'showMobileSearch')
+ - sr_label: Screen reader label (default: 'Open search')
+
+ Usage:
+ {{ search_trigger(show_var='showSearch') }}
+#}
+{% macro search_trigger(
+ show_var='showMobileSearch',
+ sr_label='Open search'
+) %}
+
+{% endmacro %}
+
+
+{#
+ Instant Search Results
+ =====================
+ Inline search results component (for header search).
+
+ Parameters:
+ - results_var: Alpine.js expression for search results (default: 'searchResults')
+ - loading_var: Alpine.js expression for loading state (default: 'isSearching')
+ - query_var: Alpine.js expression for search query (default: 'searchQuery')
+ - show_var: Alpine.js expression for visibility (default: 'showResults')
+
+ Usage:
+ {{ instant_search_results(results_var='searchResults') }}
+#}
+{% macro instant_search_results(
+ results_var='searchResults',
+ loading_var='isSearching',
+ query_var='searchQuery',
+ show_var='showResults'
+) %}
+
+ {# Loading #}
+
+
+ Searching...
+
+
+ {# Results #}
+
+
+ {# Products #}
+
+
+ Products
+
+
+
+
+
+
+
+
+
+ {# Categories #}
+
+
+
+
+ {# View All Results #}
+
+
+
+
+ {# No Results #}
+
+
+
+
+{% endmacro %}