Dark mode
Dark mode is a single prop. Set colorMode on the provider and every
component below it reads the dark token set — no per-component styling.
How it works
BootstrapStyledProvider renders a wrapper element with a data-bs-theme attribute set
to the active colorMode. The library’s tokens are defined twice: the light values on
the root, and a [data-bs-theme='dark'] block that overrides body background, text,
borders, and the theme-color emphasis tokens. Components read the tokens through
var(--bs-…), so flipping the attribute re-themes the whole subtree instantly.
<BootstrapStyledProvider colorMode="dark">
<App />
</BootstrapStyledProvider> Because the switch is attribute-scoped, you can nest a provider to make one region dark
inside an otherwise-light app — the inner data-bs-theme wins for its subtree.
Live toggle
The demo below drives colorMode from React state. Press the button — the card, its
text, borders, and controls all flip because they read the mode tokens:
Driving the mode
colorMode is a controlled prop — you own the value. A common pattern stores the
preference and seeds it from the OS setting:
import { useState } from 'react';
import { BootstrapStyledProvider } from '@metatoy/bootstrap-styled';
function initialMode(): 'light' | 'dark' {
const saved = localStorage.getItem('theme');
if (saved === 'light' || saved === 'dark') return saved;
return matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
export default function App() {
const [mode, setMode] = useState(initialMode);
const toggle = () => {
const next = mode === 'dark' ? 'light' : 'dark';
setMode(next);
localStorage.setItem('theme', next);
};
return (
<BootstrapStyledProvider colorMode={mode}>
<button onClick={toggle}>Toggle theme</button>
{/* … */}
</BootstrapStyledProvider>
);
} To avoid a flash of the wrong theme on first paint, set data-bs-theme on the document
element from an inline script before React hydrates, and seed colorMode from the same
source.
Portaled overlays
Modals, dropdowns, tooltips, and popovers render through a portal into document.body,
outside the provider wrapper. The library re-establishes the token scope on the portal
root automatically via useColorMode, so overlays match the active mode — you do not
need to do anything.
Reading the mode
Need the current mode inside your own components? Call the useColorMode hook:
import { useColorMode } from '@metatoy/bootstrap-styled';
function ModeLabel() {
const mode = useColorMode(); // 'light' | 'dark'
return <span>Current mode: {mode}</span>;
} See Color modes for the token-level detail on what changes between light and dark.