{% include "parks/partials/park_header_badge.html" %}
{% endblock %}
{% block extra_js %}
{% endblock %}
```
## Component Usage
### Pagination
```django
{# Standard pagination #}
{% include 'components/pagination.html' with page_obj=page_obj %}
{# HTMX-enabled pagination #}
{% include 'components/pagination.html' with page_obj=page_obj use_htmx=True hx_target='#results' %}
{# Small size #}
{% include 'components/pagination.html' with page_obj=page_obj size='sm' %}
```
### Status Badge
```django
{# Basic badge #}
{% include 'components/status_badge.html' with status=park.status %}
{# Interactive badge with HTMX refresh #}
{% include 'components/status_badge.html' with
status=park.status
badge_id='park-header-badge'
refresh_trigger='park-status-changed'
scroll_target='park-status-section'
can_edit=perms.parks.change_park
%}
```
### Stats Card
```django
{# Basic stat #}
{% include 'components/stats_card.html' with label='Total Rides' value=park.ride_count %}
{# Clickable stat #}
{% include 'components/stats_card.html' with label='Total Rides' value=42 link=rides_url %}
{# Priority stat (highlighted) #}
{% include 'components/stats_card.html' with label='Operator' value=park.operator.name priority=True %}
```
### History Panel
```django
{# Basic history #}
{% include 'components/history_panel.html' with history=history %}
{# With FSM toggle for moderators #}
{% include 'components/history_panel.html' with
history=history
show_fsm_toggle=True
fsm_history_url=fsm_url
model_type='park'
object_id=park.id
can_view_fsm=perms.parks.change_park
%}
```
### Loading Indicator
```django
{# Block indicator #}
{% include 'htmx/components/loading_indicator.html' with id='loading' message='Loading...' %}
{# Inline indicator (in buttons) #}
{% include 'htmx/components/loading_indicator.html' with id='btn-loading' inline=True size='sm' %}
{# Overlay indicator #}
{% include 'htmx/components/loading_indicator.html' with id='overlay' mode='overlay' %}
```
## Form Rendering
### Form Layouts
```django
{# Stacked layout (default) #}
{% include 'forms/layouts/stacked.html' with form=form %}
{# Inline/horizontal layout #}
{% include 'forms/layouts/inline.html' with form=form %}
{# 2-column grid #}
{% include 'forms/layouts/grid.html' with form=form cols=2 %}
{# With excluded fields #}
{% include 'forms/layouts/stacked.html' with form=form exclude='password2' %}
{# Custom submit text #}
{% include 'forms/layouts/stacked.html' with form=form submit_text='Save Changes' %}
```
### Individual Fields
```django
{# Standard field #}
{% include 'forms/partials/form_field.html' with field=form.email %}
{# Field with custom label #}
{% include 'forms/partials/form_field.html' with field=form.email label='Email Address' %}
{# Field with HTMX validation #}
{% include 'forms/partials/form_field.html' with
field=form.username
hx_validate=True
hx_validate_url='/api/validate/username/'
%}
```
## Template Tags
### Loading Order
Always load template tags in this order:
```django
{% load static %}
{% load i18n %}
{% load park_tags %} {# App-specific #}
{% load safe_html %} {# Sanitization #}
{% load common_filters %} {# Utility filters #}
{% load cache %} {# Caching (if used) #}
```
### Available Filters
**common_filters:**
```django
{{ datetime|humanize_timedelta }} {# "2 hours ago" #}
{{ text|truncate_smart:50 }} {# Truncate at word boundary #}
{{ number|format_number }} {# "1,234,567" #}
{{ number|format_compact }} {# "1.2K", "3.4M" #}
{{ dict|get_item:"key" }} {# Safe dict access #}
{{ count|pluralize_custom:"item,items" }}
{{ field|add_class:"form-control" }}
```
**safe_html:**
```django
{{ content|sanitize }} {# Full HTML sanitization #}
{{ comment|sanitize_minimal }} {# Basic text only #}
{{ text|strip_html }} {# Remove all HTML #}
{{ data|json_safe }} {# Safe JSON for JS #}
{% icon "check" class="w-4 h-4" %} {# SVG icon #}
```
## Context Variables
### Naming Conventions
| Type | Convention | Example |
|------|------------|---------|
| Single object | Lowercase model name | `park`, `ride`, `user` |
| List/QuerySet | `{model}_list` | `park_list`, `ride_list` |
| Paginated | `page_obj` | Django standard |
| Single form | `form` | Standard |
| Multiple forms | `{purpose}_form` | `login_form`, `signup_form` |
### Avoid
- Generic names: `object`, `item`, `obj`, `data`
- Abbreviated names: `p`, `r`, `f`, `frm`
## HTMX Patterns
See `htmx/README.md` for detailed HTMX documentation.
### Quick Reference
```django
{# Swap strategies #}
hx-swap="innerHTML" {# Replace content inside #}
hx-swap="outerHTML" {# Replace entire element #}
hx-swap="beforeend" {# Append #}
hx-swap="afterbegin" {# Prepend #}
{# Target naming #}
hx-target="#park-123" {# Specific object #}
hx-target="#results" {# Page section #}
hx-target="this" {# Self #}
{# Event naming #}
hx-trigger="park-status-changed from:body"
hx-trigger="auth-changed from:body"
```
## Caching
Use fragment caching for expensive template sections:
```django
{% load cache %}
{# Cache navigation for 5 minutes per user #}
{% cache 300 nav user.id %}
{% include 'components/layout/enhanced_header.html' %}
{% endcache %}
{# Cache stats with object version #}
{% cache 600 park_stats park.id park.updated_at %}
{% include 'parks/partials/park_stats.html' %}
{% endcache %}
```
### Cache Keys
Include in cache key:
- User ID for personalized content
- Object ID for object-specific content
- `updated_at` for automatic invalidation
Do NOT cache:
- User-specific actions (edit buttons)
- Form CSRF tokens
- Real-time data
## Accessibility
### Checklist
- [ ] Single `
` per page
- [ ] Heading hierarchy (h1 → h2 → h3)
- [ ] Labels for all form inputs
- [ ] `aria-label` for icon-only buttons
- [ ] `aria-describedby` for help text
- [ ] `aria-invalid` for fields with errors
- [ ] `role="alert"` for error messages
- [ ] `aria-live` for dynamic content
### Skip Links
Base template includes skip link to main content:
```html
Skip to main content
```
### Landmarks
```html