useTheme

Read and set the app theme through the same cookie and class the pre-paint script owns.

With theme: 'system' the framework already emits a <head> script that reads the voltro:theme cookie (falling back to prefers-color-scheme) and toggles .dark on <html> before first paint. What was missing was the in-app switcher — so apps layered a second theme system on top, and then two writers raced on one class attribute: a flash on navigation, and a stored preference that disagreed with the rendered class. useTheme is that switcher, reading and writing exactly the cookie and class the pre-paint script already uses.

import { useTheme } from '@voltro/web'

const { theme, resolvedTheme, setTheme } = useTheme()
<button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}>

theme is the stored preference — 'light' | 'dark' | 'system', where 'system' means no explicit choice. resolvedTheme is what is actually applied right now ('light' | 'dark'), resolving 'system' against matchMedia, and it stays live when the OS flips. setTheme persists the cookie and applies the class immediately; setTheme('system') clears the cookie and falls back to the OS.

Requires theme: 'system' in the web app.config.ts — that is what emits the pre-paint script. Do not also install a third-party theme provider.