Services & Advanced Theming
Super-JSS provides services for advanced theming scenarios, including runtime theme switching, dynamic updates, and signal-based reactive theming.
SjThemeService
The SjThemeService manages theme state and provides methods for dynamic theming.
Basic Usage
import { SjThemeService } from 'super-jss';
@Component({...})
export class ThemeSwitcherComponent {
constructor(private themeService: SjThemeService) {}
switchTheme(theme: SjTheme) {
this.themeService.setTheme(theme);
}
getCurrentTheme() {
return this.themeService.getCurrentTheme();
}
}
Reactive Theme Changes
import { SjThemeService } from 'super-jss';
import { signal, computed } from '@angular/core';
@Component({...})
export class ReactiveThemeComponent {
private themeService = inject(SjThemeService);
// Reactive theme state
currentTheme = signal<SjTheme>(this.themeService.getCurrentTheme());
// Computed values based on theme
primaryColor = computed(() => this.currentTheme().palette.primary.main);
constructor() {
// Subscribe to theme changes
this.themeService.theme$.subscribe(theme => {
this.currentTheme.set(theme);
});
}
updatePrimaryColor(color: string) {
const newTheme = {
...this.currentTheme(),
palette: {
...this.currentTheme().palette,
primary: {
...this.currentTheme().palette.primary,
main: color
}
}
};
this.themeService.setTheme(newTheme);
}
}
Signal-Based Components
Modern Angular applications can use signals for reactive theming:
import { signal, computed, effect } from "@angular/core";
import { SjThemeService } from "super-jss";
@Component({
template: `
<div [sj]="buttonStyle()">
<button [sj]="textStyle()">Themed Button</button>
</div>
`,
})
export class SignalThemedComponent {
private themeService = inject(SjThemeService);
// Signal for current theme
theme = signal(this.themeService.getCurrentTheme());
// Computed styles based on theme
buttonStyle = computed(() => [
sj.backgroundColor(this.theme().palette.primary.main),
sj.padding("12px"),
sj.borderRadius("8px"),
]);
textStyle = computed(() => [
sj.color(this.theme().palette.primary.contrast),
sj.fontWeight("bold"),
]);
constructor() {
// Update theme signal when service changes
effect(() => {
const currentTheme = this.themeService.getCurrentTheme();
this.theme.set(currentTheme);
});
}
}
Theme Overrides at Component Level
Override themes for specific components or sections:
@Component({
providers: [
{
provide: SJ_THEME_PROVIDER,
useValue: customComponentTheme,
},
],
})
export class CustomThemedComponent {
// This component and its children use customComponentTheme
}
Advanced Patterns
Theme Variants
// Define theme variants
const themes = {
light: lightTheme,
dark: darkTheme,
highContrast: highContrastTheme
};
@Component({...})
export class AppComponent {
currentVariant = signal<'light' | 'dark' | 'highContrast'>('light');
switchVariant(variant: keyof typeof themes) {
this.currentVariant.set(variant);
this.themeService.setTheme(themes[variant]);
}
}
Conditional Theming
@Component({...})
export class ConditionalComponent {
theme = signal(this.themeService.getCurrentTheme());
// Different styles based on theme properties
containerStyle = computed(() => {
const isDark = this.theme().palette.mode === 'dark';
return [
sj.padding('16px'),
sj.backgroundColor(
isDark
? this.theme().palette.grey[900]
: this.theme().palette.grey[100]
)
];
});
}
Performance Considerations
- Signal Updates: Use signals for reactive theme changes to minimize re-renders
- Memoization: Computed signals automatically memoize theme-derived values
- Lazy Loading: Load theme variants on demand for better initial bundle size
- Change Detection: Theme changes trigger minimal component updates
Best Practices
✅ Do: Use Signals for Reactivity
// Good: Reactive theme updates
theme = signal(initialTheme);
styles = computed(() => /* derive from theme */);
❌ Don't: Direct Theme Access
// Avoid: Direct service calls in templates
<div [sj]="themeService.getCurrentTheme().palette.primary.main">
Avoid this pattern
</div>
✅ Do: Component-Level Overrides
// Good: Scoped theme overrides
@Component({
providers: [{ provide: SJ_THEME_PROVIDER, useValue: scopedTheme }],
})
export class ScopedComponent {}
Migration from Traditional Theming
When migrating from CSS variables or traditional theming:
- Identify Theme Values: Map existing CSS variables to theme structure
- Create Theme Object: Define your theme using the SjTheme interface
- Update Components: Replace CSS variable usage with
sj.*functions - Add Reactivity: Use signals for dynamic theme switching
- Test Thoroughly: Ensure all theme variants work correctly
Next Steps
- Core Directive - Back to directive basics
- SJ API Theming & Tokens - Using theme tokens effectively