\ No newline at end of file
diff --git a/backend/templates/cotton/input.html b/backend/templates/cotton/input.html
new file mode 100644
index 00000000..f9e74e57
--- /dev/null
+++ b/backend/templates/cotton/input.html
@@ -0,0 +1,47 @@
+{% comment %}
+Input Component - Django Cotton Version of shadcn/ui Input
+Usage:
+Preserves EXACT same HTML output as the original include version
+{% endcomment %}
+
+
+
+
\ No newline at end of file
diff --git a/backend/templates/test_button_comparison.html b/backend/templates/test_button_comparison.html
new file mode 100644
index 00000000..fb686516
--- /dev/null
+++ b/backend/templates/test_button_comparison.html
@@ -0,0 +1,654 @@
+{% load static %}
+
+
+
+
+
+ UI Component Comparison Test
+
+
+
+
+
+
UI Component Comparison Test
+
Comparing old include method vs new cotton component method for Button, Input, and Card components
+
+
+
+
Test 1: All Variants (Default Size)
+
+
Default Variant
+
+
+ {% include 'components/ui/button.html' with variant='default' text='Default Button' %}
+
+
+ Default Button
+
+
+
+
Destructive Variant
+
+
+ {% include 'components/ui/button.html' with variant='destructive' text='Delete Item' %}
+
+
+ Delete Item
+
+
+
+
Outline Variant
+
+
+ {% include 'components/ui/button.html' with variant='outline' text='Outline Button' %}
+
+
+ Outline Button
+
+
+
+
Secondary Variant
+
+
+ {% include 'components/ui/button.html' with variant='secondary' text='Secondary Button' %}
+
+
+ Secondary Button
+
+
+
+
Ghost Variant
+
+
+ {% include 'components/ui/button.html' with variant='ghost' text='Ghost Button' %}
+
+
+ Ghost Button
+
+
+
+
Link Variant
+
+
+ {% include 'components/ui/button.html' with variant='link' text='Link Button' %}
+
+
+ Link Button
+
+
+
+
+
+
+
Test 2: All Sizes (Default Variant)
+
+
Default Size
+
+
+ {% include 'components/ui/button.html' with size='default' text='Default Size' %}
+
+
+ Default Size
+
+
+
+
Small Size
+
+
+ {% include 'components/ui/button.html' with size='sm' text='Small Size' %}
+
+
+ Small Size
+
+
+
+
Large Size
+
+
+ {% include 'components/ui/button.html' with size='lg' text='Large Size' %}
+
+
+ Large Size
+
+
+
+
Icon Size
+
+
+ {% include 'components/ui/button.html' with size='icon' text='🔥' %}
+
+
+ 🔥
+
+
+
+
+
+
+
Test 3: HTMX Attributes
+
+
hx-get Attribute
+
+
+ {% include 'components/ui/button.html' with text='Load Content' hx_get='/api/content/' hx_target='#content' %}
+
+
+ Load Content
+
+
+
+
hx-post Attribute
+
+
+ {% include 'components/ui/button.html' with text='Submit Form' hx_post='/api/submit/' hx_target='#result' hx_swap='innerHTML' %}
+
+
+ Submit Form
+
+
+
+
+
+
+
Test 4: Icons
+
+
Left Icon
+
+
+ {% include 'components/ui/button.html' with text='Save' icon_left='fas fa-save' %}
+
+
+ Save
+
+
+
+
Right Icon
+
+
+ {% include 'components/ui/button.html' with text='Next' icon_right='fas fa-arrow-right' %}
+
+
+ Next
+
+
+
+
+
+
+
Test 5: Additional Classes and Attributes
+
+
Custom Classes
+
+
+ {% include 'components/ui/button.html' with text='Custom Button' class='border-2 border-red-500 shadow-lg' %}
+
+
+ Custom Button
+
+
+
+
Type Attribute
+
+
+ {% include 'components/ui/button.html' with text='Submit' type='submit' %}
+
+
+ Submit
+
+
+
+
Disabled State
+
+
+ {% include 'components/ui/button.html' with text='Disabled' disabled=True %}
+
+
+ Disabled
+
+
+
+
+
+
+
Test 6: Complex Combinations
+
+
Destructive + Large + Icon + HTMX
+
+
+ {% include 'components/ui/button.html' with variant='destructive' size='lg' text='Delete All' icon_left='fas fa-trash' hx_post='/api/delete-all/' hx_target='#main' class='font-bold' %}
+
+
+ Delete All
+
+
+
+
Outline + Small + Right Icon + Custom Attributes
+
+
+ {% include 'components/ui/button.html' with variant='outline' size='sm' text='Export' icon_right='fas fa-download' attrs='data-testid="export-btn" title="Export data"' %}
+
+
+ Export
+
+
+
+
+
+
+
Test 7: Legacy Underscore Props vs Modern Hyphenated Attributes
+
This test verifies backward compatibility - cotton components should accept both legacy underscore props (hx_get, x_data) and modern hyphenated attributes (hx-get, x-data).
+
+
Legacy hx_get vs Modern hx-get
+
+
+ {% include 'components/ui/button.html' with text='Load Content' hx_get='/api/content/' hx_target='#content' %}
+
+
+ Load Content
+
+
+ Load Content
+
+
+
+
Legacy hx_post vs Modern hx-post
+
+
+ {% include 'components/ui/button.html' with text='Submit Form' hx_post='/api/submit/' hx_target='#result' hx_swap='innerHTML' %}
+
+
+ Submit Form
+
+
+ Submit Form
+
+
+
+
Legacy x_data vs Modern x-data
+
+
+ {% include 'components/ui/button.html' with text='Alpine Button' x_data='{ clicked: false }' %}
+
+
+ Alpine Button
+
+
+ Alpine Button
+
+
+
+
Legacy onclick vs Modern onclick
+
+
+ {% include 'components/ui/button.html' with text='Click Handler' onclick="alert('Clicked!')" %}
+
+
+ Click Handler
+
+
+
+
Legacy type vs Modern type
+
+
+ {% include 'components/ui/button.html' with text='Submit Form' type='submit' %}
+
+
+ Submit Form
+
+
+
+
Legacy disabled vs Modern disabled
+
+
+ {% include 'components/ui/button.html' with text='Disabled Button' disabled=True %}
+
+
+ Disabled Button
+
+
+
+
Complex Legacy Props Combination
+
+
+ {% include 'components/ui/button.html' with variant='destructive' size='lg' text='Legacy Complex' hx_post='/api/complex/' hx_target='#result' x_data='{ processing: false }' type='submit' %}
+
+
+ Legacy Complex
+
+
+ Modern Complex
+
+
+
+
Alpine.js x_on Special Case
+
+
+ {% include 'components/ui/button.html' with text='Alpine Click' x_on='@click="clicked = true"' %}
+
+
+ Alpine Click
+
+
+ Alpine Click
+
+
+
+
+
+
+
Test 8: Input Component Comparison
+
+
Basic Text Input
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Enter your name' name='name' %}
+
+
+
+
+
+
+
Email Input with Value
+
+
+ {% include 'components/ui/input.html' with type='email' placeholder='email@example.com' name='email' value='test@example.com' %}
+
+
+
+
+
+
+
Password Input
+
+
+ {% include 'components/ui/input.html' with type='password' placeholder='Password' name='password' required=True %}
+
+
+
+
+
+
+
Number Input with Min/Max
+
+
+ {% include 'components/ui/input.html' with type='number' placeholder='Age' name='age' attrs='min="18" max="100"' %}
+
+
+
+
+
+
+
Disabled Input
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Disabled input' name='disabled' disabled=True value='Cannot edit' %}
+
+
+
+
+
+
+
Readonly Input
+
+
+ {% include 'components/ui/input.html' with type='text' name='readonly' readonly=True value='Read only value' %}
+
+
+
+
+
+
+
Input with Custom Class
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Custom styled' name='custom' class='border-blue-500 bg-blue-50' %}
+
+
+
+
+
+
+
Input with HTMX Attributes
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Search...' name='search' hx_get='/api/search/' hx_target='#results' hx_trigger='keyup changed delay:300ms' %}
+
+
+
+
+
+
+
Input with Alpine.js
+
+
+ {% include 'components/ui/input.html' with type='text' placeholder='Alpine input' name='alpine' x_model='inputValue' %}
+
+
+
+
+
+
+
+
+
+
Test 8: Card Component Comparison
+
+
Basic Card with Title
+
+
+ {% include 'components/ui/card.html' with title='Basic Card' content='This is a simple card with just a title and content.' %}
+
+
+ This is a simple card with just a title and content.
+
+
+
+
Card with Title and Description
+
+
+ {% include 'components/ui/card.html' with title='Card with Description' description='This card has both a title and a description' content='Here is the main content of the card.' %}
+
+
+ Here is the main content of the card.
+
+
+
+
Card with Custom Header Content
+
+
+ {% include 'components/ui/card.html' with header_content='
Custom Header
New
' content='This card has custom header content instead of a simple title.' %}
+
+
+ This card has custom header content instead of a simple title.
+
+
+
+
Card with Body Content (instead of slot)
+
+
+ {% include 'components/ui/card.html' with title='Body Content Card' body_content='
This content is passed via body_content parameter.
' %}
+
+
+
+
+
+
+
Card with Footer
+
+
+ {% include 'components/ui/card.html' with title='Card with Footer' content='This card has footer content at the bottom.' footer_content='' %}
+
+
+ This card has footer content at the bottom.
+
+
+
+
Complete Card (All Sections)
+
+
+ {% include 'components/ui/card.html' with title='Complete Card' description='This card has all possible sections' content='Main content goes here. This is the primary content area.' footer_content='Last updated: Today' %}
+
+
+ Main content goes here. This is the primary content area.
+
+
+
+
Card with Custom Classes
+
+
+ {% include 'components/ui/card.html' with title='Custom Styled Card' content='This card has custom styling applied.' class='border-2 border-green-200 bg-green-50 shadow-lg' %}
+
+
+ This card has custom styling applied.
+
+
+
+
Content-Only Card (No Header)
+
+
+ {% include 'components/ui/card.html' with content='This card has only content, no header or footer sections.' %}
+
+
+ This card has only content, no header or footer sections.
+
+
+
+
+
+
+
HTML Output Inspection
+
Use browser dev tools to inspect the HTML output and ensure it's identical.
+
+
+
+
Include Version HTML:
+
+
+
+
+
+
Cotton Version HTML:
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/thrillwiki/urls.py b/backend/thrillwiki/urls.py
index 2a2946e7..b41e6b4b 100644
--- a/backend/thrillwiki/urls.py
+++ b/backend/thrillwiki/urls.py
@@ -101,6 +101,8 @@ urlpatterns = [
views.environment_and_settings_view,
name="environment_and_settings",
),
+ # Button component testing
+ path("test-button/", views.test_button_comparison, name="test_button_comparison"),
]
# Add autocomplete URLs if available
diff --git a/backend/thrillwiki/views.py b/backend/thrillwiki/views.py
index 2ca6119e..c8752c13 100644
--- a/backend/thrillwiki/views.py
+++ b/backend/thrillwiki/views.py
@@ -156,3 +156,11 @@ def environment_and_settings_view(request):
"environment_and_settings.html",
{"env_vars": env_vars, "settings_vars": settings_vars},
)
+
+
+def test_button_comparison(request):
+ """
+ Test view to compare cotton button component with original include version.
+ Renders a comprehensive test page with all button variants and combinations.
+ """
+ return render(request, "test_button_comparison.html")
diff --git a/backend/uv.lock b/backend/uv.lock
index 60fa43b9..2c29131e 100644
--- a/backend/uv.lock
+++ b/backend/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.13"
[[package]]
@@ -642,6 +642,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/30/d8/19ed1e47badf477d17fb177c1c19b5a21da0fd2d9f093f23be3fb86c5fab/django_cors_headers-4.9.0-py3-none-any.whl", hash = "sha256:15c7f20727f90044dcee2216a9fd7303741a864865f0c3657e28b7056f61b449", size = 12809, upload-time = "2025-09-18T10:40:50.843Z" },
]
+[[package]]
+name = "django-cotton"
+version = "2.1.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "django" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c3/99/36e318ebd1ace3fc874541a02e7e4149def8e21aab85aceb7bb01e17607b/django_cotton-2.1.3.tar.gz", hash = "sha256:737f9c088549d7febbf78532856ddf1270799675a4bc9fa191a5db0e195a9c13", size = 23432, upload-time = "2025-06-30T17:31:29.29Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ad/ec/5e5318af0304962be43e3b912aef024d8ac08c0f9a9dfcc4f0cd55d0e74e/django_cotton-2.1.3-py3-none-any.whl", hash = "sha256:f33658d05a8f5ecf7448bdf1089e2ad27d2ce42e59c752216129701d7d153c89", size = 22214, upload-time = "2025-06-30T17:31:28.093Z" },
+]
+
[[package]]
name = "django-debug-toolbar"
version = "6.0.0"
@@ -2238,6 +2250,7 @@ dependencies = [
{ name = "django-cleanup" },
{ name = "django-cloudflareimages-toolkit" },
{ name = "django-cors-headers" },
+ { name = "django-cotton" },
{ name = "django-debug-toolbar" },
{ name = "django-environ" },
{ name = "django-extensions" },
@@ -2309,6 +2322,7 @@ requires-dist = [
{ name = "django-cleanup", specifier = ">=8.0.0" },
{ name = "django-cloudflareimages-toolkit", specifier = ">=1.0.6" },
{ name = "django-cors-headers", specifier = ">=4.3.1" },
+ { name = "django-cotton", specifier = ">=2.1.3" },
{ name = "django-debug-toolbar", specifier = ">=4.0.0" },
{ name = "django-environ", specifier = ">=0.12.0" },
{ name = "django-extensions", specifier = ">=4.1" },