# Developer Guidelines Best practices for UX development in ThrillWiki. ## General Principles ### 1. Progressive Enhancement All features should work without JavaScript, with HTMX and Alpine.js enhancing the experience: ```html
{% csrf_token %}
``` ### 2. Consistent Feedback Every user action should provide immediate feedback: - **Loading states**: Show progress during requests - **Success messages**: Confirm completed actions - **Error messages**: Explain what went wrong - **Validation**: Inline feedback on form fields ### 3. Accessibility First All components must be accessible: - Proper ARIA attributes - Keyboard navigation - Screen reader support - Focus management - Sufficient color contrast ### 4. Mobile-First Design Design for mobile first, then enhance for larger screens: ```html
Left
Right
``` ## File Organization ### Template Structure ``` templates/ ├── base/ │ └── base.html # Base template ├── components/ │ ├── layout/ # Layout components │ ├── modals/ # Modal components │ ├── navigation/ # Navigation components │ ├── skeletons/ # Skeleton screens │ └── ui/ # UI primitives ├── forms/ │ ├── layouts/ # Form layouts │ └── partials/ # Form field components ├── htmx/ │ └── components/ # HTMX-specific components └── [app_name]/ ├── list.html # Full page templates ├── list_partial.html # HTMX partials └── _card.html # Reusable fragments ``` ### Naming Conventions | Type | Convention | Example | |------|------------|---------| | Full page | `name.html` | `list.html` | | HTMX partial | `name_partial.html` | `list_partial.html` | | Fragment | `_name.html` | `_card.html` | | Component | `name.html` in components/ | `button.html` | ## Django Integration ### Context Processors Use context processors for global data: ```python # apps/core/context_processors.py def breadcrumbs(request): return {'breadcrumbs': getattr(request, 'breadcrumbs', [])} def page_meta(request): return {'page_meta': getattr(request, 'page_meta', {})} ``` ### View Patterns #### Standard View ```python def park_list(request): parks = Park.objects.all() return render(request, 'parks/list.html', {'parks': parks}) ``` #### HTMX-Aware View ```python from apps.core.htmx_utils import is_htmx_request def park_list(request): parks = Park.objects.all() template = 'parks/list_partial.html' if is_htmx_request(request) else 'parks/list.html' return render(request, template, {'parks': parks}) ``` #### Using Decorator ```python from apps.core.htmx_utils import htmx_partial @htmx_partial('parks/list.html') def park_list(request): parks = Park.objects.all() return ({'parks': parks},) ``` ### Breadcrumbs Set breadcrumbs in views: ```python from apps.core.utils import BreadcrumbBuilder def park_detail(request, slug): park = get_object_or_404(Park, slug=slug) request.breadcrumbs = ( BreadcrumbBuilder() .add_home() .add('Parks', reverse('parks:list')) .add_current(park.name) .build() ) return render(request, 'parks/detail.html', {'park': park}) ``` ### Page Meta Set meta information in views: ```python from apps.core.utils import build_meta_context def park_detail(request, slug): park = get_object_or_404(Park, slug=slug) request.page_meta = build_meta_context( title=park.name, description=park.description, instance=park, request=request, ) return render(request, 'parks/detail.html', {'park': park}) ``` ## HTMX Best Practices ### 1. Use Appropriate HTTP Methods ```html
...
...
``` ### 2. Target Specific Elements ```html ``` ### 3. Use Loading Indicators ```html
{% include 'htmx/components/loading_indicator.html' %}
``` ### 4. Handle Errors Gracefully ```python def create_park(request): form = ParkForm(request.POST) if not form.is_valid(): # Return form with errors, status 422 return render( request, 'parks/_form.html', {'form': form}, status=422 ) park = form.save() return htmx_success(f'{park.name} created!') ``` ### 5. Use Response Headers ```python from apps.core.htmx_utils import ( htmx_success, htmx_error, htmx_redirect, htmx_modal_close, ) # Success with toast return htmx_success('Park saved!') # Error with toast return htmx_error('Validation failed', status=422) # Redirect return htmx_redirect('/parks/') # Close modal and refresh return htmx_modal_close( message='Saved!', refresh_target='#parks-list' ) ``` ## Alpine.js Best Practices ### 1. Keep Components Small ```html
Content
``` ### 2. Use Stores for Global State ```javascript // stores/index.js Alpine.store('toast', { messages: [], success(message, duration = 5000) { /* ... */ }, error(message, duration = 0) { /* ... */ }, }); // Usage anywhere Alpine.store('toast').success('Saved!'); ``` ### 3. Initialize from Server Data ```html
``` ### 4. Use Magic Properties ```html
``` ## CSS/Tailwind Best Practices ### 1. Use Design Tokens ```css /* Use CSS variables */ .custom-element { color: hsl(var(--foreground)); background: hsl(var(--background)); border-color: hsl(var(--border)); } ``` ### 2. Use Component Classes ```html ``` ### 3. Responsive Design ```html
``` ### 4. Dark Mode Support ```html
Content adapts to theme
Explicit dark override
``` ## Testing Guidelines ### 1. Test User Flows ```python def test_create_park_flow(self): # Navigate to create page response = self.client.get('/parks/create/') self.assertEqual(response.status_code, 200) # Submit form response = self.client.post('/parks/create/', { 'name': 'Test Park', 'location': 'Test Location', }) # Verify redirect or success self.assertEqual(response.status_code, 302) self.assertTrue(Park.objects.filter(name='Test Park').exists()) ``` ### 2. Test HTMX Responses ```python def test_htmx_park_list(self): response = self.client.get('/parks/', HTTP_HX_REQUEST='true') # Should return partial template self.assertTemplateUsed(response, 'parks/list_partial.html') # Should not include base template chrome self.assertNotContains(response, ' ``` ### 2. Use Skeleton Screens ```html
{% include 'components/skeletons/card_grid_skeleton.html' %}
``` ### 3. Lazy Load Content ```html
``` ### 4. Debounce Input ```html ``` ## Security Guidelines ### 1. CSRF Protection ```html
{% csrf_token %}
``` ### 2. Validate on Server Always validate data server-side, even with client-side validation: ```python def create_park(request): form = ParkForm(request.POST) if not form.is_valid(): return htmx_error('Invalid data', status=422) # Process valid data ``` ### 3. Escape User Content ```django {{ user_input }} {{ trusted_html|safe }} ``` ### 4. Use Appropriate Permissions ```python from django.contrib.auth.decorators import login_required, permission_required @login_required @permission_required('parks.add_park') def create_park(request): # Only authenticated users with permission pass ```