Files
orion/docs/frontend/shared/ui-components-quick-reference.md
Samir Boulahtit 95a8ffc645 docs: add architecture rules and docs for e-commerce components
Architecture rules added:
- FE-008: Use number_stepper macro for quantity inputs
- FE-009: Use product_card macro for product displays
- FE-010: Use product_grid macro for product listings
- FE-011: Use add_to_cart macros for cart interactions
- FE-012: Use mini_cart macro for cart dropdown

Documentation:
- Update ui-components-quick-reference.md with e-commerce section
- Add component-standards.md for standardization guidelines
- Add ecommerce-components-proposal.md with full 20-component roadmap
- Update validate_architecture.py with FE-008 detection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 17:04:28 +01:00

13 KiB

UI Components Quick Reference

Most Common Patterns

📝 Form Field (Basic)

<label class="block mb-4 text-sm">
    <span class="text-gray-700 dark:text-gray-400">Field Name</span>
    <input
        type="text"
        x-model="formData.field"
        class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
    />
</label>

📝 Required Field with Error

<label class="block mb-4 text-sm">
    <span class="text-gray-700 dark:text-gray-400">
        Field Name <span class="text-red-600">*</span>
    </span>
    <input
        type="text"
        x-model="formData.field"
        required
        :class="{ 'border-red-600': errors.field }"
        class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple form-input"
    />
    <span x-show="errors.field" class="text-xs text-red-600 dark:text-red-400" x-text="errors.field"></span>
</label>

📝 Read-Only Field

<label class="block mb-4 text-sm">
    <span class="text-gray-700 dark:text-gray-400">Field Name</span>
    <input
        type="text"
        x-model="data.field"
        disabled
        class="block w-full mt-1 text-sm bg-gray-100 border-gray-300 rounded-md dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 cursor-not-allowed"
    />
</label>

🃏 Stats Card

<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
    <div class="p-3 mr-4 text-purple-500 bg-purple-100 rounded-full dark:text-purple-100 dark:bg-purple-500">
        <span x-html="$icon('user-group', 'w-5 h-5')"></span>
    </div>
    <div>
        <p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">Label</p>
        <p class="text-lg font-semibold text-gray-700 dark:text-gray-200">Value</p>
    </div>
</div>

🃏 Info Card

<div class="px-4 py-3 bg-white rounded-lg shadow-md dark:bg-gray-800">
    <h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">Title</h3>
    <div class="space-y-3">
        <div>
            <p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Label</p>
            <p class="text-sm text-gray-700 dark:text-gray-300">Value</p>
        </div>
    </div>
</div>

🔘 Primary Button

<button class="px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700 focus:outline-none">
    Click Me
</button>

🔘 Button with Icon

<button class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700">
    <span x-html="$icon('plus', 'w-4 h-4 mr-2')"></span>
    Add Item
</button>

🔘 Secondary Button

<button class="px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition-colors duration-150 bg-white border border-gray-300 rounded-lg hover:border-gray-400 dark:text-gray-400 dark:border-gray-600 dark:bg-gray-800">
    Cancel
</button>

🏷️ Status Badge (Success)

<span class="inline-flex items-center px-3 py-1 text-xs font-semibold leading-tight text-green-700 bg-green-100 rounded-full dark:bg-green-700 dark:text-green-100">
    <span x-html="$icon('check-circle', 'w-3 h-3 mr-1')"></span>
    Active
</span>

🏷️ Status Badge (Warning)

<span class="inline-flex items-center px-3 py-1 text-xs font-semibold leading-tight text-orange-700 bg-orange-100 rounded-full dark:bg-orange-700 dark:text-orange-100">
    <span x-html="$icon('clock', 'w-3 h-3 mr-1')"></span>
    Pending
</span>

🏷️ Status Badge (Danger)

<span class="inline-flex items-center px-3 py-1 text-xs font-semibold leading-tight text-red-700 bg-red-100 rounded-full dark:bg-red-700 dark:text-red-100">
    <span x-html="$icon('x-circle', 'w-3 h-3 mr-1')"></span>
    Inactive
</span>

Number Stepper

A number input with +/- buttons for quantity selection. Ideal for cart quantities, batch sizes, and product pages.

Basic Number Stepper

{% from 'shared/macros/inputs.html' import number_stepper %}

{# Basic usage - cart quantity #}
{{ number_stepper(model='quantity', min=1, max=99) }}

Size Variants

{# 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

{{ 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)

{% 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 %}

<!-- Tab content panels -->
<div x-show="activeTab === 'dashboard'" x-transition>
    Dashboard content...
</div>

🗂️ Inline Tabs (with count badges)

{% from 'shared/macros/tabs.html' import tabs_inline, tab_button %}

<div class="flex justify-between gap-4">
    {% 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 %}
    <div>Search...</div>
</div>

🗂️ Tabs with Custom Click Handlers

{% 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)

<div class="grid gap-6 md:grid-cols-2">
    <!-- Column 1 -->
    <div>...</div>
    <!-- Column 2 -->
    <div>...</div>
</div>

4 Columns (Responsive)

<div class="grid gap-6 mb-8 md:grid-cols-2 xl:grid-cols-4">
    <!-- Cards -->
</div>

Color Classes

Background Colors

  • Primary: bg-purple-600
  • Success: bg-green-600
  • Warning: bg-orange-600
  • Danger: bg-red-600
  • Info: bg-blue-600

Text Colors

  • Primary: text-purple-600
  • Success: text-green-600
  • Warning: text-orange-600
  • Danger: text-red-600
  • Info: text-blue-600

Icon Colors

  • Primary: text-purple-500 bg-purple-100
  • Success: text-green-500 bg-green-100
  • Warning: text-orange-500 bg-orange-100
  • Danger: text-red-500 bg-red-100
  • Info: text-blue-500 bg-blue-100

Common Icons

  • user-group - Users/Teams
  • badge-check - Verified
  • check-circle - Success
  • x-circle - Error/Inactive
  • clock - Pending
  • calendar - Dates
  • refresh - Update
  • edit - Edit
  • delete - Delete
  • plus - Add
  • arrow-left - Back
  • exclamation - Warning

Spacing

  • Small gap: gap-3
  • Medium gap: gap-6
  • Large gap: gap-8
  • Margin bottom: mb-4, mb-6, mb-8
  • Padding: p-3, p-4, px-4 py-3

Quick Copy-Paste: Page Structure

{# app/templates/admin/your-page.html #}
{% extends "admin/base.html" %}

{% block title %}Your Page{% endblock %}

{% block alpine_data %}yourPageData(){% endblock %}

{% block content %}
<!-- Page Header -->
<div class="my-6">
    <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">
        Page Title
    </h2>
</div>

<!-- Loading State -->
<div x-show="loading" class="text-center py-12">
    <span x-html="$icon('spinner', 'inline w-8 h-8 text-purple-600')"></span>
    <p class="mt-2 text-gray-600 dark:text-gray-400">Loading...</p>
</div>

<!-- Content -->
<div x-show="!loading">
    <div class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
        <!-- Your content here -->
    </div>
</div>
{% endblock %}

Remember

  1. Always use dark: variants for dark mode
  2. Add :disabled="saving" to buttons during operations
  3. Use x-show for conditional display
  4. Use x-text for dynamic text
  5. Use x-html="$icon(...)" for icons
  6. Validation errors: border-red-600 class
  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

{% 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

{% 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

{% 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)

{% 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

// 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!