mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 08:51:09 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
@@ -1,63 +1,155 @@
|
||||
{% comment %}
|
||||
Button Component - Django Template Version of shadcn/ui Button
|
||||
Usage: {% include 'components/ui/button.html' with variant='default' size='default' text='Click me' %}
|
||||
Button Component - Unified Django Template Version of shadcn/ui Button
|
||||
|
||||
A versatile button component that supports multiple variants, sizes, icons, and both
|
||||
button/link elements. Compatible with HTMX and Alpine.js.
|
||||
|
||||
Usage Examples:
|
||||
Basic button:
|
||||
{% include 'components/ui/button.html' with text='Click me' %}
|
||||
|
||||
With variant and size:
|
||||
{% include 'components/ui/button.html' with text='Submit' variant='default' size='lg' %}
|
||||
|
||||
Link button:
|
||||
{% include 'components/ui/button.html' with href='/path' text='Go' type='link' %}
|
||||
|
||||
With HTMX:
|
||||
{% include 'components/ui/button.html' with text='Load' hx_get='/api/data' hx_target='#target' %}
|
||||
|
||||
With Alpine.js:
|
||||
{% include 'components/ui/button.html' with text='Toggle' x_on_click='open = !open' %}
|
||||
|
||||
With SVG icon (preferred):
|
||||
{% include 'components/ui/button.html' with icon=search_icon_svg text='Search' %}
|
||||
|
||||
Icon-only button:
|
||||
{% include 'components/ui/button.html' with icon=icon_svg size='icon' aria_label='Close' %}
|
||||
|
||||
Parameters:
|
||||
- variant: 'default', 'destructive', 'outline', 'secondary', 'ghost', 'link' (default: 'default')
|
||||
- size: 'default', 'sm', 'lg', 'icon' (default: 'default')
|
||||
- type: 'button', 'submit', 'reset', 'link' (default: 'button')
|
||||
- text: Button text content
|
||||
- label: Alias for text (for backwards compatibility)
|
||||
- content: Alias for text (for backwards compatibility)
|
||||
- href: URL for link buttons (required when type='link')
|
||||
- icon: SVG icon content (will be sanitized)
|
||||
- icon_left: Font Awesome class for left icon (deprecated, prefer icon)
|
||||
- icon_right: Font Awesome class for right icon (deprecated)
|
||||
- disabled: Boolean to disable the button
|
||||
- class: Additional CSS classes
|
||||
- id: Element ID
|
||||
- aria_label: Accessibility label (required for icon-only buttons)
|
||||
- onclick: JavaScript click handler
|
||||
- hx_get, hx_post, hx_target, hx_swap, hx_trigger, hx_indicator, hx_include: HTMX attributes
|
||||
- x_data, x_on_click, x_bind, x_show: Alpine.js attributes
|
||||
- attrs: Additional HTML attributes as string
|
||||
|
||||
Security: Icon SVGs are sanitized using the sanitize_svg filter to prevent XSS attacks.
|
||||
{% endcomment %}
|
||||
|
||||
{% load static %}
|
||||
{% load static safe_html %}
|
||||
|
||||
{% with variant=variant|default:'default' size=size|default:'default' %}
|
||||
<button
|
||||
class="
|
||||
inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium
|
||||
ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2
|
||||
focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50
|
||||
{% if variant == 'default' %}
|
||||
bg-primary text-primary-foreground hover:bg-primary/90
|
||||
{% elif variant == 'destructive' %}
|
||||
bg-destructive text-destructive-foreground hover:bg-destructive/90
|
||||
{% elif variant == 'outline' %}
|
||||
border border-input bg-background hover:bg-accent hover:text-accent-foreground
|
||||
{% elif variant == 'secondary' %}
|
||||
bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
{% elif variant == 'ghost' %}
|
||||
hover:bg-accent hover:text-accent-foreground
|
||||
{% elif variant == 'link' %}
|
||||
text-primary underline-offset-4 hover:underline
|
||||
{% with variant=variant|default:'default' size=size|default:'default' btn_type=type|default:'button' btn_text=text|default:label|default:content %}
|
||||
|
||||
{% if btn_type == 'link' or href %}
|
||||
{# Link element styled as button #}
|
||||
<a
|
||||
href="{{ href|default:'#' }}"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50
|
||||
{% if variant == 'destructive' %}bg-destructive text-destructive-foreground hover:bg-destructive/90
|
||||
{% elif variant == 'outline' %}border border-input bg-background hover:bg-accent hover:text-accent-foreground
|
||||
{% elif variant == 'secondary' %}bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
{% elif variant == 'ghost' %}hover:bg-accent hover:text-accent-foreground
|
||||
{% elif variant == 'link' %}text-primary underline-offset-4 hover:underline
|
||||
{% else %}bg-primary text-primary-foreground hover:bg-primary/90{% endif %}
|
||||
{% if size == 'sm' %}h-9 rounded-md px-3
|
||||
{% elif size == 'lg' %}h-11 rounded-md px-8
|
||||
{% elif size == 'icon' %}h-10 w-10
|
||||
{% else %}h-10 px-4 py-2{% endif %}
|
||||
{{ class|default:'' }}"
|
||||
{% if disabled %}aria-disabled="true" tabindex="-1"{% endif %}
|
||||
{% if aria_label %}aria-label="{{ aria_label }}"{% endif %}
|
||||
{% if x_data %}x-data="{{ x_data }}"{% endif %}
|
||||
{% if x_on_click %}@click="{{ x_on_click }}"{% endif %}
|
||||
{% if x_bind %}x-bind="{{ x_bind }}"{% endif %}
|
||||
{% if x_show %}x-show="{{ x_show }}"{% endif %}
|
||||
{% if hx_get %}hx-get="{{ hx_get }}"{% endif %}
|
||||
{% if hx_post %}hx-post="{{ hx_post }}"{% endif %}
|
||||
{% if hx_target %}hx-target="{{ hx_target }}"{% endif %}
|
||||
{% if hx_swap %}hx-swap="{{ hx_swap }}"{% endif %}
|
||||
{% if hx_trigger %}hx-trigger="{{ hx_trigger }}"{% endif %}
|
||||
{% if hx_indicator %}hx-indicator="{{ hx_indicator }}"{% endif %}
|
||||
{{ attrs|default:'' }}>
|
||||
|
||||
{% if icon %}
|
||||
<span class="w-4 h-4 flex items-center justify-center">{{ icon|sanitize_svg }}</span>
|
||||
{% if btn_text %}<span>{{ btn_text }}</span>{% endif %}
|
||||
{% elif icon_left %}
|
||||
<i class="{{ icon_left }} w-4 h-4" aria-hidden="true"></i>
|
||||
{% if btn_text %}{{ btn_text }}{% endif %}
|
||||
{% else %}
|
||||
{{ btn_text }}
|
||||
{% endif %}
|
||||
{% if size == 'default' %}
|
||||
h-10 px-4 py-2
|
||||
{% elif size == 'sm' %}
|
||||
h-9 rounded-md px-3
|
||||
{% elif size == 'lg' %}
|
||||
h-11 rounded-md px-8
|
||||
{% elif size == 'icon' %}
|
||||
h-10 w-10
|
||||
|
||||
{% if icon_right %}
|
||||
<i class="{{ icon_right }} w-4 h-4" aria-hidden="true"></i>
|
||||
{% endif %}
|
||||
{{ class|default:'' }}
|
||||
"
|
||||
{% if type %}type="{{ type }}"{% endif %}
|
||||
{% if onclick %}onclick="{{ onclick }}"{% endif %}
|
||||
{% if hx_get %}hx-get="{{ hx_get }}"{% endif %}
|
||||
{% if hx_post %}hx-post="{{ hx_post }}"{% endif %}
|
||||
{% if hx_target %}hx-target="{{ hx_target }}"{% endif %}
|
||||
{% if hx_swap %}hx-swap="{{ hx_swap }}"{% endif %}
|
||||
{% if x_data %}x-data="{{ x_data }}"{% endif %}
|
||||
{% if x_on %}{{ x_on }}{% endif %}
|
||||
{% if disabled %}disabled{% endif %}
|
||||
{{ attrs|default:'' }}
|
||||
>
|
||||
{% if icon_left %}
|
||||
<i class="{{ icon_left }} w-4 h-4"></i>
|
||||
{% endif %}
|
||||
|
||||
{% if text %}
|
||||
{{ text }}
|
||||
{% else %}
|
||||
{{ content|default:'' }}
|
||||
{% endif %}
|
||||
|
||||
{% if icon_right %}
|
||||
<i class="{{ icon_right }} w-4 h-4"></i>
|
||||
{% endif %}
|
||||
|
||||
{% block link_content %}{% endblock %}
|
||||
</a>
|
||||
|
||||
{% else %}
|
||||
{# Button element #}
|
||||
<button
|
||||
type="{{ btn_type }}"
|
||||
{% if id %}id="{{ id }}"{% endif %}
|
||||
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50
|
||||
{% if variant == 'destructive' %}bg-destructive text-destructive-foreground hover:bg-destructive/90
|
||||
{% elif variant == 'outline' %}border border-input bg-background hover:bg-accent hover:text-accent-foreground
|
||||
{% elif variant == 'secondary' %}bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
{% elif variant == 'ghost' %}hover:bg-accent hover:text-accent-foreground
|
||||
{% elif variant == 'link' %}text-primary underline-offset-4 hover:underline
|
||||
{% else %}bg-primary text-primary-foreground hover:bg-primary/90{% endif %}
|
||||
{% if size == 'sm' %}h-9 rounded-md px-3
|
||||
{% elif size == 'lg' %}h-11 rounded-md px-8
|
||||
{% elif size == 'icon' %}h-10 w-10
|
||||
{% else %}h-10 px-4 py-2{% endif %}
|
||||
{{ class|default:'' }}"
|
||||
{% if disabled %}disabled{% endif %}
|
||||
{% if aria_label %}aria-label="{{ aria_label }}"{% endif %}
|
||||
{% if onclick %}onclick="{{ onclick }}"{% endif %}
|
||||
{% if x_data %}x-data="{{ x_data }}"{% endif %}
|
||||
{% if x_on_click %}@click="{{ x_on_click }}"{% endif %}
|
||||
{% if x_bind %}x-bind="{{ x_bind }}"{% endif %}
|
||||
{% if x_show %}x-show="{{ x_show }}"{% endif %}
|
||||
{% if hx_get %}hx-get="{{ hx_get }}"{% endif %}
|
||||
{% if hx_post %}hx-post="{{ hx_post }}"{% endif %}
|
||||
{% if hx_target %}hx-target="{{ hx_target }}"{% endif %}
|
||||
{% if hx_swap %}hx-swap="{{ hx_swap }}"{% endif %}
|
||||
{% if hx_trigger %}hx-trigger="{{ hx_trigger }}"{% endif %}
|
||||
{% if hx_indicator %}hx-indicator="{{ hx_indicator }}"{% endif %}
|
||||
{% if hx_include %}hx-include="{{ hx_include }}"{% endif %}
|
||||
{{ attrs|default:'' }}>
|
||||
|
||||
{% if icon %}
|
||||
<span class="w-4 h-4 flex items-center justify-center">{{ icon|sanitize_svg }}</span>
|
||||
{% if btn_text %}<span>{{ btn_text }}</span>{% endif %}
|
||||
{% elif icon_left %}
|
||||
<i class="{{ icon_left }} w-4 h-4" aria-hidden="true"></i>
|
||||
{% if btn_text %}{{ btn_text }}{% endif %}
|
||||
{% else %}
|
||||
{{ btn_text }}
|
||||
{% endif %}
|
||||
|
||||
{% if icon_right %}
|
||||
<i class="{{ icon_right }} w-4 h-4" aria-hidden="true"></i>
|
||||
{% endif %}
|
||||
|
||||
{% block button_content %}{% endblock %}
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
{% endwith %}
|
||||
|
||||
Reference in New Issue
Block a user