Bootstrap 5.3 ships a built-in color-mode system based on the data-bs-theme attribute. @mintplayer/ng-bootstrap adds a tiny service on top that owns the user's preference, persists it, and resolves auto via prefers-color-scheme — and exposes it as Angular signals.
data-bs-theme on <html>. BsThemeService does this for you.@importing the library's bundle.--bs-* CSS variables via document.documentElement.style.setProperty(...).Bootstrap's design tokens are SCSS variables. Override them before importing the library's SCSS bundle to bake your palette into the compiled CSS — no runtime cost. This is the right approach when your brand colors are fixed.
All standard Bootstrap variables ($primary, $body-bg, $navbar-padding-y, …) are available — see the Bootstrap Sass reference for the full list.
Bootstrap also exposes most tokens as --bs-* CSS custom properties. Mutating them at runtime lets you build features like a per-user theme picker without re-deploying. The components pick up the change on the next paint.
What changes when you Apply: every component below uses --bs-primary — the swatch, the buttons, the alert, the link, and the progress bar all swap in lockstep with the CSS variable.
.btn-primary and .btn-outline-primary. Vanilla Bootstrap 5.3 bakes literal color values into each button variant's scoped --bs-btn-* variables — so mutating --bs-primary at runtime does not reach buttons in a stock Bootstrap project. @mintplayer/ng-bootstrap ships a small override that re-binds .btn-primary and .btn-outline-primary to var(--bs-primary) (with color-mix() for hover / active shades), which is why the buttons above swap with the rest. If you want the same behavior for .btn-success, .btn-danger, etc., add an analogous block in your own stylesheet.
Each Bootstrap component also defines its own scoped --bs-* variables. Tweaking these lets you restyle one component without touching the global palette. A few common ones:
| Component | Variable | Effect |
|---|---|---|
| Navbar | --bs-navbar-color | Default link/text color |
| Navbar | --bs-navbar-active-color | Active link color |
| Navbar | --bs-navbar-brand-color | Brand text color |
| Card | --bs-card-bg | Card surface color |
| Card | --bs-card-border-color | Card border |
| Card | --bs-card-cap-bg | Card header / footer surface |
| Dropdown | --bs-dropdown-bg | Dropdown menu surface |
| Dropdown | --bs-dropdown-link-hover-bg | Hovered item background |
| Modal | --bs-modal-bg | Modal surface |
| Modal | --bs-modal-header-border-color | Header separator |
| Button | --bs-btn-bg | Button surface (scope to a selector) |
| Button | --bs-btn-border-color | Button border |
| Body / global | --bs-body-bg | Page background |
| Body / global | --bs-body-color | Default text color |
| Body / global | --bs-border-color | Default border color |
| Body / global | --bs-primary / --bs-primary-rgb | Primary brand color (pair both for opacity-aware usage) |
The canonical list is component-specific and may shift between Bootstrap versions — see the CSS variables reference for the authoritative table.
BsThemeService is a signal-first service that owns the user's chosen mode and writes the resolved value to <html data-bs-theme>. auto resolves to light or dark via matchMedia('(prefers-color-scheme: dark)') and live-updates when the OS preference changes.
mode: auto effectiveMode: light To make <bs-navbar> follow the page theme automatically, pass [color]="'body-tertiary'". The navbar emits bg-body-tertiary (no data-bs-theme override) and Bootstrap's --bs-tertiary-bg CSS variable swaps with the page theme — giving a slightly tinted, theme-aware navbar in both light and dark.
[color] accepts either a Color enum value (Color.dark, Color.primary, …) or a string utility-class suffix ('body-tertiary', 'body-secondary'). Enum values also pin the navbar's own theme via data-bs-theme; string suffixes let the page theme cascade through. null emits no background — fully transparent navbar that inherits everything from the page.
The mode string is open-ended. Author a custom [data-bs-theme="…"] block in your global SCSS, then activate it via setMode('your-variant'). Known values (auto, light, dark) keep autocomplete; arbitrary strings also compile.
prefers-color-scheme doesn't know about custom variants — they must be set explicitly. Switching to auto while the user is on a custom variant returns them to the system preference.
Server-rendered HTML has no data-bs-theme attribute by default — which means dark-mode users see a brief light-mode flash before the Angular service runs. Fix it with a tiny pre-boot<script> in <head>, before any <link rel="stylesheet">. The script reads localStorage and prefers-color-scheme, then writes the attribute synchronously — before the browser has resolved any CSS.
'bs-theme-mode') must match the BS_THEME_STORAGE_KEY constant exported by @mintplayer/ng-bootstrap/theming. The script can't import the constant because it runs before any module loads — keep the two literals in lockstep.
theme.modeSignal<'auto' | 'light' | 'dark' | string> — the user's authored preference. Read-only; use setMode() to change. theme.effectiveModeSignal<'light' | 'dark' | string> — the resolved value. auto resolves to light or dark via matchMedia('(prefers-color-scheme: dark)'); explicit values pass through unchanged. theme.setMode(m)<html data-bs-theme>. BS_THEME_STORAGE_KEY'bs-theme-mode') used by the service. Exposed for the pre-boot script.