Theme System Usage Guide

Complete guide for using our theme system effectively - the right way to implement themes in components

Theme System Usage Guide

🎯 Quick Start - The Right Way

Our theme system is designed to be simple and powerful. No wrapper services needed - use the theme system directly!

✅ Simple Component Usage

import { Component, inject, computed } from '@angular/core';
import { ThemeManagerService, ThemeMode } from '@theme-runtime';

@Component({
  selector: 'app-my-component',
  template: `
    <div class="themed-component">
      <h2>{{ themeName() }}</h2>
      <button (click)="toggleMode()">Toggle Mode</button>
    </div>
  `,
  styles: [`
    .themed-component {
      background-color: var(--ux-surface);
      color: var(--ux-on-surface);
      padding: var(--ux-space-md);
      border-radius: var(--ux-radius-md);
    }
  `]
})
export class MyComponent {
  private themeManager = inject(ThemeManagerService);
  
  themeName = computed(() => this.themeManager.activeTheme()?.metadata?.name || 'Default');
  
  async toggleMode() {
    const current = this.themeManager.activeTheme()?.mode;
    const newMode = current === ThemeMode.LIGHT ? ThemeMode.DARK : ThemeMode.LIGHT;
    await this.themeManager.setThemeMode(newMode);
  }
}

✅ CSS Variables - Use Directly

.my-component {
  // ✅ Use CSS variables directly - they update automatically
  background-color: var(--ux-primary);
  color: var(--ux-on-primary);
  border: 1px solid var(--ux-border);
  border-radius: var(--ux-radius-md);
  padding: var(--ux-space-md);
  
  // ✅ Transitions work automatically
  transition: all var(--ux-transition-medium);
  
  &:hover {
    background-color: var(--ux-primary-dark);
    box-shadow: var(--ux-shadow-sm);
  }
}

🚫 What NOT to Do

❌ Don't Create Wrapper Services

// ❌ DON'T DO THIS
@Injectable()
export class UnnecessaryThemeWrapper {
  private themeManager = inject(ThemeManagerService);
  
  // This just duplicates existing functionality!
  getTheme() {
    return this.themeManager.activeTheme();
  }
}

❌ Don't Use Dynamic Imports for Core Services

// ❌ DON'T DO THIS
async loadThemeManager() {
  const themeSystem = await import('@theme-runtime');
  return this.injector.get(themeSystem.ThemeManagerService);
}

❌ Don't Manually Cache Theme Data

// ❌ DON'T DO THIS - The theme system handles caching
private cachedTheme: Theme | null = null;

getTheme() {
  if (!this.cachedTheme) {
    this.cachedTheme = this.themeManager.activeTheme();
  }
  return this.cachedTheme;
}

📋 Available CSS Variables

Our theme system provides these CSS variables automatically:

Colors

Spacing

Border Radius

Shadows

Typography

Transitions

🎯 When to Create a Service

Only create a theme-related service if you have:

  1. Complex business logic that needs to be shared across components
  2. Data transformation or processing requirements
  3. Integration with external systems
  4. Stateful operations that need coordination

✅ Example: When a Service Makes Sense

@Injectable({
  providedIn: 'root'
})
export class CustomThemeBusinessLogicService {
  private readonly themeManager = inject(ThemeManagerService);

  /**
   * ✅ EXAMPLE: Custom business logic that justifies a service
   * Calculate accessibility contrast ratios for the current theme
   */
  getAccessibilityReport(): Observable<AccessibilityReport> {
    return this.themeManager.themeChanged$.pipe(
      map(theme => this.calculateAccessibilityReport(theme)),
      distinctUntilChanged()
    );
  }

  /**
   * ✅ EXAMPLE: Complex theme transformation
   * Generate a complementary theme based on current theme
   */
  async generateComplementaryTheme(): Promise<Theme> {
    const currentTheme = this.themeManager.activeTheme();
    // Complex business logic here...
    return transformedTheme;
  }
}

🔧 Available Services

Core Services (Use These Directly)

Utility Services

🚀 Performance Tips

  1. Use CSS variables instead of JavaScript for styling
  2. Use computed signals for reactive theme data
  3. Avoid unnecessary service layers
  4. Let the theme system handle caching and updates
  5. Use the built-in reactivity instead of manual subscriptions

🛠️ Advanced Patterns

Reactive Theme Data

export class AdvancedThemeComponent {
  private themeManager = inject(ThemeManagerService);
  
  // ✅ Reactive theme properties
  currentTheme = computed(() => this.themeManager.activeTheme());
  isDarkMode = computed(() => this.currentTheme()?.mode === ThemeMode.DARK);
  primaryColor = computed(() => this.currentTheme()?.colors?.primary || '#4f46e5');
  
  // ✅ Listen to theme changes
  themeChanges$ = this.themeManager.themeChanged$;
}

Utility Functions

/**
 * Get a theme color with fallback
 */
export function getThemeColor(colorKey: string, fallback = '#000000'): string {
  return getComputedStyle(document.documentElement)
    .getPropertyValue(`--ux-${colorKey}`)
    .trim() || fallback;
}

/**
 * Check if current theme is dark mode
 */
export function isDarkMode(): boolean {
  return document.documentElement.getAttribute('data-theme-mode') === 'dark';
}

🆘 Troubleshooting

Theme not updating?

TypeScript errors?

Performance issues?

📚 Best Practices Summary

✅ DO THIS:

🚫 AVOID:


This guide ensures you use our theme system effectively and maintainably. Follow these patterns for consistent, performant theme integration.