- Add Development URL Quick Reference section to url-routing overview with all login URLs, entry points, and full examples - Replace /shop/ path segments with /storefront/ across 50 docs files - Update file references: shop_pages.py → storefront_pages.py, templates/shop/ → templates/storefront/, api/v1/shop/ → api/v1/storefront/ - Preserve domain references (orion.shop) and /store/ staff dashboard paths - Archive docs left unchanged (historical) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
48 KiB
╔══════════════════════════════════════════════════════════════════╗ ║ STOREFRONT FRONTEND ARCHITECTURE OVERVIEW ║ ║ Alpine.js + Jinja2 + Tailwind CSS + Multi-Theme ║ ╚══════════════════════════════════════════════════════════════════╝
📦 WHAT IS THIS? ═════════════════════════════════════════════════════════════════
Customer-facing storefront frontend provides visitors with a branded e-commerce experience unique to each store. Built with: ✅ Jinja2 Templates (server-side rendering) ✅ Alpine.js (client-side reactivity) ✅ Tailwind CSS (utility-first styling) ✅ Multi-Theme System (store branding) ✅ FastAPI (backend routes)
🎯 KEY PRINCIPLES ═════════════════════════════════════════════════════════════════
-
Theme-First Design • Each store has unique colors, fonts, logos • CSS variables for dynamic theming • Custom CSS support per store • Dark mode with store colors
-
Progressive Enhancement • Works without JavaScript (basic HTML) • JavaScript adds cart, search, filters • Graceful degradation for older browsers
-
API-First Data Loading • All products from REST APIs • Client-side cart management • Real-time stock updates • Search and filtering
-
Responsive & Mobile-First • Mobile-first Tailwind approach • Touch-friendly interactions • Optimized images • Fast page loads
📁 FILE STRUCTURE ═════════════════════════════════════════════════════════════════
app/ ├── templates/storefront/ │ ├── base.html ← ✅ Base template (layout + theme) │ ├── home.html ← ✅ Homepage / featured products │ ├── products.html ← ✅ Product catalog with filters │ ├── product.html ← Product detail page │ ├── cart.html ← Shopping cart │ ├── checkout.html ← Checkout flow │ ├── search.html ← Search results │ ├── account/ ← Customer account pages │ │ ├── login.html ← ✅ Customer login (IMPLEMENTED) │ │ ├── register.html ← ✅ Customer registration (IMPLEMENTED) │ │ ├── forgot-password.html ← ✅ Password reset (IMPLEMENTED) │ │ ├── dashboard.html │ │ ├── orders.html │ │ ├── profile.html │ │ └── addresses.html │ └── errors/ ← Error pages │ ├── 400.html │ ├── 404.html │ └── 500.html │ ├── static/storefront/ │ ├── css/ │ │ └── storefront.css ← ✅ Storefront-specific styles (IMPLEMENTED) │ ├── js/ │ │ └── storefront-layout.js ← ✅ Base storefront functionality (IMPLEMENTED) │ └── img/ │ └── (placeholder images) │ ├── static/shared/ ← ✅ Shared across all areas (IMPLEMENTED) │ ├── js/ │ │ ├── log-config.js ← ✅ Logging setup │ │ ├── icons.js ← ✅ Icon registry │ │ ├── utils.js ← ✅ Utility functions │ │ └── api-client.js ← ✅ API wrapper │ └── css/ │ └── (shared styles if needed) │ └── routes/ └── storefront_pages.py ← ✅ Route handlers (IMPLEMENTED)
🏗️ ARCHITECTURE LAYERS ═════════════════════════════════════════════════════════════════
Layer 1: Routes (FastAPI) ↓ Layer 2: Middleware (Store + Theme Detection) ↓ Layer 3: Templates (Jinja2) ↓ Layer 4: JavaScript (Alpine.js) ↓ Layer 5: API (REST endpoints) ↓ Layer 6: Database
Layer 1: ROUTES (FastAPI) ────────────────────────────────────────────────────────────────── Purpose: Store Detection + Template Rendering Location: app/routes/storefront_pages.py
⚠️ ROUTE REGISTRATION (main.py): The storefront router is mounted at TWO prefixes to support both access methods:
main.py
app.include_router(storefront_pages.router, prefix="/storefront", ...) # Domain/subdomain app.include_router(storefront_pages.router, prefix="/storefront/{store_code}", ...) # Path-based
This means routes defined WITHOUT /storefront prefix in storefront_pages.py: @router.get("/products") → /storefront/products OR /storefront/{code}/products
❌ COMMON MISTAKE: Don't add /storefront prefix in route definitions! @router.get("/storefront/products") ❌ WRONG - creates /storefront/storefront/products @router.get("/products") ✅ CORRECT - creates /storefront/products
Example Route Handler: @router.get("/", response_class=HTMLResponse, include_in_schema=False) @router.get("/products", response_class=HTMLResponse, include_in_schema=False) async def storefront_products_page(request: Request): """ Render storefront homepage / product catalog. Store and theme are auto-injected by middleware. """ return templates.TemplateResponse( "storefront/products.html", get_storefront_context(request) # Helper function )
Helper Function: def get_storefront_context(request: Request, **extra_context) -> dict: """Build template context with store/theme from middleware""" store = getattr(request.state, 'store', None) theme = getattr(request.state, 'theme', None) clean_path = getattr(request.state, 'clean_path', request.url.path) store_context = getattr(request.state, 'store_context', None)
# Get detection method (domain, subdomain, or path)
access_method = store_context.get('detection_method', 'unknown') if store_context else 'unknown'
# Calculate base URL for links
# - Domain/subdomain: base_url = "/"
# - Path-based: base_url = "/store/{store_code}/"
base_url = "/"
if access_method == "path" and store:
full_prefix = store_context.get('full_prefix', '/store/')
base_url = f"{full_prefix}{store.subdomain}/"
return {
"request": request,
"store": store,
"theme": theme,
"clean_path": clean_path,
"access_method": access_method,
"base_url": base_url, # ⭐ Used for all links in templates
**extra_context
}
Responsibilities: ✅ Access store from middleware (request.state.store) ✅ Access theme from middleware (request.state.theme) ✅ Calculate base_url for routing-aware links ✅ Render template with context ❌ NO database queries (data loaded client-side via API) ❌ NO business logic (handled by API endpoints)
⭐ MULTI-ACCESS ROUTING (Domain, Subdomain, Path-Based) ────────────────────────────────────────────────────────────────── The storefront frontend supports THREE access methods:
-
Custom Domain (Production) URL: https://customdomain.com/storefront/products
- Store has their own domain
- base_url = "/"
- Links: /storefront/products, /storefront/about, /storefront/contact
-
Subdomain (Production) URL: https://orion.letzshop.com/storefront/products
- Store uses platform subdomain
- base_url = "/"
- Links: /storefront/products, /storefront/about, /storefront/contact
-
Path-Based (Development/Testing) URL: http://localhost:8000/storefront/orion/products
- Store accessed via path prefix
- base_url = "/storefront/orion/"
- Links: /storefront/orion/products, /storefront/orion/about
⚠️ CRITICAL: All template links MUST use {{ base_url }}storefront/ prefix
Example: ❌ BAD: Products ❌ BAD: Products ✅ GOOD: Products
Note: The router is mounted at /storefront prefix in main.py, so all links need storefront/ after base_url
How It Works:
- StoreContextMiddleware detects access method
- Sets request.state.store_context with detection_method
- get_storefront_context() calculates base_url from detection_method
- Templates use {{ base_url }} for all internal links
- Links work correctly regardless of access method
Layer 2: MIDDLEWARE ────────────────────────────────────────────────────────────────── Purpose: Store & Theme Identification
Two middleware components work together:
-
Store Context Middleware (middleware/store_context.py) • Detects store from domain/subdomain/path • Sets request.state.store • Sets request.state.store_context (includes detection_method) • Sets request.state.clean_path (path without store prefix) • Returns 404 if store not found
-
Theme Context Middleware (middleware/theme_context.py) • Loads theme for detected store • Sets request.state.theme • Falls back to default theme
Order matters: store_context_middleware → theme_context_middleware
Detection Methods:
- custom_domain: Store has custom domain
- subdomain: Store uses platform subdomain
- path: Store accessed via /store/{code}/ or /stores/{code}/
Layer 3: TEMPLATES (Jinja2) ────────────────────────────────────────────────────────────────── Purpose: HTML Structure + Store Branding Location: app/templates/storefront/
Template Hierarchy: base.html (layout + theme injection) ↓ home.html (product grid) ↓ partials/product-card.html (components)
Example: {% extends "storefront/base.html" %} {% block title %}{{ store.name }}{% endblock %} {% block alpine_data %}shopHome(){% endblock %} {% block content %}
Key Features: ✅ Theme CSS variables injection ✅ Store logo (light/dark mode) ✅ Custom CSS from theme ✅ Social links from theme ✅ Dynamic favicon
Layer 4: JAVASCRIPT (Alpine.js) ────────────────────────────────────────────────────────────────── Purpose: Client-Side Interactivity + Cart + Search Location: app/static/storefront/js/
⚠️ CRITICAL: JavaScript Loading Order ────────────────────────────────────────────────────────────────── Scripts MUST load in this exact order (see base.html):
- log-config.js ← Logging system (loads first)
- icons.js ← Icon registry
- storefront-layout.js ← Alpine component (before Alpine!)
- utils.js ← Utility functions
- api-client.js ← API wrapper
- Alpine.js (deferred) ← Loads last
- Page-specific JS ← Optional page scripts
Why This Order Matters: • storefront-layout.js defines storefrontLayoutData() BEFORE Alpine initializes • Alpine.js defers to ensure DOM is ready • Shared utilities available to all scripts • Icons and logging available immediately
Example from base.html:
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script> <script src="{{ url_for('static', path='shared/js/icons.js') }}"></script> <script src="{{ url_for('static', path='storefront/js/storefront-layout.js') }}"></script> <script src="{{ url_for('static', path='shared/js/utils.js') }}"></script> <script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>Alpine.js Component Architecture: ──────────────────────────────────────────────────────────────────
⭐ BASE COMPONENT (storefront-layout.js):
Provides shared functionality for all storefront pages:
function storefrontLayoutData() { return { // Theme state dark: localStorage.getItem('storefront-theme') === 'dark',
// UI state
mobileMenuOpen: false,
searchOpen: false,
cartCount: 0,
// Cart state
cart: [],
init() {
shopLog.info('Storefront layout initializing...');
this.loadCart();
window.addEventListener('cart-updated', () => {
this.loadCart();
});
shopLog.info('Storefront layout initialized');
},
addToCart(product, quantity = 1) {
const existingIndex = this.cart.findIndex(
item => item.id === product.id
);
if (existingIndex !== -1) {
this.cart[existingIndex].quantity += quantity;
} else {
this.cart.push({
id: product.id,
name: product.name,
price: product.price,
image: product.image,
quantity: quantity
});
}
this.saveCart();
this.showToast(`${product.name} added to cart`, 'success');
},
toggleTheme() {
this.dark = !this.dark;
localStorage.setItem('storefront-theme',
this.dark ? 'dark' : 'light');
shopLog.debug('Theme toggled:', this.dark ? 'dark' : 'light');
},
showToast(message, type = 'info') {
// Toast notification implementation
}
};
}
// Make globally available window.storefrontLayoutData = storefrontLayoutData;
⭐ PAGE-SPECIFIC COMPONENTS:
Each page extends storefrontLayoutData() for page-specific functionality:
// Example: products.html document.addEventListener('alpine:init', () => { Alpine.data('shopProducts', () => ({ ...storefrontLayoutData(), // Extend base component
// Page-specific state
products: [],
loading: true,
filters: { search: '', category: '' },
// Override init to add page-specific initialization
async init() {
shopLog.info('Products page initializing...');
this.loadCart(); // From storefrontLayoutData
await this.loadProducts(); // Page-specific
},
// Page-specific methods
async loadProducts() {
const response = await fetch('/api/v1/storefront/products');
const data = await response.json();
this.products = data.products;
this.loading = false;
}
}));
});
Template Usage: ──────────────────────────────────────────────────────────────────
{# In base.html - uses block to allow override #}
<html x-data="{% block alpine_data %}storefrontLayoutData(){% endblock %}" x-bind:class="{ 'dark': dark }">{# In products.html - overrides to use page-specific component #} {% block alpine_data %}shopProducts(){% endblock %}
{# In home.html - uses default base component #} {# No block override needed, inherits storefrontLayoutData() #}
⭐ COMPONENT HIERARCHY:
storefrontLayoutData() ← Base component (shared state & methods) ↓ shopProducts() ← Products page (extends base + products state) shopCart() ← Cart page (extends base + cart state) shopCheckout() ← Checkout page (extends base + order state) shopAccount() ← Account page (extends base + user state)
Benefits: ✅ Shared functionality (theme, cart, toasts) available on all pages ✅ Each page has its own state and methods ✅ DRY - base functionality defined once ✅ Flexible - pages can override init() or add new methods
Tradeoffs: ⚠️ One component per page (not multiple components) ⚠️ All page state is at root level ⚠️ Can't easily split page into independent sub-components
Best Practices:
- Always extend storefrontLayoutData() in page components
- Override init() if you need page-specific initialization
- Call parent methods when needed (this.loadCart(), this.showToast())
- Keep page-specific state in the page component
- Keep shared state in storefrontLayoutData()
Responsibilities: ✅ Load products from API ✅ Manage cart in localStorage ✅ Handle search and filters ✅ Update DOM reactively ✅ Theme toggling (light/dark) ✅ Mobile menu management ✅ Toast notifications
Layer 5: API (REST) ────────────────────────────────────────────────────────────────── Purpose: Product Data + Cart + Orders Location: app/api/v1/storefront/*.py
⭐ NEW API STRUCTURE (as of 2025-11-22): All storefront endpoints use middleware-based store context. NO store_id or store_code in URLs!
Example Endpoints: GET /api/v1/storefront/products ← Product catalog GET /api/v1/storefront/products/{id} ← Product details GET /api/v1/storefront/products?search=... ← Search products GET /api/v1/storefront/cart/{session_id} ← Get cart POST /api/v1/storefront/cart/{session_id}/items ← Add to cart PUT /api/v1/storefront/cart/{session_id}/items/{product_id} ← Update item DELETE /api/v1/storefront/cart/{session_id}/items/{product_id} ← Remove item POST /api/v1/storefront/orders ← Place order (auth required) GET /api/v1/storefront/orders ← Order history (auth required) POST /api/v1/storefront/auth/login ← Customer login POST /api/v1/storefront/auth/register ← Customer registration GET /api/v1/storefront/content-pages/navigation ← CMS navigation GET /api/v1/storefront/content-pages/{slug} ← CMS page content
How Store Context Works:
- Browser makes API call from storefront page (e.g., /storefront/orion/products)
- Browser automatically sends Referer header: http://localhost:8000/storefront/orion/products
- StoreContextMiddleware extracts store from Referer header
- Middleware sets request.state.store = <Store: orion>
- API endpoint accesses store: store = request.state.store
- No store_id needed in URL!
🔄 DATA FLOW ═════════════════════════════════════════════════════════════════
Page Load Flow: ──────────────────────────────────────────────────────────────────
- Customer → visits acme-shop.com (or /storefront/acme/products)
- Store Middleware → Identifies "ACME" store from domain/path
- Theme Middleware → Loads ACME's theme config
- FastAPI → Renders storefront/products.html
- Browser → Receives HTML with theme CSS variables
- Alpine.js → init() executes
- JavaScript → GET /api/v1/storefront/products (with Referer header)
- Middleware → Extracts store from Referer, injects into request.state
- API → Returns product list JSON for ACME store
- Alpine.js → Updates products array
- Browser → DOM updates with product cards
Add to Cart Flow: ──────────────────────────────────────────────────────────────────
- Customer → Clicks "Add to Cart"
- Alpine.js → addToCart(product, quantity)
- Alpine.js → Updates cart array
- Alpine.js → Saves to localStorage
- Alpine.js → Updates cartCount badge
- Alpine.js → Shows toast notification
- Browser → Cart icon updates automatically
Checkout Flow: ──────────────────────────────────────────────────────────────────
- Customer → Goes to /cart
- Page → Loads cart from localStorage
- Customer → Fills checkout form
- Alpine.js → POST /api/v1/storefront/orders (with Referer header)
- Middleware → Extracts store from Referer
- API → Creates order + payment intent for store
- Alpine.js → Redirects to payment
- Payment → Completes
- Redirect → /order/{order_id}/confirmation
🎨 MULTI-THEME SYSTEM ═════════════════════════════════════════════════════════════════
How Themes Work:
-
Database Storage • Each store has a theme record • Stores colors, fonts, logos, layout prefs • Custom CSS per store
-
CSS Variables Injection • base.html injects variables in <style> tag • Variables available throughout page • Example: :root { --color-primary: #6366f1; --color-secondary: #8b5cf6; --color-accent: #ec4899; --color-background: #ffffff; --color-text: #1f2937; --font-heading: Inter, sans-serif; --font-body: Inter, sans-serif; }
-
Usage in Templates Buy Now
-
Dark Mode • Store colors adjust for dark mode • Saved in localStorage • Applied via :class="{ 'dark': dark }" • Uses dark: variants in Tailwind
Theme Configuration Example: ────────────────────────────────────────────────────────────────── { "theme_name": "modern", "colors": { "primary": "#6366f1", "secondary": "#8b5cf6", "accent": "#ec4899", "background": "#ffffff", "text": "#1f2937" }, "fonts": { "heading": "Inter, sans-serif", "body": "Inter, sans-serif" }, "branding": { "logo": "/media/stores/acme/logo.png", "logo_dark": "/media/stores/acme/logo-dark.png", "favicon": "/media/stores/acme/favicon.ico" }, "layout": { "header": "fixed", "style": "grid" }, "social_links": { "facebook": "https://facebook.com/acme", "instagram": "https://instagram.com/acme" }, "custom_css": ".product-card { border-radius: 12px; }" }
🛒 CART SYSTEM ═════════════════════════════════════════════════════════════════
Client-Side Cart Management:
Storage: localStorage Format: JSON array of cart items
Cart Item Structure: { "id": "product-123", "name": "Product Name", "price": 29.99, "quantity": 2, "image": "/media/products/image.jpg", "store_code": "ACME" }
Key Functions: • loadCart(): Load from localStorage • saveCart(): Persist to localStorage • addToCart(product, quantity): Add item • updateCartItem(id, quantity): Update quantity • removeFromCart(id): Remove item • clearCart(): Empty cart • cartTotal: Computed total price
Cart Persistence: • Survives page refresh • Shared across storefront pages • Cleared on checkout completion • Synced across tabs (optional)
🔍 SEARCH & FILTERS ═════════════════════════════════════════════════════════════════
Search System:
-
Search Modal • Overlay with input • Live search as you type • Keyboard shortcuts (Cmd+K)
-
Search API POST /api/v1/storefront/{store_code}/search { "query": "laptop", "category": "electronics", "min_price": 100, "max_price": 1000, "sort": "price:asc" }
-
Client-Side Filtering • Filter products array • No API call needed for basic filters • Use for in-memory datasets
Filter Components: • Category dropdown • Price range slider • Sort options • Availability toggle • Brand checkboxes
📱 RESPONSIVE DESIGN ═════════════════════════════════════════════════════════════════
Breakpoints (Tailwind): • sm: 640px (mobile landscape) • md: 768px (tablet) • lg: 1024px (desktop) • xl: 1280px (large desktop)
Product Grid Responsive: • Mobile: 1 column • Tablet: 2 columns • Desktop: 4 columns
Example:
Touch Optimization: • Larger touch targets (min 44x44px) • Swipeable carousels • Touch-friendly buttons • Tap to zoom images
🌙 DARK MODE ═════════════════════════════════════════════════════════════════
Implementation:
- Alpine.js state: dark: boolean
- HTML class binding: :class="{ 'dark': dark }"
- Tailwind variants: dark:bg-gray-800
- LocalStorage persistence
- Store colors adapt to dark mode
Toggle Button: <button @click="toggleTheme()">
Dark Mode Colors: • Background: dark:bg-gray-900 • Text: dark:text-gray-100 • Borders: dark:border-gray-700 • Cards: dark:bg-gray-800
Store Colors: • Primary color adjusts brightness • Maintains brand identity • Readable in both modes
🔐 CUSTOMER AUTHENTICATION ═════════════════════════════════════════════════════════════════
Optional Auth System:
Guest Checkout: ✅ No account required ✅ Email for order updates ✅ Quick checkout
Account Features: ✅ Order history ✅ Saved addresses ✅ Wishlist ✅ Profile management
Auth Flow:
- Login/Register → POST /api/v1/storefront/auth/login (with Referer header)
- Middleware → Extracts store from Referer
- API → Validates credentials for store's customers
- API → Returns JWT token + sets cookie (path=/storefront)
- JavaScript → Store token in localStorage
- API Client → Add token to authenticated requests
- Optional → Use account features (orders, profile, etc.)
Authentication Pages
Added: 2025-11-24
All authentication pages use Tailwind CSS, Alpine.js, and theme integration for a consistent, branded experience across all stores.
✅ Login Page (app/templates/storefront/account/login.html) ────────────────────────────────────────────────────────────────── Route: /storefront/account/login
Features: • Two-column layout (branding + form) • Email and password fields with validation • Password visibility toggle • "Remember me" checkbox • Error and success message alerts • Loading states with animated spinner • Links to register and forgot password • Theme-aware colors from CSS variables • Dark mode support • Mobile responsive design
Alpine.js Component: function customerLogin() { return { credentials: { email: '', password: '' }, rememberMe: false, showPassword: false, loading: false, errors: {}, alert: { show: false, type: 'error', message: '' }, dark: false,
async handleLogin() {
// POST /api/v1/storefront/auth/login
// Store token in localStorage
// Redirect to account or return URL
}
}
}
API Endpoint: POST /api/v1/storefront/auth/login Body: { email_or_username, password } Returns: { access_token, user }
✅ Register Page (app/templates/storefront/account/register.html) ────────────────────────────────────────────────────────────────── Route: /storefront/account/register
Features: • Two-column layout with store branding • First name, last name, email fields • Phone number (optional) • Password with strength requirements • Confirm password field • Marketing consent checkbox • Real-time client-side validation • Password visibility toggle • Theme-aware styling • Loading states • Redirects to login after success
Validation Rules: • First name: required • Last name: required • Email: required, valid format • Password: min 8 chars, 1 letter, 1 number • Confirm password: must match password
Alpine.js Component: function customerRegistration() { return { formData: { first_name: '', last_name: '', email: '', phone: '', password: '', marketing_consent: false }, confirmPassword: '',
validateForm() {
// Validates all fields
// Returns true if valid
},
async handleRegister() {
// POST /api/v1/storefront/auth/register
// Redirect to login?registered=true
}
}
}
API Endpoint: POST /api/v1/storefront/auth/register Body: { first_name, last_name, email, phone?, password, marketing_consent } Returns: { message }
✅ Forgot Password Page (app/templates/storefront/account/forgot-password.html) ────────────────────────────────────────────────────────────────── Route: /storefront/account/forgot-password
Features: • Two-column layout with store branding • Email input field • Two-state interface: 1. Form submission state 2. Success confirmation state • Success state with checkmark icon • Option to retry if email not received • Theme-aware styling • Links back to login and storefront • Dark mode support
Alpine.js Component: function forgotPassword() { return { email: '', emailSent: false, loading: false,
async handleSubmit() {
// POST /api/v1/storefront/auth/forgot-password
// Show success message
// emailSent = true
}
}
}
API Endpoint: POST /api/v1/storefront/auth/forgot-password Body: { email } Returns: { message }
🎨 THEME INTEGRATION ──────────────────────────────────────────────────────────────────
All authentication pages inject store theme CSS variables:
<style id="store-theme-variables"> :root { {% for key, value in theme.css_variables.items() %} {{ key }}: {{ value }}; {% endfor %} } /* Theme-aware button and focus colors */ .btn-primary-theme { background-color: var(--color-primary); } .btn-primary-theme:hover:not(:disabled) { background-color: var(--color-primary-dark, var(--color-primary)); filter: brightness(0.9); } .focus-primary:focus { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb, 124, 58, 237), 0.1); } </style>Key Theme Elements: • Left panel background: var(--color-primary) • Submit buttons: var(--color-primary) • Links: var(--color-primary) • Checkboxes: var(--color-primary) • Focus states: var(--color-primary) with transparency • Store logo from theme.branding.logo
Benefits: ✅ Each store's auth pages match their brand ✅ Consistent with main storefront design ✅ Dark mode adapts to store colors ✅ Professional, polished appearance
📱 RESPONSIVE DESIGN ──────────────────────────────────────────────────────────────────
Mobile (<640px): • Vertical layout (image on top, form below) • Smaller padding and spacing • Full-width buttons • Touch-friendly input fields
Tablet (640px-1024px): • Side-by-side layout begins • Balanced column widths • Comfortable spacing
Desktop (>1024px): • Full two-column layout • Max width container (max-w-4xl) • Centered on page • Larger brand imagery
🔒 SECURITY FEATURES ──────────────────────────────────────────────────────────────────
Client-Side: • Input validation before submission • Password visibility toggle • HTTPS required • No sensitive data in URLs • Token stored in localStorage (not cookies)
Server-Side (API handles): • Password hashing (bcrypt) • Email verification • Rate limiting • CSRF protection • SQL injection prevention
📡 API CLIENT ═════════════════════════════════════════════════════════════════
Location: app/static/shared/js/api-client.js
⭐ NEW USAGE (as of 2025-11-22): No store_code needed! Store extracted from Referer header automatically.
Usage: // Product catalog const products = await fetch('/api/v1/storefront/products');
// Add to cart const response = await fetch('/api/v1/storefront/cart/session123/items', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product_id: 1, quantity: 2 }) });
// Place order const order = await fetch('/api/v1/storefront/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderData) });
Features: ✅ Automatic error handling ✅ JSON parsing ✅ Loading states ✅ Auth token injection (if logged in)
🐛 LOGGING ═════════════════════════════════════════════════════════════════
Location: app/static/shared/js/log-config.js
Storefront-Specific Logging: shopLog.info('Product added to cart', product); shopLog.error('Checkout failed', error); shopLog.debug('Search query', { query, results });
Levels: • INFO: User actions • ERROR: Failures and exceptions • DEBUG: Development debugging • WARN: Warnings
🎭 ICONS ═════════════════════════════════════════════════════════════════
Location: app/static/shared/js/icons.js
Usage:
Storefront Icons: • shopping-cart, shopping-bag • heart (wishlist) • search, filter • eye (view) • star (rating) • truck (shipping) • check (success) • x (close) • chevron-left, chevron-right • spinner (loading)
🚀 PERFORMANCE ═════════════════════════════════════════════════════════════════
Optimization Techniques:
-
Image Optimization • WebP format • Lazy loading • Responsive images (srcset) • CDN delivery
-
Code Splitting • Base layout loads first • Page-specific JS loads after • Deferred non-critical CSS
-
Caching • Browser cache for assets • LocalStorage for cart • Service worker (optional)
-
Lazy Loading • Products load as you scroll • Images load when visible • Infinite scroll for large catalogs
-
CDN Assets with Fallback • Tailwind CSS from CDN (fallback to local) • Alpine.js from CDN (fallback to local) • Works offline and in restricted networks • See: CDN Fallback Strategy
📊 PAGE-BY-PAGE BREAKDOWN ═════════════════════════════════════════════════════════════════
/ (Homepage) ────────────────────────────────────────────────────────────────── Purpose: Display featured products, hero section Components: • Hero banner with CTA • Featured products grid • Category cards • About store section Data Sources: • GET /api/v1/storefront/products?is_featured=true
/products ────────────────────────────────────────────────────────────────── Purpose: Browse all products with filters Components: • Product grid • Filter sidebar • Sort dropdown • Pagination Data Sources: • GET /api/v1/storefront/products?skip=0&limit=20 • GET /api/v1/storefront/products?search=query • Filters applied client-side or server-side
/products/{product_id} ────────────────────────────────────────────────────────────────── Purpose: Single product detail page Components: • Image gallery • Product info • Add to cart form • Related products • Reviews (optional) Data Sources: • GET /api/v1/storefront/products/{id} • GET /api/v1/storefront/products?limit=4 (related products)
/cart ────────────────────────────────────────────────────────────────── Purpose: Review cart contents before checkout Components: • Cart items list • Quantity adjusters • Remove buttons • Cart total • Checkout button Data Sources: • LocalStorage cart • No API call needed
/checkout ────────────────────────────────────────────────────────────────── Purpose: Complete purchase Components: • Shipping form • Payment form • Order summary • Submit button Data Sources: • POST /api/v1/storefront/orders • Stripe/PayPal integration
/search ────────────────────────────────────────────────────────────────── Purpose: Search results page Components: • Search input • Results grid • Filter options • Sort options Data Sources: • GET /api/v1/storefront/products?search=query
/category/{category_slug} ────────────────────────────────────────────────────────────────── Purpose: Browse products by category Components: • Breadcrumbs • Product grid • Subcategories • Filters Data Sources: • GET /api/v1/storefront/products?category={slug}
/about ────────────────────────────────────────────────────────────────── Purpose: About the store Components: • Store story • Team photos • Values/mission • Contact info Data Sources: • Store info from middleware • Static content
/contact ────────────────────────────────────────────────────────────────── Purpose: Contact form Components: • Contact form • Map (optional) • Business hours • Social links Data Sources: • CMS content page (GET /api/v1/storefront/content-pages/contact) • Form submission to store email
🎓 LEARNING PATH ═════════════════════════════════════════════════════════════════
For New Developers:
-
Understand Architecture (1 hour) → Read this document → Review file structure → Examine base template → Understand theme system
-
Study Existing Page (2 hours) → Open home.html → Open storefront-layout.js → Trace product loading flow → Examine cart management
-
Create Simple Page (4 hours) → Copy templates → Modify for new feature → Test with different store themes → Verify responsive design
-
Add Complex Feature (1 day) → Product filters → Cart operations → API integration → Search functionality
-
Master Patterns (1 week) → All common patterns → Theme customization → Performance optimization → Mobile responsiveness
🔄 DEPLOYMENT CHECKLIST ═════════════════════════════════════════════════════════════════
Before Deploying: □ Build Tailwind CSS □ Minify JavaScript □ Test all routes □ Test with multiple store themes □ Verify cart persistence □ Check mobile responsive □ Test dark mode □ Validate product display □ Test checkout flow □ Check image optimization □ Test search functionality □ Verify social links □ Test across browsers □ Check console for errors □ Test in production mode
🚨 ERROR HANDLING & ERROR PAGES ═════════════════════════════════════════════════════════════════
Multi-Access Aware Error Pages:
All storefront error pages (404, 500, etc.) are store-context aware and display correct links based on the access method (domain, subdomain, or path-based).
Error Page Templates: • app/templates/storefront/errors/404.html - Not Found • app/templates/storefront/errors/400.html - Bad Request • app/templates/storefront/errors/401.html - Unauthorized • app/templates/storefront/errors/403.html - Forbidden • app/templates/storefront/errors/422.html - Validation Error • app/templates/storefront/errors/429.html - Rate Limited • app/templates/storefront/errors/500.html - Server Error • app/templates/storefront/errors/502.html - Bad Gateway • app/templates/storefront/errors/base.html - Base error template • app/templates/storefront/errors/generic.html - Generic error
Error Renderer (app/exceptions/error_renderer.py):
Calculates base_url dynamically based on store access method:
def _get_context_data(self, request: Request, ...): store = getattr(request.state, 'store', None) access_method = getattr(request.state, "access_method", None) store_context = getattr(request.state, "store_context", None)
# Calculate base_url for storefront links
base_url = "/"
if access_method == "path" and store:
full_prefix = store_context.get('full_prefix', '/store/')
base_url = f"{full_prefix}{store.subdomain}/"
return {
"request": request,
"store": store,
"base_url": base_url, # ⭐ Used in error templates
...
}
Error Template Usage:
All error page links use {{ base_url }} prefix for correct routing:
{# ❌ BAD - Hardcoded links #} Continue Shopping View Products
{# ✅ GOOD - Context-aware links #} Continue Shopping View Products
How It Works:
- Error occurs (404, 500, etc.)
- Exception handler detects storefront context
- error_renderer.py calculates base_url from store_context
- Error template renders with correct base_url
- Links work for all access methods:
- Domain: customshop.com → base_url = "/"
- Subdomain: orion.platform.com → base_url = "/"
- Path: localhost/storefront/orion/ → base_url = "/storefront/orion/"
Benefits: ✅ Error pages work correctly regardless of access method ✅ No broken links in error states ✅ Consistent user experience ✅ Store branding maintained in errors
🔒 SECURITY ═════════════════════════════════════════════════════════════════
Best Practices:
-
Input Validation ✅ Validate all form inputs ✅ Sanitize user content ✅ XSS prevention
-
Cart Security ✅ Validate cart on server ✅ Check stock availability ✅ Verify prices server-side ✅ Never trust client cart
-
Payment Security ✅ Use trusted payment providers ✅ Never store card details ✅ HTTPS only ✅ PCI compliance
-
Rate Limiting ✅ Search endpoint throttling ✅ Contact form limits ✅ Checkout attempt limits
📚 REFERENCE LINKS ═════════════════════════════════════════════════════════════════
Documentation: • Alpine.js: https://alpinejs.dev/ • Tailwind CSS: https://tailwindcss.com/ • Jinja2: https://jinja.palletsprojects.com/ • FastAPI: https://fastapi.tiangolo.com/
Internal Docs: • Page Template Guide: FRONTEND_STOREFRONT_ALPINE_PAGE_TEMPLATE.md • Multi-Theme Guide: MULTI_THEME_STOREFRONT_GUIDE.md • API Documentation: API_REFERENCE.md • Database Schema: DATABASE_SCHEMA.md
══════════════════════════════════════════════════════════════════ STOREFRONT FRONTEND ARCHITECTURE Theme-Driven, Customer-Focused, Brand-Consistent ══════════════════════════════════════════════════════════════════