useGdTheme
The light/dark selector. Three states rather than two, because a two-state toggle silently overrides the operating system on first paint — which is the thing someone setting dark mode at 11pm is trying to avoid.
Anatomy
- choice
system- resolved
light- label
- System
- next
- light
Code
const { choice, resolved, toggle, icon, label, next } = useGdTheme()Three states, and a script
- “System” is a real state, not the absence of one. It follows the OS; light and dark PIN the choice by stamping
data-gd-theme. A two-state toggle has no way to say “go back to following the OS”. - The boot script is not optional on a prerendered site. Reading the choice on mount is reading it after hydration, and after hydration is after paint — so a dark-pinned visitor sees the light HTML flash.
GdAppinjects it; nothing else needs to. - “System” needs no script at all. The stylesheet’s media query decides before paint by construction. Only a pin has to be stamped early.
- Reach for
resolvedwhen something must PAINT itself — a map canvas picking a basemap cannot read a CSS variable, and needs the answer withsystemalready collapsed. - Name the control by what it will DO.
nextexists for exactly that: “Switch to dark”, not “Theme”.
API
Returns
Props
choiceRef<"system" | "light" | "dark">—What the user picked. `system` is the default and follows the OS.
resolvedComputedRef<"light" | "dark">—The theme actually in force, with `system` already resolved — for a canvas that must paint itself and cannot read a CSS variable.
toggle() => void—Advance to the next choice: system → light → dark → system.
iconComputedRef<string>—The glyph path for the current choice, ready for `GdIcon`'s `d`.
labelComputedRef<string>—The current choice as a word — "System", "Light", "Dark".
nextComputedRef<string>—The NEXT choice, lowercased, for an accessible name that says what the control will do: "Switch to dark".
Also exported
Props
GD_THEME_BOOTstring—The pre-paint boot script. Inject it into `<head>` at `critical` priority — `GdApp` already does. Without it a pinned dark page renders its light HTML first, every refresh.
GdThemeToggle is this composable with a button around it, and GdApp is where the boot script is injected.