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.
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:
prefers-color-scheme is checked — the selection is never empty.theme-mode key.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.
---
// 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>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>