# ThrillWiki Frontend Rewrite Plan
## HTMX + Alpine.js Enhanced Implementation

**Created:** 2025-12-20  
**Status:** Planning  
**Scope:** Complete frontend redesign for enhanced design, modularity, and feature parity

---

## Executive Summary

This plan outlines a comprehensive rewrite of the ThrillWiki HTMX/Alpine.js frontend to achieve:
1. **Enhanced Design** - Modern UI with Shadcn UI patterns, animations, and polished UX
2. **Improved Modularity** - Reusable component architecture with clear separation of concerns
3. **Feature Parity** - Full functionality matching the previous React frontend

### Current State Analysis

Based on the [CRITICAL_ANALYSIS_HTMX_ALPINE.md](../docs/CRITICAL_ANALYSIS_HTMX_ALPINE.md), the existing implementation has:
- ✅ Basic template structure with Tailwind CSS
- ✅ Design tokens and component library documentation
- ✅ Basic HTMX integration for partial page updates
- ✅ Simple Alpine.js state management
- ✅ Dark/light mode support
- ❌ ~60-70% missing functionality vs React frontend
- ❌ Limited component reusability
- ❌ Incomplete state management patterns
- ❌ Missing advanced UI components

---

## Architecture Overview

```mermaid
graph TB
    subgraph Frontend Architecture
        subgraph Templates Layer
            BASE[base/base.html]
            LAYOUTS[Layout Templates]
            PAGES[Page Templates]
            COMPONENTS[Component Templates]
            PARTIALS[HTMX Partials]
        end
        
        subgraph Alpine.js Layer
            STORES[Global Stores]
            COMPONENTS_JS[Component Data]
            UTILS[Utility Functions]
        end
        
        subgraph HTMX Layer
            TRIGGERS[Event Triggers]
            TARGETS[Swap Targets]
            INDICATORS[Loading States]
        end
        
        subgraph Design System
            TOKENS[Design Tokens CSS]
            TAILWIND[Tailwind Config]
            SHADCN[Shadcn UI Components]
        end
    end
    
    BASE --> LAYOUTS
    LAYOUTS --> PAGES
    PAGES --> COMPONENTS
    COMPONENTS --> PARTIALS
    
    STORES --> COMPONENTS_JS
    COMPONENTS_JS --> UTILS
    
    TOKENS --> TAILWIND
    TAILWIND --> SHADCN
```

---

## Phase 1: Foundation & Infrastructure

### 1.1 Design System Enhancement

#### Design Tokens Consolidation
- Merge existing CSS variables from [base.html](../templates/base/base.html:41) with [design-tokens.md](../memory-bank/design-system/design-tokens.md)
- Create unified CSS custom properties file: `static/css/design-tokens.css`
- Implement CSS layers for better cascade management

```css
/* Proposed CSS Layer Structure */
@layer reset, tokens, base, components, utilities, overrides;
```

#### Tailwind CSS 4 Migration
- Update [tailwind.config.js](../tailwind.config.js) for Tailwind CSS 4 compatibility
- Extend color palette with design token references
- Add HTMX state variants (existing htmx-settling, htmx-request, etc.)
- Configure container queries for component-level responsiveness

#### Shadcn UI Integration
Create Django template versions of key Shadcn UI components:
- [ ] Button variants (primary, secondary, outline, ghost, destructive)
- [ ] Card component with header, body, footer slots
- [ ] Dialog/Modal with focus trapping
- [ ] Dropdown Menu with keyboard navigation
- [ ] Form components (Input, Select, Checkbox, Radio, Switch)
- [ ] Toast/Notification system
- [ ] Skeleton loading components
- [ ] Avatar with fallback
- [ ] Badge variants
- [ ] Tabs component
- [ ] Accordion/Collapsible
- [ ] Command palette (for advanced search)

### 1.2 Template Architecture Restructure

#### Proposed Directory Structure
```
templates/
├── base/
│   ├── base.html              # Root template with design system
│   └── htmx-base.html         # HTMX-specific base for partials
├── layouts/
│   ├── default.html           # Standard page layout
│   ├── dashboard.html         # User dashboard layout
│   ├── auth.html              # Authentication pages layout
│   ├── sidebar.html           # Sidebar navigation layout
│   └── full-width.html        # Full-width content layout
├── components/
│   ├── ui/                    # Shadcn-style UI components
│   │   ├── button.html
│   │   ├── card.html
│   │   ├── dialog.html
│   │   ├── dropdown.html
│   │   ├── form/
│   │   │   ├── input.html
│   │   │   ├── select.html
│   │   │   ├── checkbox.html
│   │   │   └── textarea.html
│   │   ├── toast.html
│   │   ├── skeleton.html
│   │   └── avatar.html
│   ├── navigation/
│   │   ├── navbar.html
│   │   ├── mobile-menu.html
│   │   ├── breadcrumbs.html
│   │   ├── pagination.html
│   │   └── user-menu.html
│   ├── search/
│   │   ├── search-bar.html
│   │   ├── search-results.html
│   │   ├── autocomplete.html
│   │   └── filters.html
│   ├── cards/
│   │   ├── park-card.html
│   │   ├── ride-card.html
│   │   ├── manufacturer-card.html
│   │   └── operator-card.html
│   └── data-display/
│       ├── stats-card.html
│       ├── data-table.html
│       ├── image-gallery.html
│       └── rating-display.html
├── pages/
│   ├── home/
│   │   └── homepage.html
│   ├── parks/
│   │   ├── list.html
│   │   ├── detail.html
│   │   ├── create.html
│   │   └── edit.html
│   ├── rides/
│   │   ├── list.html
│   │   ├── detail.html
│   │   ├── create.html
│   │   └── edit.html
│   ├── auth/
│   │   ├── login.html
│   │   ├── register.html
│   │   ├── forgot-password.html
│   │   └── reset-password.html
│   ├── user/
│   │   ├── profile.html
│   │   ├── settings.html
│   │   └── dashboard.html
│   └── search/
│       └── results.html
├── partials/                   # HTMX swap targets
│   ├── homepage/
│   ├── parks/
│   ├── rides/
│   ├── search/
│   └── user/
└── emails/                     # Email templates
```

### 1.3 Alpine.js Architecture

#### Global Store System
Create centralized Alpine.js stores in `static/js/stores/`:

```javascript
// static/js/stores/index.js
document.addEventListener('alpine:init', () => {
    // Authentication Store
    Alpine.store('auth', {
        user: null,
        isAuthenticated: false,
        permissions: [],
        
        init() {
            // Initialize from server-rendered data
            this.user = window.__AUTH_USER__ || null;
            this.isAuthenticated = !!this.user;
        },
        
        async login(credentials) { /* ... */ },
        async logout() { /* ... */ },
        hasPermission(permission) { /* ... */ }
    });
    
    // Theme Store
    Alpine.store('theme', {
        isDark: false,
        
        init() {
            this.isDark = localStorage.getItem('theme') === 'dark' ||
                (!localStorage.getItem('theme') && 
                 window.matchMedia('(prefers-color-scheme: dark)').matches);
            this.apply();
        },
        
        toggle() {
            this.isDark = !this.isDark;
            this.apply();
        },
        
        apply() {
            document.documentElement.classList.toggle('dark', this.isDark);
            localStorage.setItem('theme', this.isDark ? 'dark' : 'light');
        }
    });
    
    // Search Store
    Alpine.store('search', {
        query: '',
        results: [],
        isOpen: false,
        isLoading: false,
        filters: {},
        
        async search(query) { /* ... */ },
        clearSearch() { /* ... */ },
        applyFilters(filters) { /* ... */ }
    });
    
    // Toast/Notification Store
    Alpine.store('toast', {
        toasts: [],
        
        show(message, type = 'info', duration = 5000) {
            const id = Date.now();
            this.toasts.push({ id, message, type, duration });
            if (duration > 0) {
                setTimeout(() => this.dismiss(id), duration);
            }
            return id;
        },
        
        dismiss(id) {
            this.toasts = this.toasts.filter(t => t.id !== id);
        },
        
        success(message) { return this.show(message, 'success'); },
        error(message) { return this.show(message, 'error'); },
        warning(message) { return this.show(message, 'warning'); },
        info(message) { return this.show(message, 'info'); }
    });
    
    // UI State Store
    Alpine.store('ui', {
        sidebarOpen: false,
        modalStack: [],
        
        openModal(id) { this.modalStack.push(id); },
        closeModal(id) { 
            this.modalStack = this.modalStack.filter(m => m !== id); 
        },
        isModalOpen(id) { return this.modalStack.includes(id); }
    });
});
```

#### Reusable Component Functions
Create component-specific Alpine.js data functions:

```javascript
// static/js/components/index.js

// Modal Component
Alpine.data('modal', (config = {}) => ({
    isOpen: false,
    modalId: config.id || 'default',
    closeOnBackdrop: config.closeOnBackdrop !== false,
    closeOnEscape: config.closeOnEscape !== false,
    
    init() {
        if (this.closeOnEscape) {
            document.addEventListener('keydown', (e) => {
                if (e.key === 'Escape' && this.isOpen) this.close();
            });
        }
    },
    
    open() {
        this.isOpen = true;
        document.body.style.overflow = 'hidden';
        this.$dispatch('modal-opened', { id: this.modalId });
    },
    
    close() {
        this.isOpen = false;
        document.body.style.overflow = '';
        this.$dispatch('modal-closed', { id: this.modalId });
    },
    
    toggle() {
        this.isOpen ? this.close() : this.open();
    }
}));

// Dropdown Component
Alpine.data('dropdown', (config = {}) => ({
    isOpen: false,
    placement: config.placement || 'bottom-start',
    
    toggle() { this.isOpen = !this.isOpen; },
    close() { this.isOpen = false; },
    
    // Keyboard navigation
    handleKeydown(event) {
        if (event.key === 'ArrowDown') { /* ... */ }
        if (event.key === 'ArrowUp') { /* ... */ }
        if (event.key === 'Enter') { /* ... */ }
    }
}));

// Search with Autocomplete
Alpine.data('searchAutocomplete', (config = {}) => ({
    query: '',
    results: [],
    isLoading: false,
    selectedIndex: -1,
    minChars: config.minChars || 2,
    debounceMs: config.debounceMs || 300,
    endpoint: config.endpoint || '/api/v1/search/',
    
    async search() {
        if (this.query.length < this.minChars) {
            this.results = [];
            return;
        }
        
        this.isLoading = true;
        // Debounced search implementation
    },
    
    selectResult(index) { /* ... */ },
    handleKeydown(event) { /* ... */ },
    clear() { /* ... */ }
}));

// Form Validation
Alpine.data('formValidation', (rules = {}) => ({
    errors: {},
    touched: {},
    isSubmitting: false,
    
    validate(field, value) { /* ... */ },
    validateAll() { /* ... */ },
    isValid() { return Object.keys(this.errors).length === 0; },
    hasError(field) { return this.touched[field] && this.errors[field]; },
    reset() { /* ... */ }
}));

// Tabs Component
Alpine.data('tabs', (config = {}) => ({
    activeTab: config.defaultTab || 0,
    
    setActiveTab(index) {
        this.activeTab = index;
        this.$dispatch('tab-changed', { index });
    },
    
    isActive(index) {
        return this.activeTab === index;
    }
}));

// Image Gallery with Lightbox
Alpine.data('imageGallery', () => ({
    images: [],
    currentIndex: 0,
    isLightboxOpen: false,
    
    openLightbox(index) { /* ... */ },
    closeLightbox() { /* ... */ },
    next() { /* ... */ },
    prev() { /* ... */ }
}));
```

---

## Phase 2: Core Component Implementation

### 2.1 Navigation Components

#### Enhanced Navbar
- Sticky header with backdrop blur
- Responsive design with mobile drawer
- User authentication state handling
- Search integration with command palette (Cmd/Ctrl + K)
- Theme toggle with system preference detection
- Active route highlighting

#### Mobile Navigation
- Slide-in drawer with gesture support
- Focus trapping for accessibility
- Smooth animations with Alpine.js transitions
- Hierarchical menu support

#### Breadcrumbs
- Automatic generation from URL structure
- Schema.org markup for SEO
- Mobile-friendly truncation

### 2.2 Card Components

#### Park Card
```html
<!-- Template: components/cards/park-card.html -->
{% load static %}

<article class="card card-hover group" 
         x-data="{ isHovered: false }"
         @mouseenter="isHovered = true"
         @mouseleave="isHovered = false">
    
    <!-- Image with overlay -->
    <div class="card-image">
        <img src="{{ park.image_url|default:'/static/images/placeholder-park.jpg' }}" 
             alt="{{ park.name }}"
             class="card-image-img"
             loading="lazy">
        
        <!-- Status Badge -->
        <div class="card-image-overlay">
            <span class="badge badge-{{ park.status }}">
                {{ park.get_status_display }}
            </span>
        </div>
        
        <!-- Hover Actions -->
        <div x-show="isHovered"
             x-transition:enter="transition ease-out duration-200"
             x-transition:enter-start="opacity-0"
             x-transition:enter-end="opacity-100"
             class="absolute inset-0 flex items-end p-4 bg-gradient-to-t from-black/60 to-transparent">
            <div class="flex gap-2">
                <button class="btn btn-sm btn-secondary">
                    {% include 'components/icons/heart.html' %}
                </button>
                <button class="btn btn-sm btn-secondary">
                    {% include 'components/icons/share.html' %}
                </button>
            </div>
        </div>
    </div>
    
    <!-- Content -->
    <div class="card-body">
        <h3 class="card-title">
            <a href="{{ park.get_absolute_url }}" class="card-title-link">
                {{ park.name }}
            </a>
        </h3>
        
        <p class="card-text">{{ park.description|truncatewords:25 }}</p>
        
        <!-- Meta Information -->
        <div class="card-meta">
            <span class="card-meta-item">
                {% include 'components/icons/location.html' with class='icon-xs' %}
                {{ park.location.city }}, {{ park.location.country }}
            </span>
            <span class="card-meta-item">
                {% include 'components/icons/ride.html' with class='icon-xs' %}
                {{ park.rides_count }} rides
            </span>
        </div>
        
        <!-- Rating -->
        {% if park.average_rating %}
        <div class="flex items-center gap-2 mt-3">
            {% include 'components/data-display/rating-stars.html' with rating=park.average_rating %}
            <span class="text-sm text-muted-foreground">
                ({{ park.review_count }} reviews)
            </span>
        </div>
        {% endif %}
    </div>
</article>
```

#### Ride Card
- Similar structure with ride-specific data
- Category/type badges
- Height/intensity indicators
- Manufacturer attribution

### 2.3 Form Components

#### Enhanced Input Component
```html
<!-- Template: components/ui/form/input.html -->
{% load widget_tweaks %}

<div class="form-group" 
     x-data="{ focused: false, hasValue: {{ field.value|yesno:'true,false' }} }">
    
    {% if label %}
    <label for="{{ field.id_for_label }}" 
           class="form-label"
           :class="{ 'text-primary': focused }">
        {{ label }}
        {% if field.field.required %}
        <span class="text-error-500" aria-label="required">*</span>
        {% endif %}
    </label>
    {% endif %}
    
    <div class="relative">
        {% if icon_left %}
        <div class="absolute -translate-y-1/2 left-3 top-1/2 text-muted-foreground">
            {{ icon_left }}
        </div>
        {% endif %}
        
        {{ field|add_class:"form-input"|attr:"x-on:focus=focused = true"|attr:"x-on:blur=focused = false; hasValue = $el.value.length > 0" }}
        
        {% if icon_right %}
        <div class="absolute -translate-y-1/2 right-3 top-1/2 text-muted-foreground">
            {{ icon_right }}
        </div>
        {% endif %}
    </div>
    
    {% if field.help_text %}
    <p class="form-help">{{ field.help_text }}</p>
    {% endif %}
    
    {% if field.errors %}
    <div class="form-error" role="alert">
        {{ field.errors.0 }}
    </div>
    {% endif %}
</div>
```

### 2.4 Modal/Dialog System

#### HTMX-Powered Modal
```html
<!-- Template: components/ui/dialog.html -->
<div x-data="modal({ id: '{{ modal_id }}', closeOnBackdrop: {{ close_on_backdrop|default:'true' }} })"
     x-show="isOpen"
     x-on:open-modal.window="if ($event.detail.id === modalId) open()"
     x-on:close-modal.window="if ($event.detail.id === modalId) close()"
     class="modal-overlay"
     x-transition:enter="transition ease-out duration-300"
     x-transition:enter-start="opacity-0"
     x-transition:enter-end="opacity-100"
     x-transition:leave="transition ease-in duration-200"
     x-transition:leave-start="opacity-100"
     x-transition:leave-end="opacity-0"
     role="dialog"
     aria-modal="true"
     aria-labelledby="modal-title-{{ modal_id }}">
    
    <!-- Backdrop -->
    <div class="modal-backdrop" 
         @click="closeOnBackdrop && close()"
         aria-hidden="true"></div>
    
    <!-- Modal Content -->
    <div class="modal-container"
         x-transition:enter="transition ease-out duration-300"
         x-transition:enter-start="opacity-0 scale-95"
         x-transition:enter-end="opacity-100 scale-100"
         x-transition:leave="transition ease-in duration-200"
         x-transition:leave-start="opacity-100 scale-100"
         x-transition:leave-end="opacity-0 scale-95">
        
        <div class="modal-content">
            {% if show_header %}
            <div class="modal-header">
                <h2 id="modal-title-{{ modal_id }}" class="modal-title">
                    {{ title }}
                </h2>
                <button @click="close()" 
                        class="btn-icon"
                        aria-label="Close modal">
                    {% include 'components/icons/x.html' %}
                </button>
            </div>
            {% endif %}
            
            <div class="modal-body" 
                 id="modal-body-{{ modal_id }}"
                 hx-target="this"
                 hx-swap="innerHTML">
                {% block modal_content %}{% endblock %}
            </div>
            
            {% if show_footer %}
            <div class="modal-footer">
                {% block modal_footer %}
                <button @click="close()" class="btn btn-secondary">Cancel</button>
                <button class="btn btn-primary">Confirm</button>
                {% endblock %}
            </div>
            {% endif %}
        </div>
    </div>
</div>
```

### 2.5 Toast/Notification System

```html
<!-- Template: components/ui/toast-container.html -->
<div x-data
     class="fixed z-50 flex flex-col max-w-sm gap-2 bottom-4 right-4">
    
    <template x-for="toast in $store.toast.toasts" :key="toast.id">
        <div x-show="true"
             x-transition:enter="transition ease-out duration-300"
             x-transition:enter-start="opacity-0 translate-y-2"
             x-transition:enter-end="opacity-100 translate-y-0"
             x-transition:leave="transition ease-in duration-200"
             x-transition:leave-start="opacity-100"
             x-transition:leave-end="opacity-0 translate-x-full"
             :class="{
                 'bg-success-50 border-success-200 text-success-800': toast.type === 'success',
                 'bg-error-50 border-error-200 text-error-800': toast.type === 'error',
                 'bg-warning-50 border-warning-200 text-warning-800': toast.type === 'warning',
                 'bg-info-50 border-info-200 text-info-800': toast.type === 'info'
             }"
             class="flex items-start gap-3 p-4 border rounded-lg shadow-lg">
            
            <!-- Icon -->
            <div class="flex-shrink-0">
                <template x-if="toast.type === 'success'">
                    {% include 'components/icons/check-circle.html' %}
                </template>
                <template x-if="toast.type === 'error'">
                    {% include 'components/icons/x-circle.html' %}
                </template>
                <!-- ... other icons -->
            </div>
            
            <!-- Content -->
            <div class="flex-1">
                <p x-text="toast.message" class="text-sm font-medium"></p>
            </div>
            
            <!-- Dismiss -->
            <button @click="$store.toast.dismiss(toast.id)"
                    class="flex-shrink-0 p-1 transition-colors rounded hover:bg-black/10">
                {% include 'components/icons/x.html' with class='w-4 h-4' %}
            </button>
        </div>
    </template>
</div>
```

---

## Phase 3: Feature Implementation

### 3.1 Authentication System

#### Modal-Based Authentication
- Login modal with form validation
- Registration with multi-step wizard
- Password reset flow
- Social authentication integration
- Remember me functionality
- Session management with HTMX

#### User Profile & Settings
- Profile editing with image upload
- Account settings management
- Notification preferences
- Privacy settings
- Connected accounts

### 3.2 Advanced Search System

#### Global Search with Command Palette
```html
<!-- Cmd/Ctrl + K activated search -->
<div x-data="commandPalette()"
     @keydown.window.cmd.k.prevent="open()"
     @keydown.window.ctrl.k.prevent="open()">
    
    <div x-show="isOpen" 
         class="command-palette-overlay"
         x-transition>
        
        <div class="command-palette">
            <div class="command-palette-input-wrapper">
                <input type="text"
                       x-model="query"
                       @input.debounce.200ms="search()"
                       @keydown.arrow-down.prevent="navigateDown()"
                       @keydown.arrow-up.prevent="navigateUp()"
                       @keydown.enter.prevent="selectCurrent()"
                       @keydown.escape="close()"
                       placeholder="Search parks, rides, users..."
                       class="command-palette-input"
                       x-ref="input">
            </div>
            
            <div class="command-palette-results">
                <!-- Results grouped by category -->
                <template x-for="(group, groupName) in groupedResults">
                    <div class="command-palette-group">
                        <div class="command-palette-group-header" x-text="groupName"></div>
                        <template x-for="(result, index) in group">
                            <a :href="result.url"
                               class="command-palette-item"
                               :class="{ 'active': isSelected(result) }">
                                <!-- Result item -->
                            </a>
                        </template>
                    </div>
                </template>
            </div>
        </div>
    </div>
</div>
```

#### Filter System with URL Sync
- Multi-select filters
- Range sliders for numeric values
- Location-based filtering
- Real-time result updates via HTMX
- URL state synchronization for shareability

### 3.3 Data Tables

#### Advanced Data Table Component
- Sortable columns
- Pagination with HTMX
- Row selection
- Bulk actions
- Column visibility toggle
- Export functionality
- Responsive design with horizontal scroll

### 3.4 Image Gallery & Media

#### Image Gallery with Lightbox
- Lazy loading with intersection observer
- Touch gestures for mobile
- Keyboard navigation
- Zoom functionality
- Image upload with drag & drop
- Progress indicators

---

## Phase 4: HTMX Integration Patterns

### 4.1 Standard Patterns

#### Partial Updates
```html
<!-- Trigger partial update -->
<button hx-get="/api/v1/parks/{{ park.id }}/details/"
        hx-target="#park-details"
        hx-swap="innerHTML"
        hx-indicator="#loading-spinner"
        class="btn btn-primary">
    Load Details
</button>

<div id="park-details">
    <!-- Content loaded here -->
</div>

<div id="loading-spinner" class="htmx-indicator">
    {% include 'components/ui/skeleton.html' %}
</div>
```

#### Infinite Scroll
```html
<div id="park-list" 
     hx-get="/api/v1/parks/?page={{ next_page }}"
     hx-trigger="revealed"
     hx-swap="beforeend"
     hx-indicator="#infinite-loader">
    
    {% for park in parks %}
        {% include 'components/cards/park-card.html' %}
    {% endfor %}
</div>

<div id="infinite-loader" class="py-8 text-center htmx-indicator">
    <div class="inline-block w-8 h-8 border-2 rounded-full animate-spin border-primary border-t-transparent"></div>
</div>
```

#### Optimistic Updates
```html
<button hx-post="/api/v1/parks/{{ park.id }}/favorite/"
        hx-swap="outerHTML"
        hx-on::before-request="this.classList.add('is-favorited')"
        hx-on::response-error="this.classList.remove('is-favorited')"
        class="btn-icon favorite-btn">
    {% include 'components/icons/heart.html' %}
</button>
```

### 4.2 Error Handling

```javascript
// Global HTMX error handling
document.addEventListener('htmx:responseError', (event) => {
    const status = event.detail.xhr.status;
    
    if (status === 401) {
        Alpine.store('toast').error('Please log in to continue');
        // Optionally open login modal
    } else if (status === 403) {
        Alpine.store('toast').error('You do not have permission to perform this action');
    } else if (status === 422) {
        // Validation errors - handled by individual forms
    } else if (status >= 500) {
        Alpine.store('toast').error('Something went wrong. Please try again.');
    }
});

// Loading state management
document.addEventListener('htmx:beforeRequest', (event) => {
    event.target.classList.add('is-loading');
});

document.addEventListener('htmx:afterRequest', (event) => {
    event.target.classList.remove('is-loading');
});
```

### 4.3 HX-Trigger Events

```python
# Backend response with HX-Trigger
from django.http import HttpResponse

def favorite_park(request, pk):
    # ... logic
    response = HttpResponse(render_to_string('partials/favorite-button.html', {...}))
    response['HX-Trigger'] = json.dumps({
        'showToast': {'message': 'Park added to favorites', 'type': 'success'},
        'updateFavoritesCount': {'count': new_count}
    })
    return response
```

```javascript
// Listen for custom HX-Trigger events
document.addEventListener('showToast', (event) => {
    const { message, type } = event.detail;
    Alpine.store('toast').show(message, type);
});

document.addEventListener('updateFavoritesCount', (event) => {
    const { count } = event.detail;
    document.querySelector('#favorites-count').textContent = count;
});
```

---

## Phase 5: Performance & Optimization

### 5.1 Asset Optimization

- CSS minification and purging
- JavaScript bundling with proper chunking
- Image optimization with WebP/AVIF support
- Lazy loading for below-fold content
- Preloading critical assets

### 5.2 Caching Strategy

```html
<!-- Cache partial responses -->
<div hx-get="/api/v1/parks/featured/"
     hx-trigger="load"
     hx-swap="innerHTML"
     hx-headers='{"X-Cache-Control": "max-age=300"}'>
</div>
```

### 5.3 Performance Monitoring

- Core Web Vitals tracking
- HTMX request timing
- Alpine.js component performance
- Error tracking and reporting

---

## Phase 6: Testing Strategy

### 6.1 Unit Testing

- Alpine.js store functions
- Utility JavaScript functions
- Django template tags

### 6.2 Integration Testing

- HTMX endpoint responses
- Component rendering
- Form submissions
- Authentication flows

### 6.3 E2E Testing

- User journeys with Playwright
- Visual regression testing
- Accessibility testing (axe-core)
- Cross-browser testing

### 6.4 Accessibility Testing

- WCAG 2.1 AA compliance
- Screen reader compatibility
- Keyboard navigation
- Color contrast verification
- Focus management

---

## Implementation Phases & Milestones

### Phase 1: Foundation (Week 1-2)
- [ ] Design system CSS consolidation
- [ ] Tailwind CSS 4 configuration
- [ ] Template directory restructure
- [ ] Alpine.js store architecture
- [ ] Base component implementation

### Phase 2: Core Components (Week 3-4)
- [ ] Navigation components
- [ ] Card components (park, ride, etc.)
- [ ] Form components
- [ ] Modal/Dialog system
- [ ] Toast notifications

### Phase 3: Feature Pages (Week 5-6)
- [ ] Homepage redesign
- [ ] Park list/detail pages
- [ ] Ride list/detail pages
- [ ] Search results page
- [ ] User profile/settings

### Phase 4: Advanced Features (Week 7-8)
- [ ] Authentication system
- [ ] Advanced search/filters
- [ ] Data tables
- [ ] Image gallery
- [ ] Real-time features

### Phase 5: Polish & Testing (Week 9-10)
- [ ] Performance optimization
- [ ] Accessibility audit
- [ ] Cross-browser testing
- [ ] Documentation
- [ ] Bug fixes

---

## File Dependencies & Migration Order

```mermaid
graph TD
    A[Design Tokens CSS] --> B[Tailwind Config]
    B --> C[Base Template]
    C --> D[Layout Templates]
    D --> E[UI Components]
    E --> F[Page Templates]
    F --> G[Partials]
    
    H[Alpine.js Stores] --> I[Component Data Functions]
    I --> J[Page-specific JS]
    
    K[HTMX Config] --> L[Endpoint Integration]
    L --> M[Error Handling]
```

---

## Risk Mitigation

| Risk | Mitigation Strategy |
|------|---------------------|
| Breaking existing functionality | Implement feature flags, incremental rollout |
| Performance regression | Continuous performance monitoring, benchmarking |
| Accessibility issues | Automated testing with axe-core, manual audits |
| Browser compatibility | Cross-browser testing matrix, progressive enhancement |
| State management complexity | Clear documentation, consistent patterns |

---

## Success Metrics

- **Performance**: LCP < 2.5s, FID < 100ms, CLS < 0.1
- **Accessibility**: WCAG 2.1 AA compliance
- **Code Quality**: 80%+ test coverage, no critical linting errors
- **Developer Experience**: Component reuse > 70%, clear documentation
- **User Experience**: Consistent interactions, smooth animations

---

## Next Steps

1. Review and approve this plan
2. Set up development environment with new structure
3. Begin Phase 1 implementation
4. Schedule weekly progress reviews

---

*This plan is a living document and will be updated as implementation progresses.*
