Files
thrillwiki_django_no_react/docs/design-system/MIGRATION.md

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