AgentSkillsCN

ui-theming

一次性 UI 主题化技能。设置设计系统(主题、令牌、排版)。在项目开始时运行一次,然后再实施功能。

SKILL.md
--- frontmatter
name: ui-theming
description: One-time UI theming skill. Sets up design system (theme, tokens, typography). Run ONCE at project start before implementing features.

UI Theming Skill

Initialize the design system. This skill should be run ONCE at project start to set up:

  • Color theme selection
  • Design token generation
  • Typography system
  • SCSS structure

Usage

code
/ui-theming

Workflow

Step 0: Check Existing Design System

Before creating files, check if a design system already exists:

bash
ls frontend/src/styles/tokens/ 2>/dev/null

If directory exists with files, show:

code
⚠️ Design System bereits vorhanden!

Gefundene Dateien:
- _colors.scss
- _typography.scss
- [weitere Dateien...]

Optionen:
1. Abbrechen (bestehende Dateien behalten)
2. Überschreiben (Theme neu generieren)
3. Nur Theme-Preview generieren (keine Änderungen)

Wie möchten Sie fortfahren?
  • Option 1: Exit skill, no changes
  • Option 2: Continue with Step 1 (files will be overwritten)
  • Option 3: Skip to Step 7 (Theme-Auswahl + Preview generieren, keine SCSS-Änderungen)

If directory does not exist: Continue with Step 1.


Step 1: Theme Selection

Present the available themes:

code
Available Themes:

1. Ocean Depths     - Professional maritime (Blue/Teal)
2. Sunset Boulevard - Warm vibrant (Orange/Pink)
3. Forest Canopy    - Natural earth tones (Green/Brown)
4. Modern Minimalist - Clean grayscale (Gray/Black)
5. Golden Hour      - Rich autumnal (Gold/Orange)
6. Arctic Frost     - Cool crisp winter (Light Blue/White)
7. Desert Rose      - Soft sophisticated (Pink/Beige)
8. Tech Innovation  - Bold modern tech (Blue/Purple) [RECOMMENDED]
9. Botanical Garden - Fresh organic (Green/Cream)
10. Midnight Galaxy - Dramatic cosmic (Dark Purple/Blue)

Which theme would you like for your project?

Step 2: Read Selected Theme

After user selects a theme, read the theme file:

code
Read: ${CLAUDE_PLUGIN_ROOT}/skills/ui-theming/themes/[selected-theme].md

Extract:

  • Primary color
  • Secondary color
  • Font family
  • Font weights

Step 3: Farbvarianten berechnen

Aus den Theme-Farben (Primary + Accent) die Light/Dark-Varianten ableiten.

Algorithmus (HSL-Farbraum):

VarianteBerechnung
primary-lightLightness +20%, Saturation -10%
primary-darkLightness -20%, Saturation +5%
accent-lightLightness +20%, Saturation -10%
accent-darkLightness -20%, Saturation +5%

Beispiel: Primary #0066ff (HSL: 216°, 100%, 50%) → Light: HSL(216°, 90%, 70%) = #5c9aff → Dark: HSL(216°, 100%, 30%) = #003d99

Contrast-Farbe: Weiß (#ffffff) wenn Lightness < 50%, sonst Schwarz (#000000).

Dark-Mode Semantic Colors (fest definiert):

TokenLight ModeDark Mode
success#4caf50#81c784
warning#ff9800#ffb74d
error#f44336#e57373
info#2196f3#64b5f6

Step 4: Create Token Files

Create the following modular structure in frontend/src/styles/tokens/:

code
frontend/src/styles/
├── tokens/
│   ├── _colors.scss        # Color palette + light/dark mode
│   ├── _typography.scss    # Font system as CSS Custom Properties
│   ├── _spacing.scss       # 8pt grid + semantic aliases
│   ├── _elevation.scss     # Material elevation shadows
│   ├── _breakpoints.scss   # Responsive breakpoints + mixins
│   └── _index.scss         # Central export (@forward + theme mixins)
└── styles.scss             # Root file (@use tokens)

tokens/_colors.scss

scss
// Color System - CSS Custom Properties
// Generated by /ui-theming
// Theme: [Selected Theme]

@mixin colors-light {
  // Brand Colors (from selected theme)
  --color-primary: [primary-hex];
  --color-primary-light: [primary-light-hex];
  --color-primary-dark: [primary-dark-hex];
  --color-primary-contrast: #ffffff;

  --color-accent: [accent-hex];
  --color-accent-light: [accent-light-hex];
  --color-accent-dark: [accent-dark-hex];
  --color-accent-contrast: #ffffff;

  // Semantic Colors
  --color-success: #4caf50;
  --color-warning: #ff9800;
  --color-error: #f44336;
  --color-info: #2196f3;

  // Neutral Palette
  --color-gray-50: #fafafa;
  --color-gray-100: #f5f5f5;
  --color-gray-200: #eeeeee;
  --color-gray-300: #e0e0e0;
  --color-gray-400: #bdbdbd;
  --color-gray-500: #9e9e9e;
  --color-gray-600: #757575;
  --color-gray-700: #616161;
  --color-gray-800: #424242;
  --color-gray-900: #212121;

  // Surface & Background
  --color-background: #fafafa;
  --color-surface: #ffffff;
  --color-surface-variant: #f5f5f5;

  // Text
  --color-text-primary: rgba(0, 0, 0, 0.87);
  --color-text-secondary: rgba(0, 0, 0, 0.6);
  --color-text-disabled: rgba(0, 0, 0, 0.38);
  --color-text-on-primary: #ffffff;

  // Borders
  --color-divider: rgba(0, 0, 0, 0.12);
  --color-border: rgba(0, 0, 0, 0.23);
}

@mixin colors-dark {
  --color-primary: [primary-light-hex];
  --color-primary-light: [primary-hex];
  --color-primary-dark: [primary-dark-hex];
  --color-primary-contrast: #000000;

  --color-accent: [accent-light-hex];
  --color-accent-contrast: #000000;

  --color-success: #81c784;
  --color-warning: #ffb74d;
  --color-error: #e57373;
  --color-info: #64b5f6;

  --color-gray-50: #212121;
  --color-gray-100: #303030;
  --color-gray-200: #424242;
  --color-gray-300: #616161;
  --color-gray-400: #757575;
  --color-gray-500: #9e9e9e;
  --color-gray-600: #bdbdbd;
  --color-gray-700: #e0e0e0;
  --color-gray-800: #eeeeee;
  --color-gray-900: #fafafa;

  --color-background: #121212;
  --color-surface: #1e1e1e;
  --color-surface-variant: #2c2c2c;

  --color-text-primary: rgba(255, 255, 255, 0.87);
  --color-text-secondary: rgba(255, 255, 255, 0.6);
  --color-text-disabled: rgba(255, 255, 255, 0.38);
  --color-text-on-primary: #000000;

  --color-divider: rgba(255, 255, 255, 0.12);
  --color-border: rgba(255, 255, 255, 0.23);
}

tokens/_typography.scss

scss
// Typography System - CSS Custom Properties
// Generated by /ui-theming

@mixin typography {
  // Font Families (from selected theme)
  --font-family-base: '[theme-font]', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-family-mono: 'Roboto Mono', 'Consolas', monospace;

  // Font Sizes (Modular Scale 1.25)
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  --font-size-2xl: 1.5rem;
  --font-size-3xl: 1.875rem;
  --font-size-4xl: 2.25rem;
  --font-size-5xl: 3rem;

  // Font Weights
  --font-weight-light: 300;
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-bold: 700;

  // Line Heights
  --line-height-tight: 1.25;
  --line-height-base: 1.5;
  --line-height-relaxed: 1.75;

  // Letter Spacing
  --letter-spacing-tight: -0.025em;
  --letter-spacing-normal: 0;
  --letter-spacing-wide: 0.025em;

  // Semantic Typography Tokens
  --font-heading-1: var(--font-weight-light) var(--font-size-5xl) / var(--line-height-tight) var(--font-family-base);
  --font-heading-2: var(--font-weight-light) var(--font-size-4xl) / var(--line-height-tight) var(--font-family-base);
  --font-heading-3: var(--font-weight-regular) var(--font-size-3xl) / var(--line-height-tight) var(--font-family-base);
  --font-heading-4: var(--font-weight-regular) var(--font-size-2xl) / var(--line-height-base) var(--font-family-base);
  --font-heading-5: var(--font-weight-medium) var(--font-size-xl) / var(--line-height-base) var(--font-family-base);
  --font-heading-6: var(--font-weight-medium) var(--font-size-lg) / var(--line-height-base) var(--font-family-base);
  --font-body-1: var(--font-weight-regular) var(--font-size-base) / var(--line-height-base) var(--font-family-base);
  --font-body-2: var(--font-weight-regular) var(--font-size-sm) / var(--line-height-base) var(--font-family-base);
  --font-caption: var(--font-weight-regular) var(--font-size-xs) / var(--line-height-base) var(--font-family-base);
}

tokens/_spacing.scss

scss
// Spacing System - 8pt Grid
// Generated by /ui-theming

@mixin spacing {
  // Base Scale
  --spacing-0: 0;
  --spacing-1: 0.25rem;    // 4px
  --spacing-2: 0.5rem;     // 8px
  --spacing-3: 0.75rem;    // 12px
  --spacing-4: 1rem;       // 16px
  --spacing-5: 1.25rem;    // 20px
  --spacing-6: 1.5rem;     // 24px
  --spacing-8: 2rem;       // 32px
  --spacing-10: 2.5rem;    // 40px
  --spacing-12: 3rem;      // 48px
  --spacing-16: 4rem;      // 64px

  // Semantic Aliases
  --spacing-page-x: var(--spacing-6);
  --spacing-page-y: var(--spacing-8);
  --spacing-section: var(--spacing-8);
  --spacing-card: var(--spacing-4);
  --spacing-inline: var(--spacing-2);

  // Border Radius
  --radius-none: 0;
  --radius-sm: 0.125rem;
  --radius-md: 0.25rem;
  --radius-lg: 0.5rem;
  --radius-xl: 1rem;
  --radius-full: 9999px;

  // Component Tokens
  --card-padding: var(--spacing-4);
  --card-radius: var(--radius-md);
  --button-padding-x: var(--spacing-4);
  --button-padding-y: var(--spacing-2);
  --button-radius: var(--radius-md);
  --input-padding-x: var(--spacing-4);
  --input-padding-y: var(--spacing-2);
  --input-radius: var(--radius-md);
  --table-cell-padding: var(--spacing-3) var(--spacing-4);
  --toolbar-height: 4rem;
  --sidenav-width: 16rem;
}

tokens/_elevation.scss

scss
// Elevation & Shadows
// Generated by /ui-theming

@mixin elevation {
  // Material Design Elevation
  --elevation-0: none;
  --elevation-1: 0 2px 1px -1px rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 1px 3px 0 rgba(0,0,0,.12);
  --elevation-2: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
  --elevation-4: 0 2px 4px -1px rgba(0,0,0,.2), 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12);
  --elevation-8: 0 5px 5px -3px rgba(0,0,0,.2), 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12);
  --elevation-16: 0 8px 10px -5px rgba(0,0,0,.2), 0 16px 24px 2px rgba(0,0,0,.14), 0 6px 30px 5px rgba(0,0,0,.12);

  // Semantic Aliases
  --shadow-card: var(--elevation-1);
  --shadow-dropdown: var(--elevation-4);
  --shadow-modal: var(--elevation-16);

  // Transitions
  --transition-fast: 150ms ease-in-out;
  --transition-base: 250ms ease-in-out;
  --transition-slow: 350ms ease-in-out;

  // Z-Index Scale
  --z-dropdown: 1000;
  --z-sticky: 1020;
  --z-fixed: 1030;
  --z-modal-backdrop: 1040;
  --z-modal: 1050;
  --z-popover: 1060;
  --z-tooltip: 1070;
}

tokens/_breakpoints.scss

scss
// Responsive Breakpoints
// Generated by /ui-theming

$breakpoint-sm: 600px;
$breakpoint-md: 960px;
$breakpoint-lg: 1280px;
$breakpoint-xl: 1920px;

// Breakpoint Mixins (use in components)
@mixin sm { @media (min-width: $breakpoint-sm) { @content; } }
@mixin md { @media (min-width: $breakpoint-md) { @content; } }
@mixin lg { @media (min-width: $breakpoint-lg) { @content; } }
@mixin xl { @media (min-width: $breakpoint-xl) { @content; } }

@mixin below-sm { @media (max-width: ($breakpoint-sm - 1)) { @content; } }
@mixin below-md { @media (max-width: ($breakpoint-md - 1)) { @content; } }
@mixin below-lg { @media (max-width: ($breakpoint-lg - 1)) { @content; } }

tokens/_index.scss

scss
// Token System Entry Point
// Generated by /ui-theming
// Uses @forward/@use (modern SCSS module system)

@forward 'breakpoints';

@use 'colors';
@use 'typography';
@use 'spacing';
@use 'elevation';

// All tokens combined
@mixin all-tokens {
  @include typography.typography;
  @include spacing.spacing;
  @include elevation.elevation;
}

// Theme: Light (default)
@mixin theme-light {
  @include colors.colors-light;
  @include all-tokens;
}

// Theme: Dark
@mixin theme-dark {
  @include colors.colors-dark;
  @include all-tokens;
}

Step 5: Update styles.scss

Ensure frontend/src/styles.scss uses the token system:

scss
@use 'styles/tokens' as tokens;

// Apply light theme by default
:root {
  @include tokens.theme-light;
}

// Dark mode support
@media (prefers-color-scheme: dark) {
  :root {
    @include tokens.theme-dark;
  }
}

// Manual dark mode toggle class
.dark-theme {
  @include tokens.theme-dark;
}

Step 6: Verify Setup

bash
# Check file structure
ls -la frontend/src/styles/tokens/

# Build to verify no errors
cd frontend && npm run build

Step 7: Generate Theme Preview

7.1: Theme bestimmen

Wenn aus Step 6 kommend (voller Workflow): → Das in Step 1 gewählte Theme verwenden, keine erneute Abfrage.

Wenn direkt zu Step 7 gesprungen (Option 3 aus Step 0): → Theme-Auswahl anzeigen:

code
Welches Theme möchten Sie in der Preview sehen?

1. Ocean Depths     - Professional maritime (Blue/Teal)
2. Sunset Boulevard - Warm vibrant (Orange/Pink)
3. Forest Canopy    - Natural earth tones (Green/Brown)
4. Modern Minimalist - Clean grayscale (Gray/Black)
5. Golden Hour      - Rich autumnal (Gold/Orange)
6. Arctic Frost     - Cool crisp winter (Light Blue/White)
7. Desert Rose      - Soft sophisticated (Pink/Beige)
8. Tech Innovation  - Bold modern tech (Blue/Purple)
9. Botanical Garden - Fresh organic (Green/Cream)
10. Midnight Galaxy - Dramatic cosmic (Dark Purple/Blue)

Theme-Nummer eingeben:

7.2: Projekt-Elemente analysieren

Scanne das Projekt nach verwendeten UI-Elementen (Material UND native HTML):

bash
# 1. Angular Material Komponenten
grep -roh "mat-[a-z-]*" frontend/src/app --include="*.html" | sort | uniq -c | sort -rn

# 2. Native HTML Elemente
grep -roE "<(table|form|input|select|textarea|button|ul|ol|nav|header|footer|aside|dialog|details|summary)[^>]*>" frontend/src/app --include="*.html" | sort | uniq -c | sort -rn

# 3. Custom Components (app-*)
grep -roh "app-[a-z-]*" frontend/src/app --include="*.html" | sort | uniq -c | sort -rn

Beispiel-Output:

code
=== Angular Material ===
  45 mat-button
  34 mat-form-field
  12 mat-table

=== Native HTML ===
  28 <table
  56 <input
  23 <select
  15 <button
  12 <ul
   8 <nav
   4 <dialog

=== Custom Components ===
  18 app-user-card
   9 app-status-badge
   5 app-data-grid

Element-Mapping für Preview:

Element (Material ODER Native)Preview-Sektion
mat-table ODER <table>Datentabelle
mat-form-field ODER <input>/<select>Formular
mat-button ODER <button>Buttons
mat-card ODER <article>/<section>Cards
mat-chip ODER .badge/.tagChips/Tags
mat-tab ODER Custom TabsTabs
<ul>/<ol>Listen
<nav>Navigation
<dialog> ODER mat-dialogDialoge
mat-progress-bar ODER <progress>Loading States
<details>/<summary>Accordions

Ergebnis speichern:

code
Gefundene UI-Elemente für Preview:

Material Components:
- [X] mat-button (45x)
- [X] mat-form-field (34x)
- [X] mat-table (12x)

Native HTML:
- [X] <table> (28x)
- [X] <input> (56x)
- [X] <ul> Listen (12x)
- [X] <nav> Navigation (8x)
- [ ] <dialog> (nicht verwendet)

Custom Components:
- [X] app-user-card (18x) → Card-Variante
- [X] app-status-badge (9x) → Chip/Badge-Variante

7.3: Theme-Datei lesen (nur bei Option 3)

code
Read: ${CLAUDE_PLUGIN_ROOT}/skills/ui-theming/themes/[selected-theme].md

Extrahiere Primary und Accent Color, berechne Light/Dark-Varianten (wie in Step 3).

7.4: Preview HTML erstellen (nur gefundene Komponenten)

Create wireframes/theme-preview-[theme-name].html - a standalone HTML file for instant visual feedback:

html
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Theme Preview - [Theme Name]</title>
  <link href="https://fonts.googleapis.com/css2?family=[theme-font]:wght@300;400;500;700&display=swap" rel="stylesheet">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <style>
    /* ========== GENERATED TOKENS (from _colors.scss) ========== */
    :root {
      /* Brand Colors */
      --color-primary: [primary-hex];
      --color-primary-light: [primary-light-hex];
      --color-primary-dark: [primary-dark-hex];
      --color-accent: [accent-hex];

      /* Semantic */
      --color-success: #4caf50;
      --color-warning: #ff9800;
      --color-error: #f44336;
      --color-info: #2196f3;

      /* Neutrals */
      --color-gray-100: #f5f5f5;
      --color-gray-200: #eeeeee;
      --color-gray-300: #e0e0e0;
      --color-gray-600: #757575;
      --color-gray-800: #424242;
      --color-gray-900: #212121;

      /* Surface */
      --color-background: #fafafa;
      --color-surface: #ffffff;
      --color-text-primary: rgba(0,0,0,0.87);
      --color-text-secondary: rgba(0,0,0,0.6);
      --color-divider: rgba(0,0,0,0.12);

      /* Typography */
      --font-family-base: '[theme-font]', sans-serif;

      /* Spacing */
      --spacing-2: 0.5rem;
      --spacing-3: 0.75rem;
      --spacing-4: 1rem;
      --spacing-6: 1.5rem;
      --radius-md: 0.25rem;

      /* Elevation */
      --elevation-1: 0 2px 1px -1px rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 1px 3px 0 rgba(0,0,0,.12);
      --elevation-2: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
    }

    .dark-theme {
      --color-primary: [primary-light-hex];
      --color-background: #121212;
      --color-surface: #1e1e1e;
      --color-text-primary: rgba(255,255,255,0.87);
      --color-text-secondary: rgba(255,255,255,0.6);
      --color-divider: rgba(255,255,255,0.12);
      --color-gray-100: #303030;
      --color-gray-200: #424242;
      --color-gray-300: #616161;
    }

    /* ========== BASE STYLES ========== */
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: var(--font-family-base);
      background: var(--color-background);
      color: var(--color-text-primary);
      padding: var(--spacing-6);
      transition: background 0.3s, color 0.3s;
    }

    .container { max-width: 1200px; margin: 0 auto; }

    h1 { font-size: 2.25rem; font-weight: 300; margin-bottom: var(--spacing-6); }
    h2 { font-size: 1.5rem; font-weight: 400; margin: var(--spacing-6) 0 var(--spacing-4); color: var(--color-text-secondary); }
    h3 { font-size: 1.25rem; font-weight: 500; margin-bottom: var(--spacing-3); }

    /* ========== THEME TOGGLE ========== */
    .theme-toggle {
      position: fixed; top: 1rem; right: 1rem;
      background: var(--color-surface); border: 1px solid var(--color-divider);
      padding: var(--spacing-2) var(--spacing-4); border-radius: var(--radius-md);
      cursor: pointer; box-shadow: var(--elevation-1);
    }

    /* ========== COLOR SWATCHES ========== */
    .swatches { display: flex; flex-wrap: wrap; gap: var(--spacing-3); }
    .swatch {
      width: 100px; height: 80px; border-radius: var(--radius-md);
      display: flex; flex-direction: column; justify-content: flex-end;
      padding: var(--spacing-2); font-size: 0.75rem; color: white;
      box-shadow: var(--elevation-1);
    }
    .swatch-label { font-weight: 500; }
    .swatch-hex { opacity: 0.8; }

    /* ========== CARD ========== */
    .card {
      background: var(--color-surface);
      border-radius: var(--radius-md);
      box-shadow: var(--elevation-1);
      padding: var(--spacing-4);
      margin-bottom: var(--spacing-4);
    }

    /* ========== BUTTONS ========== */
    .btn {
      display: inline-flex; align-items: center; gap: var(--spacing-2);
      padding: var(--spacing-2) var(--spacing-4);
      border: none; border-radius: var(--radius-md);
      font-family: inherit; font-size: 0.875rem; font-weight: 500;
      cursor: pointer; transition: all 0.2s;
    }
    .btn-primary { background: var(--color-primary); color: white; }
    .btn-primary:hover { filter: brightness(1.1); }
    .btn-secondary { background: transparent; color: var(--color-primary); border: 1px solid var(--color-divider); }
    .btn-danger { background: var(--color-error); color: white; }
    .btn-icon { padding: var(--spacing-2); min-width: auto; }

    /* ========== TABLE ========== */
    .table { width: 100%; border-collapse: collapse; }
    .table th, .table td {
      padding: var(--spacing-3) var(--spacing-4);
      text-align: left; border-bottom: 1px solid var(--color-divider);
    }
    .table th { font-weight: 500; color: var(--color-text-secondary); font-size: 0.75rem; text-transform: uppercase; }
    .table tr:hover { background: var(--color-gray-100); }
    .table-actions { display: flex; gap: var(--spacing-2); }

    /* ========== FORM ========== */
    .form-group { margin-bottom: var(--spacing-4); }
    .form-label { display: block; font-size: 0.875rem; color: var(--color-text-secondary); margin-bottom: var(--spacing-2); }
    .form-input {
      width: 100%; padding: var(--spacing-3) var(--spacing-4);
      border: 1px solid var(--color-divider); border-radius: var(--radius-md);
      font-family: inherit; font-size: 1rem;
      background: var(--color-surface); color: var(--color-text-primary);
    }
    .form-input:focus { outline: 2px solid var(--color-primary); border-color: transparent; }
    .form-select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23757575' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 1rem center; }
    .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-4); }
    .form-actions { display: flex; gap: var(--spacing-3); justify-content: flex-end; margin-top: var(--spacing-6); }

    /* ========== CHIPS ========== */
    .chip {
      display: inline-flex; align-items: center; gap: var(--spacing-2);
      padding: var(--spacing-1, 0.25rem) var(--spacing-3);
      border-radius: 9999px; font-size: 0.75rem; font-weight: 500;
    }
    .chip-success { background: #e8f5e9; color: #2e7d32; }
    .chip-warning { background: #fff3e0; color: #e65100; }
    .chip-error { background: #ffebee; color: #c62828; }

    /* ========== GRID LAYOUT ========== */
    .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-6); }
    @media (max-width: 768px) { .grid-2 { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <button class="theme-toggle" onclick="document.body.classList.toggle('dark-theme')">
    <span class="material-icons" style="vertical-align: middle;">dark_mode</span> Toggle Dark Mode
  </button>

  <div class="container">
    <h1>Theme Preview: [Theme Name]</h1>

    <!-- ========== IMMER INKLUDIERT ========== -->

    <!-- COLOR PALETTE (immer) -->
    <h2>Farbpalette</h2>
    <div class="swatches">
      <div class="swatch" style="background: var(--color-primary);">
        <span class="swatch-label">Primary</span>
        <span class="swatch-hex">[primary-hex]</span>
      </div>
      <div class="swatch" style="background: var(--color-primary-light);">
        <span class="swatch-label">Primary Light</span>
        <span class="swatch-hex">[primary-light-hex]</span>
      </div>
      <div class="swatch" style="background: var(--color-primary-dark);">
        <span class="swatch-label">Primary Dark</span>
        <span class="swatch-hex">[primary-dark-hex]</span>
      </div>
      <div class="swatch" style="background: var(--color-accent);">
        <span class="swatch-label">Accent</span>
        <span class="swatch-hex">[accent-hex]</span>
      </div>
      <div class="swatch" style="background: var(--color-success);">
        <span class="swatch-label">Success</span>
        <span class="swatch-hex">#4caf50</span>
      </div>
      <div class="swatch" style="background: var(--color-warning);">
        <span class="swatch-label">Warning</span>
        <span class="swatch-hex">#ff9800</span>
      </div>
      <div class="swatch" style="background: var(--color-error);">
        <span class="swatch-label">Error</span>
        <span class="swatch-hex">#f44336</span>
      </div>
      <div class="swatch" style="background: var(--color-info);">
        <span class="swatch-label">Info</span>
        <span class="swatch-hex">#2196f3</span>
      </div>
    </div>

    <!-- TYPOGRAPHY (immer) -->
    <h2>Typografie</h2>
    <div class="card">
      <h1 style="margin-bottom: 0.5rem;">Heading 1 - Light 2.25rem</h1>
      <h2 style="margin: 0.5rem 0;">Heading 2 - Regular 1.5rem</h2>
      <h3 style="margin: 0.5rem 0;">Heading 3 - Medium 1.25rem</h3>
      <p style="margin-top: 1rem;">Body Text - Dies ist ein Beispieltext in der Standardschriftgröße. Die Schriftfamilie ist <strong>[theme-font]</strong>.</p>
      <p style="font-size: 0.875rem; color: var(--color-text-secondary); margin-top: 0.5rem;">Secondary Text - Kleinere Schrift für weniger wichtige Informationen.</p>
    </div>

    <!-- ========== BEDINGT: NUR WENN KOMPONENTE GEFUNDEN ========== -->

    <div class="grid-2">
      <!-- MINI TABLE (wenn mat-table ODER <table> gefunden) -->
      <div>
        <h2>Datentabelle</h2>
        <div class="card" style="padding: 0; overflow: hidden;">
          <table class="table">
            <thead>
              <tr>
                <th>Name</th>
                <th>Status</th>
                <th>Aktionen</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>Max Mustermann</td>
                <td><span class="chip chip-success">Aktiv</span></td>
                <td class="table-actions">
                  <button class="btn btn-icon btn-secondary"><span class="material-icons">edit</span></button>
                  <button class="btn btn-icon btn-danger"><span class="material-icons">delete</span></button>
                </td>
              </tr>
              <tr>
                <td>Erika Musterfrau</td>
                <td><span class="chip chip-warning">Ausstehend</span></td>
                <td class="table-actions">
                  <button class="btn btn-icon btn-secondary"><span class="material-icons">edit</span></button>
                  <button class="btn btn-icon btn-danger"><span class="material-icons">delete</span></button>
                </td>
              </tr>
              <tr>
                <td>John Doe</td>
                <td><span class="chip chip-error">Inaktiv</span></td>
                <td class="table-actions">
                  <button class="btn btn-icon btn-secondary"><span class="material-icons">edit</span></button>
                  <button class="btn btn-icon btn-danger"><span class="material-icons">delete</span></button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <!-- MINI FORM (wenn mat-form-field ODER <input>/<select> gefunden) -->
      <div>
        <h2>Formular</h2>
        <div class="card">
          <h3>Benutzer bearbeiten</h3>
          <form style="margin-top: var(--spacing-4);">
            <div class="form-row">
              <div class="form-group">
                <label class="form-label">Vorname</label>
                <input type="text" class="form-input" value="Max" />
              </div>
              <div class="form-group">
                <label class="form-label">Nachname</label>
                <input type="text" class="form-input" value="Mustermann" />
              </div>
            </div>
            <div class="form-group">
              <label class="form-label">E-Mail</label>
              <input type="email" class="form-input" value="max@example.com" />
            </div>
            <div class="form-group">
              <label class="form-label">Rolle</label>
              <select class="form-input form-select">
                <option>Administrator</option>
                <option selected>Benutzer</option>
                <option>Gast</option>
              </select>
            </div>
            <div class="form-actions">
              <button type="button" class="btn btn-secondary">Abbrechen</button>
              <button type="submit" class="btn btn-primary">
                <span class="material-icons">save</span> Speichern
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>

    <!-- BUTTONS (wenn mat-button ODER <button> gefunden) -->
    <h2>Buttons</h2>
    <div class="card" style="display: flex; gap: var(--spacing-4); flex-wrap: wrap; align-items: center;">
      <button class="btn btn-primary"><span class="material-icons">add</span> Primary</button>
      <button class="btn btn-secondary">Secondary</button>
      <button class="btn btn-danger"><span class="material-icons">delete</span> Danger</button>
      <button class="btn btn-primary" disabled style="opacity: 0.5; cursor: not-allowed;">Disabled</button>
    </div>
  </div>
</body>
</html>

Nach Erstellung:

code
THEME PREVIEW ERSTELLT: [Theme Name]

Öffnen Sie die Vorschau im Browser:
  open wireframes/theme-preview-[theme-name].html

Features:
- [X] Farbpalette mit allen Brand & Semantic Colors (immer)
- [X] Typografie-Beispiele (immer)
- [X] Dark Mode Toggle (immer)
- Projekt-spezifische Elemente (nur wenn im Projekt gefunden):
  - [ ] Datentabelle (mat-table ODER <table>)
  - [ ] Formular (mat-form-field ODER <input>/<select>)
  - [ ] Buttons (mat-button ODER <button>)
  - [ ] Listen (<ul>/<ol>)
  - [ ] Navigation (<nav>)
  - [ ] Cards, Chips, Dialogs, etc.

Nächste Schritte:
- Anderes Theme testen? → /byt8:ui-theming (Option 3)
- Dieses Theme übernehmen? → /byt8:ui-theming (Option 2 mit gleichem Theme)

Output

code
DESIGN SYSTEM INITIALIZED

Theme: [Selected Theme]
Primary: [primary-hex]
Accent: [accent-hex]
Font: [theme-font]

Files Created:
- [X] frontend/src/styles/tokens/_colors.scss
- [X] frontend/src/styles/tokens/_typography.scss
- [X] frontend/src/styles/tokens/_spacing.scss
- [X] frontend/src/styles/tokens/_elevation.scss
- [X] frontend/src/styles/tokens/_breakpoints.scss
- [X] frontend/src/styles/tokens/_index.scss
- [X] frontend/src/styles.scss (updated)
- [X] wireframes/theme-preview-[theme-name].html (Preview)

Architecture:
- CSS Custom Properties (runtime theming)
- @use/@forward module system (no @import)
- Light/Dark mode support
- 8pt spacing grid with semantic aliases
- Material Design elevations
- Responsive breakpoint mixins

Preview:
  open wireframes/theme-preview-[theme-name].html

Next Steps:
1. Preview öffnen und Theme prüfen (Light + Dark Mode)
2. Falls Theme nicht passt: /byt8:ui-theming erneut ausführen
3. Falls Theme passt: /byt8:full-stack-feature starten
4. In Components: var(--token-name) verwenden

When to Run

  • ONCE at project start
  • When changing the project theme
  • When updating design system tokens

Prerequisites

  • Frontend project initialized (npm install complete)