Multi Theming

Themer is not limited to just light and dark. You can define as many theme values as your app needs.

<ThemeProvider themes={["light", "dark", "ocean", "forest", "sunset"]}>
  <App />
</ThemeProvider>

Defining custom themes

Use the themes prop on ThemeProvider to define the allowed theme names:

<ThemeProvider
  themes={["light", "dark", "ocean", "forest", "sunset"]}
  defaultTheme="ocean"
>
  <App />
</ThemeProvider>

Each value in themes becomes a valid theme that can be selected through setTheme.

Styling custom themes

By default, Themer writes the active theme name to the root element as a class.

:root.ocean {
  --bg: #041c32;
  --fg: #ecf8ff;
}

:root.forest {
  --bg: #102418;
  --fg: #e9f7e9;
}

:root.sunset {
  --bg: #3d1f1f;
  --fg: #fff1e6;
}

You can also use a data attribute instead:

<ThemeProvider
  attribute="data-theme"
  themes={["light", "dark", "ocean", "forest", "sunset"]}
>
  <App />
</ThemeProvider>
[data-theme="ocean"] {
  --bg: #041c32;
}

Building a theme switcher

The themes array returned by useTheme makes it easy to render a picker dynamically:

import { useTheme } from "@lonik/themer";

export function ThemePicker() {
  const { theme, themes, setTheme } = useTheme();

  return (
    <select value={theme} onChange={(e) => setTheme(e.target.value)}>
      {themes.map((themeName) => (
        <option key={themeName} value={themeName}>
          {themeName}
        </option>
      ))}
    </select>
  );
}

Using custom DOM values

If you want your app state to use one set of names and your DOM to use another, use the value prop:

<ThemeProvider
  themes={["light", "dark", "ocean"]}
  value={{
    light: "theme-light",
    dark: "theme-dark",
    ocean: "theme-ocean",
  }}
>
  <App />
</ThemeProvider>

This keeps theme === "ocean" in React state while applying theme-ocean to the root element.

Choosing a default theme

Set defaultTheme to the theme you want to use when the user has not selected one yet:

<ThemeProvider
  themes={["light", "dark", "ocean", "forest"]}
  defaultTheme="forest"
>
  <App />
</ThemeProvider>

Make sure defaultTheme matches one of your configured theme names.

System theme with multiple themes

If enableSystem is true, useTheme().themes also includes "system".

That works best when your theme set includes light and dark, because the system preference resolves to one of those two values.

If your app only uses custom names such as ocean, forest, and sunset, you will usually want to disable system themes:

<ThemeProvider
  themes={["ocean", "forest", "sunset"]}
  defaultTheme="ocean"
  enableSystem={false}
>
  <App />
</ThemeProvider>

color-scheme note

The browser color-scheme style only understands light and dark modes.

That means built-in browser UI such as form controls will automatically track light and dark, but fully custom theme names do not map to a browser color scheme by themselves.