Theming
The Theming service provides the color system used across BuilderKit. It enables dynamic theming with support for light and dark modes, generates CSS variables for primary, neutral, and error colors, and supplies the tokens that BuilderKit’s Angular Material styles are built on.
Overview
The service automatically creates CSS variables for every color scale and injects them into the DOM. These tokens are used throughout BuilderKit components and styles to ensure consistency, so the service is required for both custom component styles and the Angular Material styles.
- Single Source of Truth
- Three source colors define every generated palette.
- Light and Dark Palettes
- Generates a palette for light mode and one for dark mode from the same source colors.
- Scheme Classes
- Adds scheme-aware classes to the
<html>element. - System Scheme
- Supports system-based dark mode via media queries.
- Runtime Updates
- Allows dynamic updates to colors and schemes at runtime.
Provider Setup
Add provideTheming to the providers array of your application configuration. It requires provideWindow:
import { provideTheming } from '@/app/core/theming';
import { provideWindow } from '@/app/core/window';
export const appConfig: ApplicationConfig = {
providers: [
provideTheming({
scheme: 'light',
primary: '#3E63DD',
error: '#E5484D',
neutral: '#8B8D98',
}),
provideWindow(),
// ...other providers
],
};Configuration
borderRadius is optional. All other options are required.
| Option | Type | Description |
|---|---|---|
scheme | 'light' | 'dark' | 'system' | The initial color scheme. system follows the operating system preference. |
primary | string | Source color for the primary palette. |
error | string | Source color for the error palette. |
neutral | string | Source color for the neutral palette. |
borderRadius | string | Sets --theme-border-radius, the base corner radius that BuilderKit component styles use and derive their other radii from. Defaults to 0.5rem. |
Colors are parsed as CSS color strings. Hex (#3E63DD, #36d), rgb(), hsl(), oklch(), lab(), color(), and named colors such as royalblue are all accepted. A string that cannot be parsed throws when the palettes are generated.
Persistence
In the browser, the service stores the scheme and the three source colors in local storage under the keys scheme, primaryColor, errorColor, and neutralColor. They are written on startup and after every change, and read back on the next visit.
Stored values take precedence over the values passed to provideTheming. Since they are written on a visitor’s first visit, changing the configuration in code does not change what that visitor sees until the stored values are cleared or overwritten.
Generated CSS Variables
The service writes a <style class="theme-colors"> element into the document head that defines these variables on :root:
--theme-color-primary-1to--theme-color-primary-12, alpha steps--theme-color-primary-a1to--theme-color-primary-a12, plus--theme-color-primary-surfaceand--theme-color-primary-contrast- The same set for
error:--theme-color-error-* - The same steps and
surfaceforneutral, withoutcontrast --theme-border-radius
Every color value is a light-dark() pair of OKLCH colors, so it follows the active color scheme. BuilderKit’s Tailwind theme maps the variables to color tokens such as --color-primary-9, so they are available as utility classes. The unnumbered primary, error, and neutral tokens point to step 9:
<button class="bg-primary text-primary-contrast">Save</button>
<div class="border border-neutral-a6 bg-neutral-2 text-neutral-11">
Card content
</div>Color Scheme
The service adds scheme-light or scheme-dark to the <html> element. With the system scheme, the class follows the operating system’s prefers-color-scheme preference and updates when it changes. The same classes can be added to any element to give that region its own scheme.
The isDark signal is true when the resolved scheme is dark, which is useful for libraries that take their own theme option:
private theming = inject(Theming);
private chartTheme = computed(() => (this.theming.isDark() ? 'dark' : 'light')); The palettes signal holds the current source colors under source and the generated light and dark palettes, each with the primary, error, and neutral scales behind the CSS variables above.
Dynamic Scheme Updates
The current color scheme can be accessed and updated at runtime using the Theming service:
private theming = inject(Theming);
updateScheme(scheme: Scheme) {
this.theming.scheme.set(scheme);
}Dynamic Theme Updates
The entire theme can also be accessed and updated dynamically at runtime. set() takes all three colors:
private theming = inject(Theming);
updateTheme(theme: Theme) {
this.theming.theme.set(theme);
}To change a single color, use update():
updatePrimary(primary: string) {
this.theming.theme.update((theme) => ({ ...theme, primary }));
}Server Rendering
During server rendering, the service writes the theme style element from the values passed to provideTheming, since a visitor’s stored values are only available in the browser. The scheme class is not added to <html> and nothing is stored. In the browser, the service reads the stored values, rewrites the same style element, and adds the scheme class, so a visitor whose stored values differ from the configuration first receives the configured theme.