block

Theme Mode Select

A pre-built block for switching between light and dark. Renders a button that opens a Menu whose radio group holds two options — Dark and Light — and persists the choice to localStorage. The button icon updates automatically to reflect the active mode.

Because it’s a menu radio group, the options come with roving focus, typeahead, a keyboard-only focus ring, and a check mark on the active mode for free — and picking one doesn’t dismiss the panel, so modes can be previewed in place.

Two options, three states

There’s deliberately no “System” row. Changing theme is a situational comfort fix — a page is too bright right now — not a standing declaration about how every future page should resolve, so asking users to reason about a third state surfaces the data model rather than the goal.

The third state still exists; it’s just reached by using the two that are visible:

  • The checked row is the mode on screen, not the stored preference. With nothing saved, the row matching the OS prefers-color-scheme is checked — the selection is never empty.
  • Picking the other mode stores an override under the theme-mode key.
  • Picking the mode the OS already prefers clears that key, so the page follows the OS again. Every choice stays reversible with one more click.

An override is only ever released at interaction time. If the OS flips to match a stored choice, the choice is kept — nothing is cleared behind the user’s back.

Default

---
// GENERATED from packages/demos/theme-mode-select/default.demo.tsx — edit the source, not this file.
import ThemeModeSelect from "@pindoba/astro-theme-mode-select";
import { flex } from "@pindoba/styled-system/patterns";
---

<div
  class={flex({
    justify: "center",
  })}
>
  <ThemeModeSelect />
</div>

Placement

The menu opens below the trigger and aligned to its end edge. placement takes any Floating UI placement — useful when the switch sits at the bottom of a sidebar or footer, where a downward panel would have nowhere to go.

---
// GENERATED from packages/demos/theme-mode-select/placement.demo.tsx — edit the source, not this file.
import ThemeModeSelect from "@pindoba/astro-theme-mode-select";
import { flex } from "@pindoba/styled-system/patterns";
---

<div
  class={flex({
    justify: "center",
  })}
>
  <ThemeModeSelect placement="top" />
</div>
  • Components
  • Blocks