# ThrillWiki Design System - Component Library ## Overview Comprehensive component library for ThrillWiki's frontend redesign, providing reusable UI patterns that implement the design token system consistently across all templates. ## Component Categories ### 1. Layout Components #### Container ```html
``` ```css .container { width: 100%; margin-left: auto; margin-right: auto; padding-left: var(--spacing-container-sm); padding-right: var(--spacing-container-sm); } @media (min-width: 640px) { .container { padding-left: var(--spacing-container-md); padding-right: var(--spacing-container-md); } } @media (min-width: 1024px) { .container { padding-left: var(--spacing-container-lg); padding-right: var(--spacing-container-lg); } } .container-constrained { max-width: 1280px; } .container-fluid { width: 100%; padding-left: var(--spacing-container-xs); padding-right: var(--spacing-container-xs); } ``` #### Grid System ```html
Item 1
Item 2
Item 3
Item 1
Item 2
Item 3
``` ```css .grid-auto-fit { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--spacing-layout-md); } .grid-item { min-width: 0; /* Prevent grid blowout */ } ``` ### 2. Navigation Components #### Primary Navigation ```html ``` ```css .nav-primary { background: var(--bg-elevated); border-bottom: 1px solid var(--border-primary); position: sticky; top: 0; z-index: var(--z-sticky); backdrop-filter: blur(8px); } .nav-container { display: flex; align-items: center; justify-content: space-between; padding: var(--spacing-component-md) var(--spacing-container-md); max-width: 1280px; margin: 0 auto; } .nav-brand { flex-shrink: 0; } .nav-logo-img { height: 2rem; width: auto; } .nav-links { display: flex; align-items: center; gap: var(--spacing-component-lg); } .nav-link { color: var(--text-secondary); text-decoration: none; font-weight: 500; padding: var(--spacing-component-sm) var(--spacing-component-md); border-radius: var(--radius-md); transition: all var(--transition-fast); } .nav-link:hover { color: var(--text-primary); background: var(--interactive-secondary); } .nav-link.active { color: var(--text-brand); background: var(--color-primary-50); } .dark .nav-link.active { background: var(--color-primary-900); } .nav-actions { display: flex; align-items: center; gap: var(--spacing-component-sm); } .nav-mobile { border-top: 1px solid var(--border-primary); background: var(--bg-elevated); } .nav-mobile-links { padding: var(--spacing-component-md); display: flex; flex-direction: column; gap: var(--spacing-component-xs); } .nav-mobile-link { color: var(--text-secondary); text-decoration: none; padding: var(--spacing-component-md); border-radius: var(--radius-md); transition: all var(--transition-fast); } .nav-mobile-link:hover { color: var(--text-primary); background: var(--interactive-secondary); } ``` ### 3. Button Components #### Button Variants ```html ``` ```css .btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--spacing-component-xs); padding: var(--spacing-component-sm) var(--spacing-component-md); border: 1px solid transparent; border-radius: var(--radius-button); font-size: var(--text-sm); font-weight: 500; line-height: 1.5; text-decoration: none; cursor: pointer; transition: all var(--transition-fast); user-select: none; white-space: nowrap; } .btn:disabled { opacity: 0.6; cursor: not-allowed; } .btn-primary { background: var(--interactive-primary); color: var(--text-inverse); box-shadow: var(--shadow-button); } .btn-primary:hover:not(:disabled) { background: var(--interactive-primary-hover); box-shadow: var(--shadow-button-hover); } .btn-primary:active { background: var(--interactive-primary-active); } .btn-secondary { background: var(--interactive-secondary); color: var(--text-primary); border-color: var(--border-primary); } .btn-secondary:hover:not(:disabled) { background: var(--interactive-secondary-hover); border-color: var(--border-secondary); } .btn-outline { background: transparent; color: var(--interactive-primary); border-color: var(--interactive-primary); } .btn-outline:hover:not(:disabled) { background: var(--interactive-primary); color: var(--text-inverse); } .btn-ghost { background: transparent; color: var(--text-secondary); } .btn-ghost:hover:not(:disabled) { background: var(--interactive-secondary); color: var(--text-primary); } .btn-icon { display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem; padding: 0; border: none; border-radius: var(--radius-button); background: transparent; color: var(--text-secondary); cursor: pointer; transition: all var(--transition-fast); } .btn-icon:hover { background: var(--interactive-secondary); color: var(--text-primary); } /* Button Sizes */ .btn-sm { padding: var(--spacing-component-xs) var(--spacing-component-sm); font-size: var(--text-xs); } .btn-lg { padding: var(--spacing-component-md) var(--spacing-component-lg); font-size: var(--text-base); } .btn-xl { padding: var(--spacing-component-lg) var(--spacing-component-xl); font-size: var(--text-lg); } ``` ### 4. Form Components #### Input Fields ```html
Park Type
``` ```css .form-group { margin-bottom: var(--spacing-component-lg); } .form-label { display: block; font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); margin-bottom: var(--spacing-component-xs); } .form-input, .form-textarea, .form-select { width: 100%; padding: var(--spacing-component-sm) var(--spacing-component-md); border: 1px solid var(--border-primary); border-radius: var(--radius-input); background: var(--bg-elevated); color: var(--text-primary); font-size: var(--text-sm); line-height: 1.5; transition: all var(--transition-fast); } .form-input:focus, .form-textarea:focus, .form-select:focus { outline: none; border-color: var(--border-focus); box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); } .form-input.error, .form-textarea.error, .form-select.error { border-color: var(--border-error); } .form-error { margin-top: var(--spacing-component-xs); font-size: var(--text-xs); color: var(--color-error-600); } .form-textarea { resize: vertical; min-height: 100px; } .form-checkbox, .form-radio { display: flex; align-items: flex-start; gap: var(--spacing-component-sm); cursor: pointer; } .form-checkbox-input, .form-radio-input { margin: 0; width: 1rem; height: 1rem; flex-shrink: 0; } .form-checkbox-label, .form-radio-label { font-size: var(--text-sm); color: var(--text-primary); line-height: 1.5; } .form-fieldset { border: none; padding: 0; margin: 0; } .form-legend { font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); margin-bottom: var(--spacing-component-sm); } .form-radio-group { display: flex; flex-direction: column; gap: var(--spacing-component-sm); } @media (min-width: 640px) { .form-radio-group { flex-direction: row; gap: var(--spacing-component-lg); } } ``` ### 5. Card Components #### Basic Card ```html

Card Title

Card subtitle

Card content goes here.

{{ park.name }}
{{ park.get_status_display }}

{{ park.name }}

{{ park.description|truncatewords:20 }}

{{ park.location }} {{ park.rides.count }} rides
``` ```css .card { background: var(--bg-elevated); border: 1px solid var(--border-primary); border-radius: var(--radius-card); box-shadow: var(--shadow-card); overflow: hidden; transition: all var(--transition-fast); } .card-hover:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-2px); } .card-header { padding: var(--spacing-component-lg); border-bottom: 1px solid var(--border-primary); } .card-title { font-size: var(--text-lg); font-weight: 600; color: var(--text-primary); margin: 0 0 var(--spacing-component-xs) 0; } .card-subtitle { font-size: var(--text-sm); color: var(--text-secondary); margin: 0; } .card-title-link { color: inherit; text-decoration: none; transition: color var(--transition-fast); } .card-title-link:hover { color: var(--text-brand); } .card-body { padding: var(--spacing-component-lg); } .card-text { color: var(--text-secondary); line-height: var(--leading-relaxed); margin: 0 0 var(--spacing-component-md) 0; } .card-footer { padding: var(--spacing-component-lg); border-top: 1px solid var(--border-primary); background: var(--bg-secondary); } .card-image { position: relative; aspect-ratio: 16 / 9; overflow: hidden; } .card-image-img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--transition-slow); } .card-hover:hover .card-image-img { transform: scale(1.05); } .card-image-overlay { position: absolute; top: var(--spacing-component-sm); right: var(--spacing-component-sm); } .card-meta { display: flex; flex-wrap: wrap; gap: var(--spacing-component-md); margin-top: var(--spacing-component-md); } .card-meta-item { display: flex; align-items: center; gap: var(--spacing-component-xs); font-size: var(--text-xs); color: var(--text-tertiary); } ``` ### 6. Modal Components #### Modal Structure ```html ``` ```css .modal-overlay { position: fixed; inset: 0; background: var(--bg-overlay); z-index: var(--z-modal); display: flex; align-items: center; justify-content: center; padding: var(--spacing-component-md); } .modal-container { width: 100%; max-width: 32rem; max-height: 90vh; overflow-y: auto; } .modal-content { background: var(--bg-elevated); border-radius: var(--radius-modal); box-shadow: var(--shadow-modal); overflow: hidden; } .modal-header { display: flex; align-items: center; justify-content: space-between; padding: var(--spacing-component-lg); border-bottom: 1px solid var(--border-primary); } .modal-title { font-size: var(--text-xl); font-weight: 600; color: var(--text-primary); margin: 0; } .modal-body { padding: var(--spacing-component-lg); } .modal-footer { display: flex; justify-content: flex-end; gap: var(--spacing-component-sm); padding: var(--spacing-component-lg); border-top: 1px solid var(--border-primary); background: var(--bg-secondary); } ``` ### 7. Utility Components #### Icons ```html ``` ```css .icon-xs { width: 0.75rem; height: 0.75rem; } .icon-sm { width: 1rem; height: 1rem; } .icon-md { width: 1.25rem; height: 1.25rem; } .icon-lg { width: 1.5rem; height: 1.5rem; } .icon-xl { width: 2rem; height: 2rem; } ``` #### Badges ```html Operating Seasonal Closed Under Construction Small Default Large ``` ```css .badge { display: inline-flex; align-items: center; padding: var(--spacing-component-xs) var(--spacing-component-sm); font-size: var(--text-xs); font-weight: 500; border-radius: var(--radius-badge); text-transform: uppercase; letter-spacing: var(--tracking-wide); } .badge-sm { padding: 0.125rem var(--spacing-component-xs); font-size: 0.625rem; } .badge-lg { padding: var(--spacing-component-sm) var(--spacing-component-md); font-size: var(--text-sm); } .badge-primary { background: var(--color-primary-100); color: var(--color-primary-800); } .badge-success { background: var(--color-success-100); color: var(--color-success-800); } .badge-warning { background: var(--color-warning-100); color: var(--color-warning-800); } .badge-error { background: var(--color-error-100); color: var(--color-error-800); } .badge-info { background: var(--color-info-100); color: var(--color-info-800); } .dark .badge-primary { background: var(--color-primary-900); color: var(--color-primary-200); } .dark .badge-success { background: var(--color-success-900); color: var(--color-success-200); } .dark .badge-warning { background: var(--color-warning-900); color: var(--color-warning-200); } .dark .badge-error { background: var(--color-error-900); color: var(--color-error-200); } .dark .badge-info { background: var(--color-info-900); color: var(--color-info-200); } ``` #### Avatars ```html User User User User User
JD
``` ```css .avatar-xs, .avatar-sm, .avatar-md, .avatar-lg, .avatar-xl { border-radius: var(--radius-full); object-fit: cover; flex-shrink: 0; } .avatar-xs { width: 1.5rem; height: 1.5rem; } .avatar-sm { width: 2rem; height: 2rem; } .avatar-md { width: 2.5rem; height: 2.5rem; } .avatar-lg { width: 3rem; height: 3rem; } .avatar-xl { width: 4rem; height: 4rem; } .avatar-fallback { display: flex; align-items: center; justify-content: center; background: var(--color-primary-100); color: var(--color-primary-700); font-weight: 500; font-size: 0.875rem; } .dark .avatar-fallback { background: var(--color-primary-800); color: var(--color-primary-200); } ``` ## Alpine.js Component Patterns ### Theme Toggle Component ```javascript Alpine.data('themeToggle', () => ({ isDark: localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches), init() { this.updateTheme(); }, toggle() { this.isDark = !this.isDark; this.updateTheme(); }, updateTheme() { if (this.isDark) { document.documentElement.classList.add('dark'); localStorage.setItem('theme', 'dark'); } else { document.documentElement.classList.remove('dark'); localStorage.setItem('theme', 'light'); } } })); ``` ### Modal Component ```javascript Alpine.data