@bquery/ui switches colour scheme with the data-theme attribute. Put it on
<html> for the whole page, or on any element to theme just that subtree.
<html data-theme="dark">
<!-- every bq-* element below uses dark tokens -->
</html>data-theme takes three values:
| Value | Meaning |
|---|---|
dark |
Dark, regardless of the operating system |
light |
Light, regardless of the operating system |
auto |
Follows prefers-color-scheme, live |
import { setColorScheme, getResolvedColorScheme } from '@bquery/ui/theme';
setColorScheme('auto'); // follow the OS
setColorScheme('dark'); // pin dark
setColorScheme('light'); // pin light
getResolvedColorScheme(); // 'light' | 'dark' — what is actually showingauto is resolved in CSS with a prefers-color-scheme media query, so a
system-level switch repaints the page immediately with no JavaScript listening
for the change — and there is no matchMedia subscription to leak.
Because the scheme is inherited, a themed island needs no API at all:
<body data-theme="light">
<aside data-theme="dark">
<bq-button>Dark sidebar</bq-button>
</aside>
</body>Each scheme also sets the CSS color-scheme property, so scrollbars, the
native date and colour pickers, and form-control defaults inside your page
match the theme instead of staying stubbornly light.
A component defines its tokens on its own :host, and a value set on an
element always beats one inherited from an ancestor — which is why setting
--bq-bg-base on <html> cannot re-theme a component on its own.
So components resolve every semantic token through a scheme channel:
:host {
--bq-bg-base: var(--bq-scheme-bg-base, #ffffff);
}--bq-scheme-bg-base is never defined on the host, so it resolves the ordinary
way — by inheritance from the document. A small stylesheet, installed
automatically the first time any component module is imported, defines the
channel for each data-theme value.
That stylesheet is plain CSS with no exotic selectors. The previous approach
relied on :host-context(), which exists only in Chromium; worse, it appeared
in a selector list alongside :host([data-theme="dark"]), and one unknown
selector invalidates the entire list — so Firefox and Safari dropped the whole
dark theme. The channel works in every browser.
If you need to install the stylesheet into another document — an iframe or a popup window — call it yourself:
import { installThemeStyles } from '@bquery/ui/theme';
installThemeStyles(iframe.contentDocument);Override any token for one scheme by writing the rule against the attribute:
[data-theme='dark'] {
--bq-color-primary-600: #818cf8;
--bq-surface-overlay: #111827;
}To override a token in both schemes, set it on :root instead — see
Theming.