Files

8.1 KiB

Design System Migration Guide

This guide helps migrate from the legacy frontend system to the unified design system.

Overview

The ThrillWiki frontend was consolidated from two parallel systems:

  • Legacy System (/backend/templates/, /backend/static/css/) - HSL-based variables, Font Awesome icons
  • Modern System (/templates/, /static/css/) - RGB hex-based design tokens, SVG icons

The unified system uses design-tokens.css as the single source of truth.

CSS Migration

Color Variables

Replace HSL-based variables with design tokens:

/* Before (legacy) */
background-color: hsl(var(--primary));
color: hsl(var(--primary-foreground));

/* After (design tokens) */
background-color: var(--color-primary);
color: var(--color-primary-foreground);

Common Replacements

Legacy Design Token
hsl(var(--primary)) var(--color-primary)
hsl(var(--secondary)) var(--color-secondary)
hsl(var(--background)) var(--color-background)
hsl(var(--foreground)) var(--color-foreground)
hsl(var(--muted)) var(--color-muted)
hsl(var(--accent)) var(--color-accent)
hsl(var(--destructive)) var(--color-destructive)
hsl(var(--border)) var(--color-border)
hsl(var(--card)) var(--color-card)

Font Variables

/* Before */
font-family: var(--font-sans);

/* After */
font-family: var(--font-family-sans);

Shadow Variables

/* Before */
box-shadow: var(--shadow);

/* After */
box-shadow: var(--shadow-base);

Icon Migration

Replace Font Awesome icons with the SVG icon component:

<!-- Before (Font Awesome) -->
<i class="fa fa-search"></i>
<i class="fas fa-user"></i>
<i class="far fa-heart"></i>

<!-- After (SVG Icon Component) -->
{% include "components/ui/icon.html" with name="search" %}
{% include "components/ui/icon.html" with name="user" %}
{% include "components/ui/icon.html" with name="heart" %}

Icon Name Mapping

Font Awesome SVG Icon
fa-search search
fa-user user
fa-users users
fa-cog settings
fa-heart heart
fa-star star
fa-home home
fa-edit edit
fa-trash trash
fa-copy copy
fa-external-link external-link
fa-chevron-down chevron-down
fa-chevron-up chevron-up
fa-chevron-left chevron-left
fa-chevron-right chevron-right
fa-check check
fa-times close
fa-plus plus
fa-minus minus
fa-bars menu

Icon Sizes

<!-- Before -->
<i class="fa fa-search fa-sm"></i>
<i class="fa fa-search fa-lg"></i>
<i class="fa fa-search fa-2x"></i>

<!-- After -->
{% include "components/ui/icon.html" with name="search" size="sm" %}
{% include "components/ui/icon.html" with name="search" size="lg" %}
{% include "components/ui/icon.html" with name="search" size="xl" %}

Button Migration

Basic Buttons

<!-- Before -->
<button class="btn btn-primary">Click Me</button>
<button class="btn btn-secondary">Click Me</button>
<button class="btn btn-danger">Delete</button>
<button class="btn btn-outline">Outline</button>

<!-- After -->
{% include "components/ui/button.html" with text="Click Me" variant="default" %}
{% include "components/ui/button.html" with text="Click Me" variant="secondary" %}
{% include "components/ui/button.html" with text="Delete" variant="destructive" %}
{% include "components/ui/button.html" with text="Outline" variant="outline" %}

Buttons with Icons

<!-- Before -->
<button class="btn btn-primary">
    <i class="fa fa-search mr-2"></i>
    Search
</button>

<!-- After -->
{% include "components/ui/button.html" with text="Search" icon="search" %}
<!-- Before -->
<a href="/url" class="btn btn-primary">Go</a>

<!-- After -->
{% include "components/ui/button.html" with text="Go" href="/url" %}

Form Input Migration

<!-- Before -->
<div class="form-group">
    <label for="email">Email</label>
    <input type="email" id="email" name="email" class="form-control" placeholder="Enter email">
    <small class="form-text text-muted">We'll never share your email.</small>
</div>

<!-- After -->
{% include "components/ui/input.html" with
    name="email"
    label="Email"
    type="email"
    placeholder="Enter email"
    hint="We'll never share your email."
%}

Card Migration

<!-- Before -->
<div class="card">
    <div class="card-header">
        <h3 class="card-title">Title</h3>
    </div>
    <div class="card-body">
        <p>Content here</p>
    </div>
    <div class="card-footer">
        <button class="btn">Action</button>
    </div>
</div>

<!-- After -->
{% include "components/ui/card.html" with
    title="Title"
    body_content="<p>Content here</p>"
    footer_content="<button class='btn'>Action</button>"
%}

Modal Migration

<!-- Before -->
<div class="modal" id="myModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Title</h5>
                <button class="close">&times;</button>
            </div>
            <div class="modal-body">Content</div>
            <div class="modal-footer">
                <button class="btn">Close</button>
            </div>
        </div>
    </div>
</div>

<!-- After -->
{% include "components/ui/dialog.html" with
    id="myModal"
    title="Title"
    content="Content"
    footer="<button class='btn'>Close</button>"
%}

Alpine.js Migration

Store Definitions

Move inline store definitions to stores/index.js:

// Before (inline in template)
<script>
document.addEventListener('alpine:init', () => {
    Alpine.store('theme', {
        isDark: false,
        toggle() { this.isDark = !this.isDark; }
    });
});
</script>

// After (use centralized store)
// Store is already defined in stores/index.js
// Access via $store.theme.toggle()

Component Definitions

Move inline component definitions to alpine-components.js:

// Before (inline in template)
<div x-data="{ open: false }">
    <button @click="open = !open">Toggle</button>
    <div x-show="open">Content</div>
</div>

// After (use registered component)
<div x-data="dropdown()">
    <button @click="toggle()">Toggle</button>
    <div x-show="open">Content</div>
</div>

Alert/Toast Migration

<!-- Before -->
<div class="alert alert-success">
    <strong>Success!</strong> Your action was completed.
</div>

<!-- After -->
<div class="alert alert-success" role="alert">
    <div class="alert-title">Success!</div>
    <div class="alert-description">Your action was completed.</div>
</div>

<!-- Or use toast store for dynamic notifications -->
<script>
    Alpine.store('toast').success('Your action was completed.');
</script>

Responsive Classes Migration

<!-- Before -->
<div class="d-none d-md-block">Hidden on mobile</div>
<div class="d-md-none">Only on mobile</div>
<div class="row">
    <div class="col-12 col-md-6">...</div>
</div>

<!-- After -->
<div class="hidden-mobile">Hidden on mobile</div>
<div class="show-mobile">Only on mobile</div>
<div class="grid-responsive-2">
    <div>...</div>
    <div>...</div>
</div>

Checklist

Use this checklist when migrating a template:

  • Replace HSL color variables with design tokens
  • Replace Font Awesome icons with SVG icon component
  • Update button markup to use button component
  • Update form inputs to use input component
  • Update cards to use card component
  • Update modals to use dialog component
  • Remove inline Alpine.js store definitions
  • Update responsive classes
  • Test dark mode appearance
  • Test focus states for accessibility
  • Test on mobile viewport

Testing Migration

After migrating, visit /design-system-test/ to compare your migrated components against the reference implementation.

Getting Help

If you encounter issues during migration:

  1. Check the design system README for component documentation
  2. Review design-tokens.css for available tokens
  3. Inspect the design system test page for examples