# 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 {% load static %}
{{ park.name }}
{{ park.get_status_display }}

{{ park.name }}

{{ park.description|truncatewords:25 }}

{% include 'components/icons/location.html' with class='icon-xs' %} {{ park.location.city }}, {{ park.location.country }} {% include 'components/icons/ride.html' with class='icon-xs' %} {{ park.rides_count }} rides
{% if park.average_rating %}
{% include 'components/data-display/rating-stars.html' with rating=park.average_rating %} ({{ park.review_count }} reviews)
{% endif %}
``` #### Ride Card - Similar structure with ride-specific data - Category/type badges - Height/intensity indicators - Manufacturer attribution ### 2.3 Form Components #### Enhanced Input Component ```html {% load widget_tweaks %}
{% if label %} {% endif %}
{% if icon_left %}
{{ icon_left }}
{% 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 %}
{{ icon_right }}
{% endif %}
{% if field.help_text %}

{{ field.help_text }}

{% endif %} {% if field.errors %} {% endif %}
``` ### 2.4 Modal/Dialog System #### HTMX-Powered Modal ```html ``` ### 2.5 Toast/Notification System ```html
``` --- ## 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
``` #### 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
{% include 'components/ui/skeleton.html' %}
``` #### Infinite Scroll ```html
{% for park in parks %} {% include 'components/cards/park-card.html' %} {% endfor %}
``` #### Optimistic Updates ```html ``` ### 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
``` ### 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.*