# Theme toggle

> A light/dark switch, with the inline script that keeps a stored choice from flashing on the way in.

sitelo-ui resolves dark mode from `prefers-color-scheme` on its own — a site that is happy following the operating system needs nothing on this page. The toggle is for letting a reader override that.

It is one of the five components that need a script, because the choice lives in `localStorage` and only a script can read it. The button fetches that script itself, on the first press.

## Setting it up

Two things in the head, and the button wherever it belongs:

`src/index.ht.js`

```javascript
import { styles, themeScript, themeToggle } from 'sitelo/ui'

head(
  themeScript(), // applies the stored choice before the first paint
  styles(),
)

body(
  appBar({ brand: 'My site' },
    appBarSpacer(),
    appBarActions(themeToggle()),
  ),
)
```

There is no third file. `themeScript()` is blocking and inline on purpose — anything deferred paints first, which is exactly the dark flash it exists to prevent — and the flip itself rides on the button:

`Rendered markup`

```html
<button data-su-theme-toggle
        onclick="import('/su/theme.js').then(m=>m.toggle(this))">
```

Pair the two. `themeScript()` is also what marks the toggle `aria-pressed` on load: nothing has been pressed yet, so the button itself cannot know which theme resolved.

## The toggle

The icon is pure CSS, read straight off the theme attribute — so it is already correct on the first paint, before any script runs. It shows what a click will switch `to`.

```javascript
stack({ direction: 'row', gap: 'md', align: 'center' },
  themeToggle(),
  themeToggle({ variant: 'soft' }),
  themeToggle({ variant: 'outline' }),
)
```

Those buttons work — this page loads the runtime. Clicking one sets `data-su-theme` on `<html>`, which is sitelo-ui’s own attribute, so only the sitelo-ui components on this page change. The rest of this site follows its own `data-theme`, set by the toggle in the top bar. On your site there would be only one of them.

## In an app bar

```javascript
appBar({ brand: 'sitelo' },
  appBarNav(navLink({ href: '#docs', current: true }, 'Docs')),
  appBarSpacer(),
  appBarActions(
    themeToggle(),
    button({ size: 'sm' }, 'Get started'),
  ),
)
```

## How the theme resolves

In order: an explicit `data-theme` or `data-su-theme` on any ancestor wins; failing that, `prefers-color-scheme` decides. Both attribute names are honoured so sitelo-ui can sit inside a site that already has its own theme switch — which is exactly what these docs do.

## Driving it yourself

The runtime exports the same functions the button uses, for a custom control, or a three-way light / dark / system picker.

`src/main.js`

```javascript
import { getTheme, setTheme, toggleTheme } from 'sitelo/ui/client'

getTheme()          // 'light' | 'dark' — resolved, not stored
toggleTheme()       // flip
setTheme('dark')    // pin
setTheme('system')  // clear the override and follow the OS again
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` | `'Toggle dark mode'` | Accessible name and tooltip. |
| `variant` | `'solid' \| 'soft' \| 'outline' \| 'ghost' \| 'link'` | `'ghost'` | Button variant. |
| `color` | `'primary' \| 'neutral' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Which palette to draw from. |

`themeScript()` takes an optional `nonce`, for a site with a content security policy.
