A theme is a palette of ten colour roles. AsheeUI ships two, `light` and `dark`, and an
application adds its own by declaring them in the configuration. Nothing about a theme is a
stylesheet: the palette is resolved from the configuration, written into CSS variables as
`--ashee-*`, and selected by one class on the document root.

## The two built-in themes

`defaultTheme` states what a first-time reader sees. It takes a theme name, or `"system"`,
which follows `prefers-color-scheme`.

```ts
export default {
  defaultTheme: "system",
};
```

A reader's choice is remembered in `localStorage` under `ashee-theme`, and it wins over
`defaultTheme` on later visits. `themeController` and `useTheme` are the API for changing
it, see [Switch a theme](#switch-a-theme).

## A custom theme

A custom theme is a palette that inherits one of the built-ins and states only what it
changes. `extends` names the palette it inherits. The name is your own string, it needs no
declaration, and the palette key, `defaultTheme` and `themeController.setTheme` all accept it:

```ts
import type { ExternalConfig } from "@asheeui/core";

export const config: ExternalConfig = {
  defaultTheme: "system",
  color: {
    midnight: {
      extends: "dark",
      background: "#0b0b12",
      primary: "#8b5cf6",
      border: "#2d2a45",
    },
  },
};
```

The *existence* of a theme is checked at runtime rather than at compile time: a `defaultTheme`
naming a theme the configuration does not declare is rejected when the provider renders. So the
declaration and its use have to agree, and the compiler will not catch a typo for you.

Three things follow from that declaration:

- **The seven roles it does not state are inherited**, so a change to the built-in `dark`
  palette reaches it.
- **The inheritance is from the merged palette**, not from the palette that shipped with
  the library, an application that also overrides `dark` gets its own values here.
- **It is a theme like any other**: it appears in `availableThemes`, is selectable, is
  remembered, and gets `.theme-midnight` on the document root.

A custom theme with no `extends` inherits the family of `defaultTheme` when that is `light`
or `dark`; with `defaultTheme: "system"` it inherits `light`, because a system default has
no single family to inherit.

## Switch a theme

`useTheme` reads the controller and re-renders when it changes.

```tsx demo="concepts/theme-switch"
```

| Field | Meaning |
| --- | --- |
| `theme` | The selection: a theme name, or `"system"` |
| `resolvedTheme` | What the selection resolves to, `system` becomes `light` or `dark` |
| `availableThemes` | Every theme the application declared |
| `setTheme` | Choose a theme and persist the choice |
| `toggleTheme` | Advance to the next available theme, for a single-control switch |

## What the controller puts on the page

The controller does two things, both before the first paint:

1. It applies `theme-<name>` to the document root, removing any other `theme-*` class.
2. It writes a stylesheet of `--ashee-*` variables, one block per theme.

The stylesheet is built from every provider's configuration rather than from the last one
to mount, so a provider nested inside the application, a page demonstrating a different
configuration, for instance, can add a theme but never remove one. If you have seen a
custom theme resolve to the light palette, that is the failure this rule prevents.

## Themes and `color-scheme`

The applied theme is also written to the root's `color-scheme`, so the browser's own
surfaces, scrollbars, form controls, the caret, follow the theme rather than the operating
system. It is written wherever the theme is applied: by the pre-paint script on first load, and
by the controller on every later switch, so a runtime change moves the browser's surfaces with it.

## Server rendering

The provider renders a small script that applies the class and injects the variables while
the server HTML is parsed, so a dark application does not flash light before hydration. A
client-only render cannot run a script React creates, so the provider applies the same
values itself in a layout effect, still before the first paint.

Only the **outermost** provider emits that script. A provider nested inside the application, a
page demonstrating its own configuration, still adds its themes to the controller and to the
stylesheet, but it is not the document's writer: the application's provider owns the pre-paint
class, the `color-scheme` and the document's default theme.
