Files
thrillwiki_django_no_react/docs/design-system

ThrillWiki Design System

A unified design system for the ThrillWiki application, providing consistent styling, components, and utilities across the entire frontend.

Quick Start

Directory Structure

backend/
├── static/
│   ├── css/
│   │   └── design-tokens.css    # Primary design tokens (colors, typography, spacing)
│   └── js/
│       ├── alpine-components.js  # Alpine.js components
│       └── stores/index.js       # Alpine.js stores
└── templates/
    ├── base/
    │   └── base.html             # Base template (extend this)
    └── components/
        └── ui/
            ├── button.html       # Button component
            ├── card.html         # Card component
            ├── dialog.html       # Modal/dialog component
            ├── icon.html         # SVG icon component
            └── input.html        # Form input component

CSS Loading Order

CSS files must be loaded in this specific order:

<link href="{% static 'css/design-tokens.css' %}" rel="stylesheet">
<link href="{% static 'css/tailwind.css' %}" rel="stylesheet">
<link href="{% static 'css/components.css' %}" rel="stylesheet">

JavaScript Loading Order

<!-- Alpine.js stores (loaded before Alpine) -->
<script src="{% static 'js/stores/index.js' %}"></script>

<!-- Alpine.js plugins -->
<script defer src="https://unpkg.com/@alpinejs/intersect@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://unpkg.com/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script>

<!-- Alpine.js core -->
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>

<!-- Alpine.js components (after Alpine loads) -->
<script src="{% static 'js/alpine-components.js' %}"></script>

Design Tokens

Design tokens are CSS custom properties that define the visual language of the application.

Color System

Color Scales

Each color has a scale from 50-950:

--color-primary-50   /* Lightest */
--color-primary-100
--color-primary-200
--color-primary-300
--color-primary-400
--color-primary-500  /* Base */
--color-primary-600
--color-primary-700
--color-primary-800
--color-primary-900
--color-primary-950  /* Darkest */

Available color scales:

  • primary - Blue (brand color)
  • secondary - Slate (neutral)
  • accent - Red (highlights)
  • success - Green
  • warning - Amber
  • error - Red
  • info - Sky blue

Semantic Colors

These adapt automatically for dark mode:

--color-background          /* Page background */
--color-foreground          /* Primary text */
--color-muted               /* Muted backgrounds */
--color-muted-foreground    /* Secondary text */
--color-border              /* Border color */
--color-card                /* Card background */
--color-card-foreground     /* Card text */
--color-primary             /* Primary action color */
--color-primary-foreground  /* Text on primary */
--color-destructive         /* Destructive actions */

Typography

/* Font Families */
--font-family-sans   /* Inter - body text */
--font-family-serif  /* Playfair Display - headings */
--font-family-mono   /* JetBrains Mono - code */

/* Font Sizes */
--font-size-xs       /* 12px */
--font-size-sm       /* 14px */
--font-size-base     /* 16px */
--font-size-lg       /* 18px */
--font-size-xl       /* 20px */
--font-size-2xl      /* 24px */
--font-size-3xl      /* 30px */
--font-size-4xl      /* 36px */

Spacing Scale

--spacing-1    /* 4px */
--spacing-2    /* 8px */
--spacing-3    /* 12px */
--spacing-4    /* 16px */
--spacing-6    /* 24px */
--spacing-8    /* 32px */
--spacing-12   /* 48px */
--spacing-16   /* 64px */

Shadows

--shadow-sm     /* Subtle shadow */
--shadow-base   /* Default shadow */
--shadow-md     /* Medium shadow */
--shadow-lg     /* Large shadow */
--shadow-xl     /* Extra large shadow */
--shadow-2xl    /* Maximum shadow */

Border Radius

--radius-sm     /* 2px */
--radius-base   /* 4px */
--radius-md     /* 6px */
--radius-lg     /* 8px */
--radius-xl     /* 12px */
--radius-full   /* 9999px (pill) */

Breakpoints

--breakpoint-sm   /* 640px - Mobile landscape */
--breakpoint-md   /* 768px - Tablets */
--breakpoint-lg   /* 1024px - Desktops */
--breakpoint-xl   /* 1280px - Large screens */
--breakpoint-2xl  /* 1536px - Extra large */

Components

Button Component

{% include "components/ui/button.html" with
    text="Button Text"
    variant="default"      {# default|secondary|destructive|outline|ghost|link #}
    size="default"         {# default|sm|lg|icon #}
    icon="search"          {# Optional: icon name #}
    disabled=False
    type="button"          {# button|submit|reset #}
    href=""                {# If provided, renders as <a> #}
    hx_get="/url"          {# HTMX attributes #}
    hx_post="/url"
    hx_target="#element"
    hx_swap="innerHTML"
    x_on_click="handler"   {# Alpine.js attributes #}
%}

Card Component

{% include "components/ui/card.html" with
    title="Card Title"
    description="Optional description"
    body_content="<p>Card content here</p>"
    footer_content="<button>Action</button>"
    class="additional-classes"
%}

Input Component

{% include "components/ui/input.html" with
    name="field_name"
    label="Field Label"
    type="text"            {# text|email|password|number|textarea #}
    placeholder="Placeholder text"
    value=""
    required=False
    disabled=False
    error="Error message"
    hint="Helper text"
%}

Dialog/Modal Component

{% include "components/ui/dialog.html" with
    id="modal-id"
    title="Dialog Title"
    description="Optional description"
    content="<p>Dialog content</p>"
    footer="<button>Action</button>"
    size="default"         {# sm|default|lg|xl|full #}
    closable=True
    open=True
%}

Icon Component

{% include "components/ui/icon.html" with
    name="search"          {# Icon name from library #}
    size="md"              {# xs|sm|md|lg|xl #}
    class="text-primary"   {# Additional classes #}
%}

Available icons: search, menu, close, chevron-up, chevron-down, chevron-left, chevron-right, arrow-up, arrow-down, arrow-left, arrow-right, user, users, settings, cog, heart, heart-filled, star, star-filled, home, edit, trash, copy, external-link, download, upload, check, check-circle, x-circle, info, warning, error, plus, minus, filter, sort, calendar, clock, map-pin, phone, mail, globe, link, image, camera, play, pause, volume, bell, bookmark, share, refresh, eye, eye-off, lock, unlock, sun, moon, loader

Utility Classes

Responsive Utilities

/* Visibility */
.hidden-mobile     /* Hidden on mobile */
.hidden-sm         /* Hidden on sm breakpoint */
.show-mobile       /* Only visible on mobile */
.show-lg           /* Only visible on lg breakpoint */

/* Grid */
.grid-responsive-2  /* 1 col mobile, 2 cols sm+ */
.grid-responsive-3  /* 1 col mobile, 2 cols sm+, 3 cols lg+ */
.grid-responsive-4  /* 1 col mobile, 2 cols sm+, 4 cols lg+ */
.grid-auto-fit      /* Auto-fit grid with 300px min */

/* Flex */
.stack-to-row      /* Column on mobile, row on sm+ */
.stack-to-row-lg   /* Column on mobile/tablet, row on lg+ */

/* Spacing */
.py-responsive     /* 16px mobile, 24px sm, 32px lg */
.px-responsive     /* Same for horizontal */
.gap-responsive    /* Responsive gap */

Container Classes

.container      /* max-width: 1280px */
.container-sm   /* max-width: 640px */
.container-md   /* max-width: 768px */
.container-lg   /* max-width: 1024px */
.container-xl   /* max-width: 1280px */
.container-2xl  /* max-width: 1536px */

Accessibility Utilities

.sr-only              /* Visually hidden, screen reader accessible */
.sr-only-focusable    /* sr-only that becomes visible on focus */
.focus-ring           /* Focus ring on :focus-visible */
.touch-target         /* Minimum 44x44px touch target */
.skip-link            /* Skip to content link */

Alpine.js Stores

Theme Store

// Access
Alpine.store('theme')

// Properties
$store.theme.isDark      // Current theme state
$store.theme.systemTheme // System preference

// Methods
$store.theme.toggle()    // Toggle theme
$store.theme.set('dark') // Set specific theme

Toast Store

// Show notifications
Alpine.store('toast').show('Message', 'success')
Alpine.store('toast').success('Success message')
Alpine.store('toast').error('Error message')
Alpine.store('toast').warning('Warning message')
Alpine.store('toast').info('Info message')

Auth Store

// Access current user
$store.auth.user         // User object or null
$store.auth.isLoggedIn   // Boolean
$store.auth.hasPermission('permission_name')

UI Store

// Modal management
Alpine.store('ui').openModal('modal-id')
Alpine.store('ui').closeModal('modal-id')
Alpine.store('ui').isModalOpen('modal-id')

// Sidebar
Alpine.store('ui').toggleSidebar()
$store.ui.isSidebarOpen

Dark Mode

Dark mode is automatically supported through CSS custom properties. Toggle with:

<button @click="$store.theme.toggle()">
    Toggle Theme
</button>

Or use the theme toggle component in the navbar.

Accessibility

Focus Management

All interactive elements have visible focus indicators using :focus-visible. Custom focus rings can be added with .focus-ring.

Reduced Motion

Animations are automatically disabled for users who prefer reduced motion:

@media (prefers-reduced-motion: reduce) {
    * { animation-duration: 0.01ms !important; }
}

Screen Reader Support

Use .sr-only for screen reader text:

<button>
    <svg>...</svg>
    <span class="sr-only">Close menu</span>
</button>

Touch Targets

Ensure interactive elements meet minimum touch target size (44x44px):

<button class="touch-target">...</button>

Testing

Visit /design-system-test/ (development only) to see all components rendered with their various states and options.

Migration from Legacy System

If migrating from the legacy system:

  1. Replace HSL color variables with design token references
  2. Replace Font Awesome icons with {% include "components/ui/icon.html" %}
  3. Update button classes from .btn-primary to the button component
  4. Replace inline Alpine.js stores with centralized stores from stores/index.js
  5. Update responsive classes to use new utility classes

See MIGRATION.md for detailed migration steps.