Dark Mode

Bloom ships full dark mode support out of the box. Color mode is managed by ThemeProvider and exposed through the useTheme hook.

Basic setup

Wrap your app with ThemeProvider and set a defaultColorMode. The "system" value automatically follows the user's OS preference:

import { ThemeProvider } from "@bloomkit/react";
 
export default function App({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider defaultColorMode="system">
      {children}
    </ThemeProvider>
  );
}

Other valid values are "light" and "dark" if you want to start in a specific mode.

useTheme hook

The useTheme hook gives you full control over the active color mode and palette from any component:

PropertyTypeDescription
colorMode"light" | "dark" | "system"The current user-facing setting
resolvedMode"light" | "dark"The actual applied mode (never "system")
setColorMode(mode) => voidExplicitly set the color mode
toggleColorMode() => voidToggle between light and dark
palettestringThe name of the currently active palette
setPalette(name) => voidSwitch to a different palette by name
palettesstring[]All available palette names

Building a theme toggle

Use toggleColorMode and resolvedMode to build a simple toggle button:

import { useTheme } from "@bloomkit/react";
 
export function ThemeToggle() {
  const { resolvedMode, toggleColorMode } = useTheme();
 
  return (
    <button
      onClick={toggleColorMode}
      aria-label={resolvedMode === "dark" ? "Switch to light mode" : "Switch to dark mode"}
    >
      {resolvedMode === "dark" ? "Light mode" : "Dark mode"}
    </button>
  );
}

If you need a three-way picker (light / dark / system), use colorMode and setColorMode instead:

import { useTheme } from "@bloomkit/react";
 
const modes = ["light", "dark", "system"] as const;
 
export function ColorModePicker() {
  const { colorMode, setColorMode } = useTheme();
 
  return (
    <div role="group" aria-label="Color mode">
      {modes.map((mode) => (
        <button
          key={mode}
          onClick={() => setColorMode(mode)}
          aria-pressed={colorMode === mode}
        >
          {mode}
        </button>
      ))}
    </div>
  );
}

OS preference detection

When defaultColorMode is set to "system", Bloom listens to the prefers-color-scheme media query and applies light or dark tokens automatically. No additional setup is required — this works out of the box.

resolvedMode always reflects the actual applied value, so you can safely use it to render icons or labels without worrying about "system" appearing as a value.

localStorage persistence

ThemeProvider persists the user's color mode choice to localStorage under the key bloom-color-mode. On the next page load the stored preference is read before the first render, preventing a flash of the wrong theme.

If defaultColorMode="system" and the user has never explicitly picked a mode, no value is written to storage and the OS preference continues to be used.

Manual approach (without ThemeProvider)

If you prefer to manage color mode yourself — or are using Bloom inside a larger app that already has its own theme system — you can apply dark mode manually by adding a dark class or data-theme="dark" attribute to your root element:

<!-- class-based -->
<html class="dark">
 
<!-- attribute-based -->
<html data-theme="dark">

Bloom's stylesheet includes .dark and [data-theme="dark"] selectors that override all color tokens to their dark-mode values. Toggle the class or attribute however you like and Bloom will respond accordingly.