Skip to content

Dynamic Theme Management

Learn to create and switch between themes dynamically at runtime.

Basic Theme System

Create a simple theme manager:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class ThemeManager {
  private static currentTheme: "light" | "dark" = "light";

  static themes = {
    light: {
      backgroundColor: "#ffffff",
      color: "#000000",
      borderColor: "#e0e0e0",
      accentColor: "#0066cc"
    },
    dark: {
      backgroundColor: "#1e1e1e",
      color: "#ffffff",
      borderColor: "#444444",
      accentColor: "#66b3ff"
    }
  };

  static setTheme(theme: "light" | "dark") {
    this.currentTheme = theme;
    const colors = this.themes[theme];

    // Apply to document root
    const style = new InlineStyle({
      "--bg-color": colors.backgroundColor,
      "--text-color": colors.color,
      "--border-color": colors.borderColor,
      "--accent-color": colors.accentColor
    } as any);

    style.applyTo(document.documentElement);
  }

  static getTheme() {
    return this.currentTheme;
  }
}

// Usage
ThemeManager.setTheme("dark");

Live demo of theme switching:

Console
No logs yet.

Multi-Color Theme Palette

Define comprehensive color palettes:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class ColorPalette {
  static palettes = {
    ocean: {
      primary: "#0077be",
      secondary: "#00d4ff",
      success: "#06a77d",
      warning: "#ffc857",
      error: "#d62828",
      neutral: "#f5f5f5"
    },
    forest: {
      primary: "#2d6a4f",
      secondary: "#40916c",
      success: "#52b788",
      warning: "#d4a574",
      error: "#a4243b",
      neutral: "#f1faee"
    },
    sunset: {
      primary: "#e76f51",
      secondary: "#f4a261",
      success: "#2a9d8f",
      warning: "#f4a261",
      error: "#d62828",
      neutral: "#fffcf2"
    }
  };

  static applyPalette(paletteKey: keyof typeof this.palettes) {
    const palette = this.palettes[paletteKey];

    // Create CSS custom properties
    const vars: Record<string, string> = {};
    Object.entries(palette).forEach(([key, value]) => {
      vars[`--color-${key}`] = value;
    });

    new InlineStyle(vars as any).applyTo(document.documentElement);
  }
}

// Usage
ColorPalette.applyPalette("ocean");

Component-Level Theming

Apply themes to specific components:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class ComponentTheme {
  static getButtonStyle(variant: "primary" | "secondary" | "danger") {
    const variants = {
      primary: {
        backgroundColor: "#0066cc",
        color: "#ffffff",
        borderColor: "#0052a3"
      },
      secondary: {
        backgroundColor: "#f0f0f0",
        color: "#333333",
        borderColor: "#cccccc"
      },
      danger: {
        backgroundColor: "#dc3545",
        color: "#ffffff",
        borderColor: "#c82333"
      }
    };

    return new InlineStyle({
      padding: "10px 20px",
      border: "1px solid",
      borderRadius: "4px",
      cursor: "pointer",
      fontSize: "14px",
      fontWeight: "500",
      transition: "all 0.2s ease",
      ...variants[variant]
    });
  }

  static applyButtonTheme(button: HTMLElement, variant: "primary" | "secondary" | "danger") {
    this.getButtonStyle(variant).applyTo(button);

    // Add hover effect
    button.addEventListener("mouseenter", () => {
      const variants = {
        primary: { backgroundColor: "#0052a3" },
        secondary: { backgroundColor: "#e0e0e0" },
        danger: { backgroundColor: "#c82333" }
      };

      new InlineStyle({
        ...variants[variant],
        transform: "translateY(-2px)",
        boxShadow: "0 4px 12px rgba(0,0,0,0.15)"
      }).applyTo(button);
    });

    button.addEventListener("mouseleave", () => {
      this.getButtonStyle(variant).applyTo(button);
    });
  }
}

// Usage
document.querySelectorAll(".btn-primary").forEach(btn => {
  if (btn instanceof HTMLElement) {
    ComponentTheme.applyButtonTheme(btn, "primary");
  }
});

System Preference Detection

Respect user's system theme preference:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class SystemTheme {
  static getSystemTheme(): "light" | "dark" {
    return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
  }

  static applySystemTheme() {
    const theme = this.getSystemTheme();

    const themes = {
      light: {
        backgroundColor: "#ffffff",
        color: "#000000"
      },
      dark: {
        backgroundColor: "#121212",
        color: "#ffffff"
      }
    };

    new InlineStyle(themes[theme]).applyTo(document.body);

    // Listen for system changes
    window.matchMedia("(prefers-color-scheme: dark)").addListener((e) => {
      const newTheme = e.matches ? "dark" : "light";
      new InlineStyle(themes[newTheme]).applyTo(document.body);
    });
  }
}

// Usage
SystemTheme.applySystemTheme();

LocalStorage Theme Persistence

Save user's theme preference:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class PersistentTheme {
  private static STORAGE_KEY = "app-theme";

  static getStoredTheme(): "light" | "dark" {
    const stored = localStorage.getItem(this.STORAGE_KEY);
    return (stored as "light" | "dark") || "light";
  }

  static saveTheme(theme: "light" | "dark") {
    localStorage.setItem(this.STORAGE_KEY, theme);
  }

  static applyStoredTheme() {
    const theme = this.getStoredTheme();
    this.applyTheme(theme);
  }

  static applyTheme(theme: "light" | "dark") {
    const styles = {
      light: { backgroundColor: "#ffffff", color: "#000000" },
      dark: { backgroundColor: "#1e1e1e", color: "#ffffff" }
    };

    new InlineStyle(styles[theme]).applyTo(document.body);
  }

  static toggleTheme() {
    const current = this.getStoredTheme();
    const next = current === "light" ? "dark" : "light";
    this.applyTheme(next);
    this.saveTheme(next);
  }
}

// Initialize on page load
document.addEventListener("DOMContentLoaded", () => {
  PersistentTheme.applyStoredTheme();

  // Toggle button listener
  document.querySelector(".theme-toggle")?.addEventListener("click", () => {
    PersistentTheme.toggleTheme();
  });
});

Animated Theme Transitions

Smoothly transition between themes:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class AnimatedTheme {
  static transitionDuration = 300; // milliseconds

  static applyThemeWithAnimation(theme: "light" | "dark") {
    const element = document.documentElement;

    // Add transition
    new InlineStyle({
      transition: `background-color ${this.transitionDuration}ms ease, color ${this.transitionDuration}ms ease`
    }).applyTo(element);

    // Apply theme
    const themes = {
      light: { backgroundColor: "#ffffff", color: "#000000" },
      dark: { backgroundColor: "#1e1e1e", color: "#ffffff" }
    };

    new InlineStyle(themes[theme]).applyTo(element);

    // Remove transition after completion
    setTimeout(() => {
      new InlineStyle({
        transition: "none"
      }).applyTo(element);
    }, this.transitionDuration);
  }
}

// Usage
document.querySelector(".theme-btn")?.addEventListener("click", () => {
  AnimatedTheme.applyThemeWithAnimation("dark");
});

Context-Based Theming

Different themes for different sections:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class RegionalThemes {
  static themes = {
    marketing: {
      primary: "#ff6b6b",
      background: "#fff5f5",
      text: "#1a1a1a"
    },
    dashboard: {
      primary: "#0066cc",
      background: "#f5f5f5",
      text: "#000000"
    },
    admin: {
      primary: "#2d3436",
      background: "#2f3542",
      text: "#ffffff"
    }
  };

  static applyRegionalTheme(
    section: HTMLElement,
    themeKey: keyof typeof this.themes
  ) {
    const theme = this.themes[themeKey];

    new InlineStyle({
      "--primary-color": theme.primary,
      "--bg-color": theme.background,
      "--text-color": theme.text
    } as any).applyTo(section);
  }
}

// Usage
const marketingSection = document.querySelector(".marketing-section");
const dashboardSection = document.querySelector(".dashboard-section");

if (marketingSection instanceof HTMLElement) {
  RegionalThemes.applyRegionalTheme(marketingSection, "marketing");
}

if (dashboardSection instanceof HTMLElement) {
  RegionalThemes.applyRegionalTheme(dashboardSection, "dashboard");
}

Advanced Theme Composition

Combine and override themes:

typescript
import { InlineStyle } from "@briklab/lib/stylesheet";

class ComposedTheme {
  static baseTheme = {
    fontSize: "16px",
    lineHeight: "1.5",
    borderRadius: "4px"
  };

  static colorThemes = {
    light: {
      backgroundColor: "#ffffff",
      color: "#000000"
    },
    dark: {
      backgroundColor: "#1e1e1e",
      color: "#ffffff"
    }
  };

  static densityThemes = {
    comfortable: { padding: "16px", gap: "12px" },
    compact: { padding: "8px", gap: "6px" }
  };

  static getComposedTheme(
    colorTheme: "light" | "dark",
    densityTheme: "comfortable" | "compact"
  ) {
    return new InlineStyle({
      ...this.baseTheme,
      ...this.colorThemes[colorTheme],
      ...this.densityThemes[densityTheme]
    });
  }

  static applyComposedTheme(
    element: HTMLElement,
    colorTheme: "light" | "dark",
    densityTheme: "comfortable" | "compact"
  ) {
    this.getComposedTheme(colorTheme, densityTheme).applyTo(element);
  }
}

// Usage
const layout = document.querySelector(".app-layout");
if (layout instanceof HTMLElement) {
  ComposedTheme.applyComposedTheme(layout, "dark", "compact");
}

Next Steps