mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 18:51:09 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
423
docs/ux/cta-guidelines.md
Normal file
423
docs/ux/cta-guidelines.md
Normal file
@@ -0,0 +1,423 @@
|
||||
# Call-to-Action (CTA) Guidelines
|
||||
|
||||
This document outlines the standardized patterns for buttons, links, and action elements in ThrillWiki.
|
||||
|
||||
## Button Hierarchy
|
||||
|
||||
### Primary Actions
|
||||
|
||||
Use for the main action on a page or in a section:
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary">Save Park</button>
|
||||
<a href="/create/" class="btn btn-primary">Create New</a>
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Form submissions
|
||||
- Main page actions
|
||||
- Completing a flow
|
||||
|
||||
### Secondary Actions
|
||||
|
||||
Use for important but non-primary actions:
|
||||
|
||||
```html
|
||||
<button class="btn btn-outline">Preview</button>
|
||||
<button class="btn btn-secondary">Save Draft</button>
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Alternative actions
|
||||
- Non-destructive secondary options
|
||||
- Cancel with importance
|
||||
|
||||
### Tertiary Actions
|
||||
|
||||
Use for low-emphasis actions:
|
||||
|
||||
```html
|
||||
<button class="btn btn-ghost">Cancel</button>
|
||||
<a href="/back/" class="btn btn-link">Go Back</a>
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Cancel buttons
|
||||
- Navigation links
|
||||
- Dismissive actions
|
||||
|
||||
### Destructive Actions
|
||||
|
||||
Use for actions that delete or remove data:
|
||||
|
||||
```html
|
||||
<button class="btn btn-destructive">Delete Park</button>
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Delete operations
|
||||
- Remove actions
|
||||
- Destructive confirmations
|
||||
|
||||
## Button Sizes
|
||||
|
||||
```html
|
||||
<!-- Small - for compact UIs -->
|
||||
<button class="btn btn-primary btn-sm">Small</button>
|
||||
|
||||
<!-- Default -->
|
||||
<button class="btn btn-primary">Default</button>
|
||||
|
||||
<!-- Large - for prominent CTAs -->
|
||||
<button class="btn btn-primary btn-lg">Large</button>
|
||||
```
|
||||
|
||||
## Button States
|
||||
|
||||
### Loading State
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary" disabled>
|
||||
<i class="fas fa-spinner fa-spin mr-2"></i>
|
||||
Saving...
|
||||
</button>
|
||||
```
|
||||
|
||||
### Disabled State
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary" disabled>
|
||||
Submit
|
||||
</button>
|
||||
```
|
||||
|
||||
### With Icon
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Add Park
|
||||
</button>
|
||||
|
||||
<button class="btn btn-outline">
|
||||
Download
|
||||
<i class="fas fa-download ml-2"></i>
|
||||
</button>
|
||||
```
|
||||
|
||||
## Action Bar Component
|
||||
|
||||
Use the action bar for consistent button placement:
|
||||
|
||||
```django
|
||||
{% include 'components/ui/action_bar.html' with
|
||||
primary_action_text='Save Changes'
|
||||
primary_action_icon='fas fa-save'
|
||||
secondary_action_text='Preview'
|
||||
tertiary_action_text='Cancel'
|
||||
tertiary_action_url='/parks/'
|
||||
%}
|
||||
```
|
||||
|
||||
### Alignment Options
|
||||
|
||||
```django
|
||||
{# Right-aligned (default) #}
|
||||
{% include 'components/ui/action_bar.html' with align='right' ... %}
|
||||
|
||||
{# Left-aligned #}
|
||||
{% include 'components/ui/action_bar.html' with align='left' ... %}
|
||||
|
||||
{# Space-between (cancel left, actions right) #}
|
||||
{% include 'components/ui/action_bar.html' with align='between' ... %}
|
||||
|
||||
{# Center-aligned #}
|
||||
{% include 'components/ui/action_bar.html' with align='center' ... %}
|
||||
```
|
||||
|
||||
## Placement Guidelines
|
||||
|
||||
### Page Headers
|
||||
|
||||
Primary CTA in page header, aligned right:
|
||||
|
||||
```django
|
||||
{% include 'components/layout/page_header.html' with
|
||||
title='Parks'
|
||||
primary_action_url='/parks/create/'
|
||||
primary_action_text='Add Park'
|
||||
primary_action_icon='fas fa-plus'
|
||||
%}
|
||||
```
|
||||
|
||||
### Card Actions
|
||||
|
||||
Actions in card footer, right-aligned:
|
||||
|
||||
```html
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<!-- Content -->
|
||||
</div>
|
||||
<div class="card-footer flex justify-end gap-2">
|
||||
<button class="btn btn-ghost btn-sm">Cancel</button>
|
||||
<button class="btn btn-primary btn-sm">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Form Actions
|
||||
|
||||
Form buttons at bottom, with cancel on left or right based on flow:
|
||||
|
||||
```html
|
||||
<!-- Standard form (cancel left, submit right) -->
|
||||
<div class="flex justify-between mt-6">
|
||||
<a href="/cancel/" class="btn btn-ghost">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</div>
|
||||
|
||||
<!-- Simple form (all right-aligned) -->
|
||||
<div class="flex justify-end gap-3 mt-6">
|
||||
<button type="button" class="btn btn-ghost">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Modal Actions
|
||||
|
||||
Actions in modal footer, right-aligned:
|
||||
|
||||
```html
|
||||
<div class="modal-footer">
|
||||
<button @click="showModal = false" class="btn btn-ghost">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary">
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Inline Actions
|
||||
|
||||
For table rows or list items:
|
||||
|
||||
```html
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="btn btn-ghost btn-sm">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm text-destructive">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Link vs Button
|
||||
|
||||
### Use Links For:
|
||||
|
||||
- Navigation to other pages
|
||||
- Opening in new tabs
|
||||
- Bookmarkable destinations
|
||||
|
||||
```html
|
||||
<a href="/parks/123/" class="btn btn-primary">View Park</a>
|
||||
```
|
||||
|
||||
### Use Buttons For:
|
||||
|
||||
- Form submissions
|
||||
- JavaScript actions
|
||||
- State changes
|
||||
- Toggles
|
||||
|
||||
```html
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button @click="showModal = true" class="btn btn-outline">Open Modal</button>
|
||||
```
|
||||
|
||||
## Icon Guidelines
|
||||
|
||||
### Icon Position
|
||||
|
||||
- **Left icon**: For actions (Add, Create, Save)
|
||||
- **Right icon**: For navigation (Next, Go, External link)
|
||||
|
||||
```html
|
||||
<!-- Action with left icon -->
|
||||
<button class="btn btn-primary">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Add Park
|
||||
</button>
|
||||
|
||||
<!-- Navigation with right icon -->
|
||||
<a href="/next/" class="btn btn-outline">
|
||||
Next Step
|
||||
<i class="fas fa-arrow-right ml-2"></i>
|
||||
</a>
|
||||
```
|
||||
|
||||
### Common Icon Mappings
|
||||
|
||||
| Action | Icon |
|
||||
|--------|------|
|
||||
| Create/Add | `fa-plus` |
|
||||
| Edit | `fa-edit` or `fa-pen` |
|
||||
| Delete | `fa-trash` |
|
||||
| Save | `fa-save` |
|
||||
| Cancel | `fa-times` |
|
||||
| Search | `fa-search` |
|
||||
| Filter | `fa-filter` |
|
||||
| Download | `fa-download` |
|
||||
| Upload | `fa-upload` |
|
||||
| Settings | `fa-cog` |
|
||||
| Back | `fa-arrow-left` |
|
||||
| Next | `fa-arrow-right` |
|
||||
| External Link | `fa-external-link-alt` |
|
||||
|
||||
## Confirmation Patterns
|
||||
|
||||
### Inline Confirmation (Low Risk)
|
||||
|
||||
```html
|
||||
<button hx-delete="/items/123/"
|
||||
hx-confirm="Delete this item?">
|
||||
Delete
|
||||
</button>
|
||||
```
|
||||
|
||||
### Modal Confirmation (High Risk)
|
||||
|
||||
```django
|
||||
<button @click="showDeleteModal = true" class="btn btn-destructive">
|
||||
Delete Park
|
||||
</button>
|
||||
|
||||
{% include 'components/modals/modal_confirm.html' with
|
||||
modal_id='delete-confirm'
|
||||
show_var='showDeleteModal'
|
||||
title='Delete Park'
|
||||
message='This will permanently delete the park and all associated data. This action cannot be undone.'
|
||||
confirm_text='Delete'
|
||||
confirm_variant='destructive'
|
||||
confirm_hx_delete='/parks/123/'
|
||||
%}
|
||||
```
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
### Mobile Stacking
|
||||
|
||||
Buttons stack vertically on mobile:
|
||||
|
||||
```html
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<button class="btn btn-ghost order-2 sm:order-1">Cancel</button>
|
||||
<button class="btn btn-primary order-1 sm:order-2">Save</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Icon-Only on Mobile
|
||||
|
||||
```html
|
||||
<button class="btn btn-outline">
|
||||
<i class="fas fa-filter"></i>
|
||||
<span class="hidden sm:inline ml-2">Filter</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
### Full-Width on Mobile
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary w-full sm:w-auto">
|
||||
Save Changes
|
||||
</button>
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Button Labels
|
||||
|
||||
Always provide accessible labels:
|
||||
|
||||
```html
|
||||
<!-- Icon-only buttons need aria-label -->
|
||||
<button class="btn btn-ghost" aria-label="Edit park">
|
||||
<i class="fas fa-edit" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<!-- Or use visually hidden text -->
|
||||
<button class="btn btn-ghost">
|
||||
<i class="fas fa-edit" aria-hidden="true"></i>
|
||||
<span class="sr-only">Edit park</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
### Focus States
|
||||
|
||||
All buttons must have visible focus states:
|
||||
|
||||
```css
|
||||
.btn:focus {
|
||||
outline: none;
|
||||
ring: 2px;
|
||||
ring-offset: 2px;
|
||||
ring-color: var(--ring);
|
||||
}
|
||||
```
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
Ensure buttons are keyboard accessible:
|
||||
|
||||
```html
|
||||
<!-- Use native button elements -->
|
||||
<button class="btn btn-primary">Click Me</button>
|
||||
|
||||
<!-- If using div/span, add role and tabindex -->
|
||||
<div role="button" tabindex="0" class="btn btn-primary"
|
||||
@keydown.enter="handleClick"
|
||||
@keydown.space.prevent="handleClick">
|
||||
Click Me
|
||||
</div>
|
||||
```
|
||||
|
||||
## HTMX Integration
|
||||
|
||||
### Loading Indicators
|
||||
|
||||
Show loading state during requests:
|
||||
|
||||
```html
|
||||
<button hx-post="/save/"
|
||||
hx-indicator="this"
|
||||
class="btn btn-primary">
|
||||
<span class="htmx-indicator hidden">
|
||||
<i class="fas fa-spinner fa-spin mr-2"></i>
|
||||
</span>
|
||||
Save
|
||||
</button>
|
||||
```
|
||||
|
||||
### Disable During Request
|
||||
|
||||
```html
|
||||
<button hx-post="/save/"
|
||||
hx-disabled-elt="this"
|
||||
class="btn btn-primary">
|
||||
Save
|
||||
</button>
|
||||
```
|
||||
|
||||
### Success Feedback
|
||||
|
||||
```python
|
||||
# In view
|
||||
from apps.core.htmx_utils import htmx_success
|
||||
|
||||
def save_item(request):
|
||||
# ... save logic ...
|
||||
return htmx_success('Item saved successfully!')
|
||||
```
|
||||
Reference in New Issue
Block a user