# @aweftjs/ui

Components and theming on top of `@aweftjs/dom`. `h` is `dom`'s `h` plus the handful of things
that need to know where the element sits: its theme, its state cells, its listeners. A theme is
data you define once and match against, not a stylesheet you write. Everything one page owns is
one object made per render, so a build can render two pages at the same time and neither can see
the other's classes.

## Quickstart

```tsx
import { h, mount } from '@aweftjs/ui';
import { mutable } from '@aweftjs/core';

const Counter = () => {
	const clicks = mutable(0);
	const hovered = mutable(false);
	return (
		<button theme={['button', hovered.bool('hovered', null)]} isHovered={hovered}
			onClick={() => clicks.set(clicks.get() + 1)}>
			clicked {clicks} times
		</button>
	);
};

mount(document.body, <Counter />);
```

[`recipes/ui/`](/docs/recipes/ui) is the whole of this
README as a running page. `npx vite recipes/ui` serves it.

## Five rules

Each is stated again where it belongs. They are here together because each is easy to get wrong
and the symptom does not name the cause.

1. **A theme value is a list of segments.** `theme={['button', 'quiet']}` reaches `button` and
   `button_quiet`. `theme="button_quiet"` is one token naming one entry, which is how a part is
   reached: the element gets the variant's colours and none of `button`'s box. (The theme.)
2. **`each` builds one shape per list.** The row component under `each` renders the same tags in
   the same order on every row; a button on some rows and nothing on others is wrong, with no
   message. Vary a value, not the shape. `Table`'s `cell` is not under that rule: it may answer
   a button on one row and `null` on the next. (Navigation and data.)
3. **The root entry paints nothing.** Your own page entry sets `background`, `color` and a
   `minHeight` of the viewport, and the page's HTML carries `<style>body { margin: 0 }</style>`,
   because a theme entry cannot reach `body` and the host's white margin shows as a band around
   a dark page. (The look.)
4. **`validate` is handed the cell, not its value.** Read it with `cell.get()`; a formatter
   writes back with `cell.set()`. (Composites, under `Validate`.)
5. **`theme` appends, on every component.** `<Card theme="tight">` is the card entry plus
   `card_tight`; the segments you pass never replace the component's own. (What every component
   takes.)

## Source is `.tsx`

This package's own source is `.tsx`, compiled by this stack's own transform. JSX becomes plain
`h` calls resolved by ordinary scope, so `h` has to be in scope in every file that writes JSX.
Node cannot load `.tsx` on its own, so a suite that imports one runs with
`node --import @aweftjs/build/loader`; the root gate does that for you.

An application bundles with `aweft()` from `@aweftjs/build`, which is one line in a vite config
and is what
[`recipes/ui/vite.config.ts`](/docs/recipes/ui/files/vite.config.ts)
shows. Pass `aweft({ defaultH: '@aweftjs/ui' })` if your pages are `ui` pages: a file that writes
JSX and imports no `h` of its own is given one, and without that setting it is `dom`'s, which
writes `theme` out as an attribute nothing reads.

**Getting the packages.** Nothing is published to a registry yet. An application takes this repo
as a git submodule and its own package manager resolves `@aweftjs/*` through the workspace, which
is how the five applications that use the stack do it. A program written outside a checkout has
nothing to resolve those specifiers to, so
[`recipes/`](/docs/recipes) and the tests live inside the
repo.

## `h`, `svg` and `html`

`h(tag, props, ...children)` is `dom`'s, with eight prop names taken off it:

| prop | what happens |
|---|---|
| `theme` | a string, a list, a cell, or lists of those; flattened at mount into a class list and matched against the theme |
| `class` | kept, and joined in front of the classes the theme generated. Without a `theme` it is an ordinary attribute |
| `style` | an object (or a string); a bare number in a size property gets `px`, and `$var` and `$fn()` resolve against the element's own theme chain |
| `isHovered`, `isFocused`, `isClicked`, `isTouched` | a cell this package writes from real events. `isFocused` is the element's own focus, and `isTouched` is a pointer event that says it was a finger |
| `onClick`, `onInput`, `onKeyDown`, … | `on` and an uppercase letter: a handler, handed to `dom` as `$on<type>` so a hydration replays it, and gone when its element goes. Your own `$onclick` beside it runs first |

Everything else goes to `dom` unchanged: a bare name is an attribute, `$name` is a property.

An element with none of those is `dom`'s `h` exactly, node and all, so
`const box = h('div', { class: 'box' })` is an element. **An element with any of them is a
mounter, not a node**: the theme it gets depends on where it is mounted, and `dom` hands the
mount context to a mounter and to nothing else. Hand it to `mount` as you would anything else.

`svg` is the same in the SVG namespace, with no theme: `class` and `style` stay plain attributes.
`html` is `dom`'s template-literal tag bound to this `h`.

On a component, `each:name` renames the loop item: `<Row each:row={rows} />` hands each item to
the component as `props.row` rather than `props.each`.

## The theme

A theme is a flat object whose keys are `_`-joined selector paths.

```ts
Theme.define({
	'*': { $brand: '#1b6ef3' },
	tile: { padding: '$space4', borderRadius: '$radius', background: '$brand' },
	tile_flat: { boxShadow: 'none' },
});
```

An element's `theme` prop flattens to a class list with `*` in front of it. **An entry matches
when its segments appear in that list in order, with gaps allowed.** So `card_hovered` matches an
element themed `card primary hovered`, and `hovered_card` does not match anything themed
`card hovered`. Entries are ordered by where their last segment matched, ties going to the longer
entry, and later in that order wins.

**A class token holding `_` names one entry: that is how you reach a part.** A part is a different
element from its component, and it must not wear the component's own box, so `theme="card_title"`
reaches `card_title` and not the bare `card`. A modifier is a state or a variant of the same
element and stays a segment of its own, so `theme={['card', 'tight']}` reaches `card` and
`card_tight`. Both spellings mix: `theme={['card_title', 'muted']}` reaches `card_title` and
`card_title_muted`, and an element that wants a part and its component says both tokens.

**Values.** `$name` is a variable, `$fn(a, b)` is a call, `$$` is a literal `$`, and `$size$px`
is the variable followed by the text `px`. A bare number in one of `sizeProperties` gets `px`.
What a variable holds is text, and that text is not read again: `$a: '$b'` writes the four
characters `$b` into the CSS rather than following them. Point a declaration at the variable you
mean.

**Variables and functions are the same namespace.** A `$name` whose value is a function is a
function; anything else is a variable. Both are found by walking the matched chain from the most
specific entry down, so a generic entry writes `background: '$hover'` once and every component
supplies its own `$hover`. And because a function is theme data, an application defines its own
and a nested theme replaces one:

```ts
Theme.define({ '*': { $em: (args) => `${Number(args[0]) * 16}px` } });
```

The colour functions (`$shiftBrightness`, `$brightness`, `$saturate`, `$hue`, `$alpha`,
`$invert`, `$contrast_text`, `$luminance`) and the arithmetic ones (`$add`, `$sub`, `$mul`,
`$div`, `$mod`, `$min`, `$max`, `$floor`, `$ceil`, `$round`, `$if`) ship in the default theme, so
every one of them can be replaced the same way. A `$name(` nothing defines is written back out as
it was read, so `calc()` and `rgb()` survive untouched.

**Directives** live inside an entry, keyed `_name_rest`:

| directive | compiles to |
|---|---|
| `extends: 'other'` or `['a', 'b']` | what it extends applies first, so this entry wins over it; a list starting `*` replaces the inherited one |
| `_elem_button` | `button .awN { … }` |
| `_children_span` | `.awN > span { … }` |
| `_cssProp_focus` | `.awN:focus { … }`; a pseudo-element gets `::`, and a key written with its own colons is used as written |
| `_media_(min-width: 40em)` | that entry's body inside the query |
| `_container_(min-width: 28rem)` | that entry's body inside a container query |
| `_starting_` | that entry's body inside `@starting-style`, the style a transition starts from. It takes nothing after the name |
| `_keyframes_spin` | `@keyframes spin-<id>`, with `$spin` bound to the generated name |
| `_fontFace_body` | `@font-face { … }` |
| `_import_fonts` | `@import url(…) layer(aweft);` |

`@font-face`, `@keyframes` and `@import` are emitted once per definition per render rather than
with the entry's own rules, so two class chains reaching one entry emit the font once.

**A directive block holds declarations and one more directive.** A `_media_` or a `_container_` may
hold a `_cssProp_` and a `_starting_`; a `_cssProp_` may hold a `_starting_`. That is what lets a
dialog's `::backdrop` fade: its transition has to reach `.awN::backdrop` and has to stay inside the
reduced-motion query. A third level compiles to nothing, and an enclosing rule is dropped wherever
its own body came out empty. `_keyframes_`, `_fontFace_` and `_import_` leave the entry body rather
than wrapping the rule, so they are read at the entry's own level only.

**A declaration whose value is a list is emitted once per item, in order.**
`appearance: ['none', 'base-select']` writes `appearance: none; appearance: base-select;`, and a
host that cannot read the second keeps the first. That is the only way an entry, which is an
object, can say one property twice.

**Every rule is inside `@layer aweft`, and this package ships zero `!important`.** Unlayered
styles beat every layer, so an application's own stylesheet overrides the library with a one-class
selector and no specificity fight.
[`packages/ui/tests/browser.test.ts`](https://github.com/torrinworx/aweft/blob/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/tests/browser.test.ts)
asserts that in Chromium.

**Two themes on one page.** `<Theme value={partial}>` merges a partial theme onto whatever is
above it for its subtree, and that subtree generates its own classes. `<ThemeContext value="brand">`
is different: it sets a prefix that `ThemeContext.use(h => component)` puts in front of every
`theme` an element below asks for. An element with no `theme` prop stays plain; write `theme=""`
to take the cascade and nothing else.

Values in a generated class are literals, not custom properties. The element's `class` attribute
holds one minted name per distinct list, not the segments themselves, and that name's rules are
written one block per matched entry, so a stylesheet has as many blocks for `.aw1` as entries
matched it.

## The per-render object

```ts
const ui = context();
const body = await render(<App />, { context: ui });
const page = `<!doctype html><html><head><style data-aweft>${ui.theme.markup()}</style></head><body>${body}</body></html>`;
```

`mount`, `render` and `hydrate` each make one and thread it through `dom`'s context. It carries
`theme` (this render's class cache and stylesheet), `ids` (the counter behind an
`aria-labelledby`, counting from zero per render so a server and a browser agree), `popups`,
`head` (the page's head tags, and `head.markup()`), `stage` (one entry per live `StageContext`),
and the language the page shows: `context({ locale, catalog })`, which the text tokens below look
up. `usedText(ui)` answers the keys a render's tokens looked up, which is what a static walk reads
to say what a catalog lacks.

`render` holds the head list and the stage list for the length of the call, so a caller can read
both after the page has been taken down. A render object is therefore for one page.

`mount` puts the stylesheet in the document head and takes it back out when it unmounts.
`hydrate` adopts the one the server wrote rather than making a second. `render` returns the item's
markup only; the CSS is `context.theme.markup()`, which the page puts in its own head.

The element the mount puts there carries the sheet as it stood then, and nothing writes it again.
A class compiled later (a hover state the pointer reaches, a theme cell that moves) arrives as a
`<style>` of its own holding only what that compile added, and every one of them comes out with the
mount. So reading the sheet back out of the DOM means every `style[data-aweft]`, each carrying its
own `@layer aweft` wrapper, while `theme.markup()` is the same rules as one stylesheet without
reading the DOM at all. `theme.watch(fn)` hears each compile's CSS as it is added.

The reason is webfonts. A browser registers an `@font-face` by name when it parses the sheet that
declares it. Change a stylesheet the document already holds and Chromium drops every face on the
page and registers them again, whether the change is a rewrite, a rule put in through the CSSOM, or
a write to a different element entirely; Firefox does the same when a sheet's text changes. Until
the data is back, the page's text falls back to a system font. Nothing in this package declares a
face, so nothing here showed it; an application that does hits it on the first hover (design 257).

Two `mount` calls into one page share that page's render, so two widgets on one page cannot mint
the same class name for two different themes. Passing a `context()` by name asks for a render of
your own instead, which is what a static render wants; two named renders in one page each count
their classes from zero.

**A component hydrates by identity, and the elements it makes on the way are dropped.** `hydrate`
builds the client's tree and pairs it against the nodes the server wrote, keeping the server's;
the fresh elements it made to compare against are thrown away. So a hydration is not free of
`createElement`, and counting those calls counts the client's tree, not a defect.
[`packages/ui/tests/controls.test.ts`](https://github.com/torrinworx/aweft/blob/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/tests/controls.test.ts)
pins both halves: every server element is kept by identity, and nothing the client made ends up in
the page.

**A hydrated page is live.** Nothing this package computes is written onto an element: a handler is
a `$on<type>` property and the class and the style are cells handed to `dom` as attributes, and
`dom` carries all three onto the node the hydration adopted (design 133). So a click, a keystroke,
a focus, a hover, a theme cell and an inline style all reach the page a server sent.
`browser.test.ts` clicks, types, focuses and hovers for real in Chromium and reads the hovered tint
back off the button the server wrote.

**A theme mismatch across a hydration is not reported.** `dom` catches a structural mismatch
loudly, but if the client's theme registry says something different from the server's, the client
rewrites the adopted `<style>` and says nothing. Define your theme in a module both sides import.

Reaching a `ui` system from a mount that has none is an assert naming what to call instead.

## Contexts

```ts
const Tone = createContext('plain');
const Label = Tone.use((tone) => (props) => <span theme={['label', tone]}>{props.children}</span>);

mount(document.body, <Tone value="accent"><Label>hi</Label></Tone>);
```

`createContext(def, transform)` returns the provider, with `def`, `read(context)`,
`node(context)` and `use(build)` on it. `transform(raw, parentValue, children)` runs on first read
and again after `raw` changes; `raw` arrives exactly as written, so a cell arrives as a cell.

`use(build)` calls `build` **once per mount of the component it hands back**, with the value read at
that mount's own place in the tree. So two mounts under two different providers each get their own
component, and a mount already on the page does not call `build` again when the value above it
moves. A value that moves is read with `read(context)` or held in a cell the built component
follows, not through `use`.

A node has `id`, `parent`, `children` and `value()`. `children` is an observable array in mount
order, so something above can watch what appears below it. `node(context)` answering null is how a
component asks whether there is a provider above it at all.

`node(context)` reads the tree **at that point**, not from the root: the node it answers is the
nearest provider above the mount whose context you handed it, and `parent` walks up from there.
There is no way in from outside, and that is the design: to introspect from elsewhere, capture a
context inside the tree (a component's fourth argument, or a mounter's) and start from that.

## Marks

A component that takes more than one slot of children reads them with `mark` and `categories`.

```tsx
<Shown value={open}>
	<p>shown</p>
	<mark.else><p>hidden</p></mark.else>
</Shown>
```

A mark is a value, never a node, and never reaches `mount`. Inside a component:

```ts
const [popup, anchor] = categories(props.children, ['popup', 'anchor'], 'anchor');
```

Each category has `items` and a `props` merged from every mark of that name. An unknown slot, or
a bare child with no default slot, is an assert naming the slots the component knows.

`mark.name` is declared for the eight slots this package reads (`then`, `else`, `case`, `default`,
`popup`, `anchor`, `tabs`, `panels`). Any other name works at run time; in a TypeScript file write
it as `mark('name', props, ...children)`.

## Control flow

`Shown` takes a condition, a `then` (the default slot) and a `<mark.else>`, with `invert` to flip
it. `Switch` takes a `value` matched against `<mark.case value=…>` with a `<mark.default>`, or a
`cases` object of cells where the first truthy key wins. Neither builds an element or keeps state.

## Loading

```tsx
const Article = suspend(Spinner, async ({ id }) => <Body article={await fetchArticle(id)} />, ErrorPanel);
```

The fallback shows, the loader runs, and what it resolves to replaces the fallback. The promise is
declared `pending`, so a static render waits for it.

**A rejection never leaves the fallback on screen.** The call's own `failed` component shows it,
or the `LoaderContext`'s, and with neither the slot goes empty and the rejection is rethrown so
the host reports it. `LoaderContext` carries `{ loading, failed }` and inherits them one field at
a time.

**Every wait in this package shows the same loader** (design 219). `suspend` while its loader runs,
a `Button` while a promise its `onClick` returned is pending, and a `FileDrop` entry an application
moved to `status: 'loading'` all show `LoaderContext.loading`, and `LoadingDots` when nothing named
one. So naming a loader once names it for all three:

```tsx
<LoaderContext value={{ loading: MySpinner }}><App /></LoaderContext>
```

No drawing ships (design 144). `LoadingDots` is three spans and a keyframes block; for an animated
spinner, `@aweftjs/icons` says which set to install and shows the one line that puts it here.

## Popups

```tsx
<PopupContext>
	<App />
</PopupContext>

<Detached enabled={open}>
	<button onClick={() => open.set(!open.get())}>details</button>
	<mark.popup><Card>what floats</Card></mark.popup>
</Detached>
```

Everything that floats is a popup: a `Tooltip`, a `Menu`, a `Select`, and `Popup` itself. Every one
of them has somewhere to go without being told. A popup's sink is the nearest `<dialog>` above where
it was written, then the sink a `PopupContext` gave it, then the element the page was mounted into.
So a page needs no wrapper to open one, and a menu opened inside a modal draws inside that dialog,
which is what makes its rows clickable: a dialog's top layer swallows every pointer event aimed at
anything outside it.

`PopupContext` is how a page chooses a sink on purpose. It renders its children and then the popups,
so a popup is after the page in DOM order. The first one on a page takes the render's own sink; a
second one, nested or beside it, makes its own, so a nested `PopupContext` renders its popups at the
end of its own subtree rather than at the end of the page. `Popup` renders nothing where it is
written and puts its element in whichever sink it found. `Detached` measures its own children,
scores twelve placements and picks one, re-measures every animation frame, and closes when the
anchor moves, on the reading that the page scrolled.

**There is no `z-index` in this package.** A popup asks for the top layer with the `popover`
attribute where the host has one, and falls back to DOM order where it does not. Below the top
layer a page with its own stacking context can put something over a popup, and that is the page's
decision to make.

`Detached` mounts its anchor where the anchor was written and reads the anchor's nodes back out of
the document, so a page taken over from a server adopts the anchor the server sent (design 153).
`trackedMount()` is the other way of reaching children a component does not own: it returns the
array the real nodes appear in and a mounter to render where they belong, and children mounted
that way are built fresh rather than adopted.

**`Detached` needs a real browser to open.** It places its popup from the anchor's measured
rectangle and asks for the next animation frame, and the light tree has neither layout nor frames.
So in Node, and in a static render, a `Detached` renders its anchor, and its popup's children are
in the markup inside a `display: none` container rather than left out of it. That is the right
server output, and it is also why the interaction tests for it are in
`browser.test.ts` rather than in the light tree. `Popup` on its own has no such need: give it a
placement and it renders where you put it, anywhere.

## Other exports

`InputContext` is where an input event goes: `InputContext.fire(context, 'click', payload)` calls
the generic `on` and then `on<Type>`, with the application's `meta` merged in last. `useAbort(fn)`
runs `fn` with a fresh `AbortSignal` and hands back the abort. `sizeProperties` is the set of
property names a bare number is given `px` for.

## What every component takes

Four props mean the same thing on every component in this package, controls, composites, display
pieces and grouping pieces alike.

- **`theme` appends your own segments** to the ones the component writes, so
  `<Card theme="tight">` is the card entry plus `card_tight` and `<Button theme={cell}>` follows a
  cell.
- **`class` appends** a plain class name, leaving the generated one alone.
- **`element` hands in the node to decorate** instead of building one. An `element` of the wrong tag
  is an assert naming both tags, because a `<div>` wearing a checkbox's theme is a checkbox that
  does nothing.
- **Anything else goes to the element**, so `id`, `aria-*` and a handler the component does not name
  land where you would expect them.

**Every state prop is a cell, or absent.** Given one, the component writes it and follows it; given
none, it keeps its own. A display prop takes a value or a cell. There are no imperative handles.

## Controls

Every control here is one native element with a theme on it. There is nothing in this package that
draws a control out of `div`s, so the keyboard, the role, the form participation and the autofill
are the platform's and not ours (design 128).

```tsx
import { Button, Select, TextField, Toggle } from '@aweftjs/ui';
import { mutable } from '@aweftjs/core';

const email = mutable('');
const problem = mutable(null);

<TextField label="Email" value={email} error={problem} description="Work address" />
<Toggle label="Email me" value={subscribed} />
<Select label="Owner" value={owner} options={users} display={(user) => user.name} placeholder="Pick someone" />
<Button label="Save" onClick={() => save(email.get())} />
```

| component | the element | its own props | `size` | example |
|---|---|---|---|---|
| `Button` | `<button>`, or `<a>` with an `href` | `label`, `type`, `icon`, `iconPosition`, `disabled`, `loading`, `round`, `inline`, `href`, `hrefNewTab`, `onClick`, `track` | `sm`, `lg`, `icon`, `icon-sm`, `icon-lg` | `button` |
| `TextField` | `<input>`, in a `<div>` on `input_group` when it was given an addon | `value`, `label`, `description`, `error`, `leading`, `trailing`, `placeholder`, `password`, `onEnter`, `onKeyDown`, `disabled`, `type` | `sm`, `lg` | `text-field` |
| `TextArea` | `<textarea>` | the same, plus `maxHeight` | `sm`, `lg` | `text-area` |
| `Checkbox` | `<input type="checkbox">` | `value`, `label`, `invert`, `indeterminate`, `disabled`, `onChange` | `sm`, `lg` | `checkbox` |
| `Radio` | `<input type="radio">` | `value` (the group's), `option` (this one's), `label`, `disabled`, `onChange` | `sm`, `lg` | `radio` |
| `Toggle` | `<input type="checkbox" role="switch">` | `value`, `label`, `disabled`, `onChange`, `type` | `sm`, `lg` | `toggle` |
| `Slider` | `<input type="range">` | `value` (a number), `min`, `max`, `step`, `disabled`, `track` | `sm`, `lg` | `slider` |
| `Select` | a `<button role="combobox">` in a `<span>` with its arrow, over a hidden `<select>` | `value`, `options`, `display`, `placeholder`, `open`, `disabled`, `name`, `autocomplete`, `onChange` | `sm`, `lg` | `select` |
| `LoadingDots` | three dots | `type`, `size`, `label` | a CSS length | `loading-dots` |
| `Icon` | `<svg>` | `name`, `size`, `label`, `rot` | a CSS length | `icon` |

The last column is the file under
[`recipes/ui/examples/`](https://github.com/torrinworx/aweft/tree/9a5bb24770dc7555257f8a307d79917efc58df70/recipes/ui/examples),
without its `.example.tsx` ending. Each one renders every state, type and size of its component,
in both modes, on a page of its own at `catalogue.html#/<Component>`, and the recipe's driver
reads it there (designs 197 and 226).

**`size` is one theme segment, right after `type`** (design 194): `sm` is 32px tall, nothing is
36px and `lg` is 40px, and the entries are `button_sm`, `input_lg`, `checkbox_sm` and so on, the
same way `quiet` and `danger` are. A small control takes the smaller text step with it. `Button`
also takes `icon`, `icon-sm` and `icon-lg`, which are a square of that height with no padding, for
a button whose label is an icon. It is a value or a cell, like every other display prop.
`LoadingDots` and `Icon` have a `size` of their own, which is a CSS length and not this axis.

**A label is what gives a control a name.** Give one and the component renders a `<label for>`
next to the element, both inside one `<div>`, mints the ids off the render (so a server and its
hydration agree), and wires `aria-describedby` and `aria-invalid` for you. Give none and you get
the bare element, and naming it is yours.
[`packages/ui/tests/controls.test.ts`](https://github.com/torrinworx/aweft/blob/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/tests/controls.test.ts)
finds every control by its role and its name.

**A `Button` with no name throws where it mounts, in development** (design 266). Its name is its
`label`, its text, an `aria-label` or `title` on it, or a `label` on the `Icon` inside it; a button
that is only an unlabelled icon has none, a screen reader says "button" and nothing else, and the
mount throws with those three ways to name it. The button is read once it and what is inside it are
on the page, so a labelled `Icon` counts, and a static `render()` reads it the same way. A release
build has no check: the statements are gone.

**One document is one render.** The ids come off the render's counter, which starts at zero every
time (design 109), so two named renders mounted into the same document mint the same ids and their
labels point at each other's controls. Two `mount` calls into one page share that page's render and
do not collide; a second `context()` is a second render and belongs in a second document.

**What a control starts with is in the markup.** `Checkbox`, `Radio` and `Toggle` write the state
they start in as the `checked` attribute; `TextField` writes its text as `value` and `TextArea` as
its content. So a server page shows a ticked box and a filled field before any script runs. The
property follows the cell once the page is alive, which is what the platform does with these
attributes too: they say what the control started as, not what it holds now.

**An `error` cell is announced when it arrives.** While it says something the control carries
`aria-invalid`, its `aria-describedby` names the message, and the message is a live region. Once the
error clears the attribute is removed rather than set to `false`.

**A promise makes a button busy.** A promise `onClick` returns disables the button and shows the
`LoaderContext` loader until it settles, however it settles, so a double click cannot submit twice.

**A radio group is a group because its members share a `name`**, minted once per `value` cell. So
the arrow keys, the wrapping and the roving focus are the platform's. `browser.test.ts` presses
the real keys: Space on a checkbox, the arrows in a radio group, Home and End on a slider.

**`TextField` takes `leading` and `trailing`, and builds a box only when it was given one**
(design 210). With neither, the markup is the `<input>` and nothing else. With either, the input
goes inside a `<div>` on `input_group` that carries the border, the radius, the fill and the height,
and the input carries none of them, so the two read as one control. The focus ring is on the box, so
tabbing into the input rings the whole thing. Text is wrapped on `input_group_addon` for you, and
anything else is mounted as it is, which is what lets an `Icon` or a `Button` of `size="icon"` go
there.

```tsx
<TextField label="Price" leading="$" trailing="CAD" value={price} />
<TextField label="Search" leading={<Icon name="search" />} value={query} />
```

**`Select` draws its own list, on every host** (design 224). The closed control is a
`<button role="combobox">`; the open list is a `<div role="listbox">` of `<div role="option">` rows
in a popup placed under the control at the control's width. So it looks the same everywhere, the
theme reaches every part of it, and `open` is a cell you can read and write. The list goes where
every popup in this package goes, so it needs no wrapper above it.

It opens on a click, on ArrowDown, ArrowUp, Enter and Space, and on typing a letter, which opens it
and jumps to the first row beginning with that letter in the one press. Inside it the arrows move
and wrap, Home and End go to the ends, typing keeps searching, Enter and Space choose, and Escape
closes and gives the keyboard back to the control. The focus stays on the control the whole time
and `aria-activedescendant` says which row the keys are on.

**A hidden `<select>` sits under it**, off the screen and out of the reading order, carrying the
same options and the same choice. It takes `name` and `autocomplete`, so a form the control is in
posts the value and autofill has a real control to find; anything that writes it writes your cell.
What autofill cannot do is draw its own highlight over the button, because the element it filled is
one pixel square.

**`Select` holds items, not the text a row reads as.** `options` is a list of anything, `display`
says what a person reads, and the cell holds the item you put in the list, so adding, removing or
reordering leaves the choice on its own item. An item that is a string or a number carries itself as
the hidden option's `value`; an object carries none, and the platform then posts what the row reads
as.

**`Select` draws its own arrow** (design 195). Every host draws a different one and no theme can
reach any of them, so the entry tells every host to draw none and the component renders the control
inside a `<span>` with an empty box at the right of it. The `select_chevron` entry draws the arrow the
way `checkbox` draws its tick: a `$chevron` square with two of its sides in `$mutedForeground`,
turned a quarter turn. So it needs no icon pack, and an application that wants another arrow gives
`select_chevron` its own rules. It is `aria-hidden` and takes no pointer events, so a screen reader
reads the combobox and a click reaches the element under it.

**`Icon` is built from icon data**, in the shape the icon sets publish: `body`, `width`, `height`,
`left`, `top`, and optional `rotate`, `hFlip` and `vFlip`. `name` is that data, or a name looked up
through the `Icons` context.

**This package ships no drawings** (design 144). `Icons` starts empty, and an application supplies
a pack, a resolver, or both. This is not only about the `Icon` you write yourself: `DropDown`,
`FileDrop`, `Modal` and `Validate` each mount one of their own, by name, so a page that mounts any
of them and has no `Icons` provider above it asserts
on its first render rather than rendering without the glyph. `Validate` is the one that surprises people, because its icon is a default
nobody asked for. Give the page a provider, or give the component its own `icon` prop. (`Select`
needs none: its arrow is drawn by the theme, design 195.)
`@aweftjs/icons` turns an installed icon set into exactly that:

```tsx
import { Icon, Icons } from '@aweftjs/ui';
import standard from '@aweftjs/icons/lucide/+standard';

<Icons value={standard}><App /></Icons>
<Icon name="check" label="done" />
```

A provider stacks a pack or a resolver in front of what it inherited, newest first, so a pack an
application puts up answers before anything under it. A pack is
`{ prefix, icons, aliases?, width?, height? }`, where the root `width` and `height` are the box
every icon in it that states none is drawn in; the sets state it once at the root, so without it a
real set renders clipped. A resolver is `(name) => data | Promise<data> | null`, and a promise is
declared `pending` so a static render waits for it. A resolver that answers null passes the name on
to the next source, and so does one that answers a promise of null, so a resolver in front of a
pack does not stop the pack behind it being asked. An icon with no `label` is `aria-hidden`,
because an icon beside the word it means is otherwise read out twice. A name nothing answers is an
assert naming the icon, how many sources were asked, and how to answer it: wrap the page in `Icons`
with a pack or resolver that has it.

**The components here ask for names, never for drawings.** `standardIcons` is that list, in the
spelling the sets publish: `chevron-down`, `chevron-up`, `chevron-left`, `chevron-right`, `check`,
`x`, `triangle-alert`, `search`, `upload`. Give one of them a drawing of your own by putting a pack of your
own in front, which is what `Icons` is for.

**Laying things out is a theme entry, not a component** (design 132): `row` and `column`, each with
`fill`, `center`, `start`, `end`, `spread`, `wrap` and `tight`, and `divider`. `center`, `start`
and `end` mean across the page on both.

```tsx
<div theme={['row', 'fill', 'spread']}><span>left</span><span>right</span></div>
```

[`recipes/ui/catalogue.html`](/docs/recipes/ui/files/catalogue.html)
is every one of them in every state, in both modes, one page per component at `#/<Component>`,
driven in Chromium by
[`recipes/ui/main.ts`](/docs/recipes/ui/files/main.ts) with
axe-core over every page.

## Laying a form out

There is no `Field` component. A form is the theme entries and a bare `<label>` (design 209): every
page that laid a form out reached past the components, for the same reason each time, so the
components went and the entries stayed.

```tsx
import { Checkbox, TextField } from '@aweftjs/ui';

<div theme="field_group">
  <fieldset theme="field_set">
    <legend theme="field_legend">Where to send it</legend>
    <div theme={['field', 'responsive']}>
      <label for="street" theme="field_label">Street</label>
      <TextField id="street" value={street} />
    </div>
    <div theme={['field', 'inline']}>
      <Checkbox id="post" value={post} />
      <label for="post" theme="field_label">Post it rather than email it</label>
    </div>
  </fieldset>
  <TextField label="Notes" value={notes} description="Anything else" />
</div>
```

| entry | what it lays out |
|---|---|
| `field` | one field: a control, whatever labels it, and whatever is said under it, in a column |
| `field_inline` | the same field as one `$control`-tall row |
| `field_responsive` | a column that turns into that row from 28rem of its container |
| `field_group` | the stack a form is, `$space6` apart, declaring itself the container above measures |
| `field_set` | the same stack on a `<fieldset>`, with the host's border, padding and minimum width off |
| `field_legend` | the `<legend>` inside one |
| `field_label`, `field_hint`, `field_error` | the three things written around a control |

**A control still labels itself.** `label`, `description` and `error` stay on the control, and a
control given any of the three wraps itself in its own `<div theme="field">`. So the last line of
the form above needs no box around it: a `Field` around a control that lays itself out is a column
of one thing.

**`field_inline` and `field_responsive` lay out the box's own children**, so they are for a bare
control and the `<label for>` you wrote beside it.

**`field_responsive` measures the nearest ancestor that declares itself a container**, which in this
package is `field_group` and nothing else. With no group above it, a query with no container answers
false and the field stays a column at every width.

**A label under an ancestor carrying `data-invalid` takes the colour its message has.** Nothing
writes that attribute for you; write it from the same cell you pass to `error` if you want it.

## Composites

Ten more components, each built out of the controls above and the behaviours underneath them.

```tsx
import { ColorPicker, Country, Default, DropDown, FileDrop, Menu, Modal, Region, Tooltip, Validate, ValidateContext } from '@aweftjs/ui';
```

| component | what it is | its own props | example |
|---|---|---|---|
| `Modal` | a stage template: the act inside a native `<dialog>` | `label`, `noEsc`, `noClickEsc`, `type` (`sheet`), `side` | `modal` |
| `Default` | the stage template that adds nothing | none | `modal` |
| `Tooltip` | `Detached` plus the hover and focus trigger | `label`, `enabled`, `locations`, `type` | `tooltip` |
| `DropDown` | a `<details>` whose `<summary>` wears the `button` theme | `open`, `label`, `icon`, `iconOpen`, `iconClose`, `arrow`, `name`, `type`, `disabled` | `drop-down` |
| `FileDrop` | a drop zone with a real file input in it | `files`, `extensions`, `multiple`, `limit`, `clickable`, `disabled`, `onDrop`, `ready`, `type` | `file-drop` |
| `Validate` | a check around a control, and the message it shows | `value`, `validate`, `signal`, `valid`, `error`, `showError`, `icon`, `type` | `validate` |
| `ValidateContext` | the form's answer: every `Validate` below it | `value` | `validate` |
| `ColorPicker` | a saturation and brightness square, the hue and the alpha as `Slider`s, and a swatch | `value`, `hasAlpha`, `disabled`, `type` | `color-picker` |
| `Country` | a button that opens a searched grid of every country | `value`, `locale`, `priority`, `flags`, `suggest` | `country` |
| `Region` | the same, over one country's subdivisions | `value`, `country` | `country` |
| `Menu` | a button and the list of actions it opens | `items`, `open`, `label`, `icon`, `type`, `size`, `disabled`, `locations` | `menu` |

These ask for `chevron-up`, `chevron-down`, `x`, `triangle-alert` and `upload` by name, so a page
using them answers those five through `Icons`; `@aweftjs/icons/<set>/+standard` does. A `Menu` asks
for none: its rows hold whatever `icon` you give them.

**A `Menu` is a button and the actions under it.** `items` is a list of
`{ label, icon?, type?, disabled?, onSelect }`, and `{ heading, items }` anywhere in that list draws
a small heading over its own group. `type: 'danger'` draws a row in the danger colour, which is what
a delete belongs in.

```tsx
<Menu label="Quick Actions" items={[{
	heading: 'Conversation',
	items: [
		{ label: 'Mute Conversation', onSelect: mute },
		{ label: 'Mark as Read', onSelect: read },
		{ label: 'Delete Conversation', type: 'danger', onSelect: remove },
	],
}]} />
```

The keys are the ones a `Select` has, because they are the same behaviour: the arrows move and wrap,
Home and End go to the ends, typing moves by what a row reads, Enter and Space choose, Escape and an
outside click close it, and Escape and choosing put the focus back on the button. The list is
`role="menu"` and the rows are `role="menuitem"`, named by the button. The list is in the page
while the menu is closed, inside the box the sink places, which is what hides it; a script that looks
for an open one asks by role with hidden elements left out, not for the first `role="menu"` it finds.

Opening it moves the focus onto the `role="menu"` element, which is where `aria-activedescendant`
names the row the keys are on. That is the ARIA menu-button pattern, and it is the only shape ARIA
allows: the attribute may not sit on the button that opened the menu. Escape and choosing a row put
the focus back on that button.

The anchor is the button this component builds, so the ARIA is in the markup rather than written
onto a node it does not own. Children go inside that button, and `element` hands one in. A group is
`{ heading, items }`, and a heading makes it one whether or not it has any items yet.

**A modal is a stage template, so back closes it.**

```tsx
stage.open({ name: 'edit', history: true, template: Modal });
```

A stage calls a template as `h(template, props, act)`, where `props` is what the `open` carried past
`name`, `template`, `history` and `children` (design 213). So the dialog is named at the call, and
`Modal` goes in as the template itself:

```tsx
stage.open({ name: 'edit', history: true, template: Modal, label: 'Edit the thing' });
```

The act is handed the same props, plus the `stage` after them. An act reached from the URL carried
nothing, so the template the stage was given is called with nothing.

**`Modal` reads the props it names and forwards none of the others.** `label`, `noEsc`, `noClickEsc`,
`type`, `side`, `element`, `theme` and `class` are its own; anything else an `open` carried goes to
the act and no further, so a row handed to the act is not written on the `<dialog>` as an attribute.
A prop the act wants that is spelled like one of the eight is read by `Modal` too: that is what one
prop bag means, and it is visible in the call.

Escape (through the element's own `cancel` event), a mousedown on the backdrop and the close button
all call the stage's `close()`, so a modal that owns a history entry goes down the same way whichever
one you used (design 124). `noEsc` and `noClickEsc` turn the first two off. A `Modal` with no stage
above it is an assert naming the call that shows one. A popup opened inside the dialog goes in the
dialog, so a `Select` or a `Menu` in a modal works with nothing else to write.

**A sheet is a `Modal` with `type="sheet"`**, against an edge instead of in the middle, sliding in
from it (design 202). `side` says which edge, `right` by default, and is read for no other type.
Everything else is the same: the same stage, the same three ways to close it, the same backdrop.

```tsx
stage.open({ name: 'filters', template: Modal, label: 'Filters', type: 'sheet' });
```

**A stack of disclosures that keeps one open is a run of `DropDown`s sharing one `name`**, which is
the platform's own behaviour for a group of `<details>`. There is no component for it (design 212):
give each drop-down the same `name` and you have one.

**A tip is on hover and on focus, and the anchor names it.** The children are the anchor, and a
`<mark.popup>` replaces the label with markup. The panel sits inside the box `Detached` placed and
wears no `popover` of its own: the box is already a popover, and a popover inside a popover is put in
the top layer and laid out by the browser, which takes the panel out of the box and lands it in the
middle of the screen (design 135). The pause before a hover shows it belongs to the
behaviour, so every tip on a page waits the same time and there is no prop for it; focus shows it at
once. Each element in the anchor gets `aria-describedby` naming the panel, written when the page
comes alive. A static render leaves it out, because nothing on the client can write it before the
pairing walk reaches the anchor, so markup carrying it could not be taken over (design 153). It is
taken off again when the component unmounts.

A `Tooltip` takes over server markup and can sit inside an act a stage swaps away, both since
design 153.

**A drop down is a disclosure, not a floating menu.** The content is the children, in the page's
flow. The `open` cell goes both ways: writing it opens and closes the element, and a person opening
it writes the cell. Space, Enter, the `button` role and the expanded state are the platform's,
because the element is a `<details>`. A floating menu under a button is `Detached` with a `Button`
anchor, which is one call and no new component.

**The country fields are a button and a dialog, and they ship no country data.**

```tsx
import { Countries, Country, Region } from '@aweftjs/ui';
import { countryData } from '@aweftjs/ui/countries';

const data = await countryData();

<Countries value={data}>
  <Country label="Country" value={country} name="country" autocomplete="country"
           priority={['CA', 'US']} placeholder="Pick a country" />
  <Region label="Province" value={region} country={country} name="region" />
</Countries>
```

Each is a button that opens a modal dialog with a search box over a grid of rows. `Country`'s cell
holds a two-letter code, `Region`'s holds a subdivision's short code, and under each is a hidden
`<select>` carrying the same options, so a form posts the code and autofill has a control to find.
Nothing is chosen to begin with and nothing ever chooses for the person.

**The names come from the host, the codes and the subdivisions from you.** `Intl.DisplayNames` has a
country's name in the page's own language, so no list of names ships here and `locale` is the only
thing that decides which language they are in; the flags are the two letters of the code as regional
indicator symbols, so no flags ship either, and `flags={false}` turns them off where a host draws
none. What is left is the codes and the subdivisions, which nothing derives: `countryData()` reads
the optional `country-region-data` peer, and `CountryData` is a shape you can build yourself instead
if the form allows five countries rather than 249. A field with no `Countries` above it is a loud
assert naming both.

**A search matches the name, the code and this package's aliases, without accents**: `uk` finds the
United Kingdom, `usa` and `america` find the United States, `cote` finds Côte d'Ivoire. It matches
anywhere in the word rather than only the start of it, so `uk` finds Ukraine as well. The aliases are
`countryAliases` on the same subpath, and `CountryData.aliases` is the whole map rather than an
addition to it: to add your own words, spread the shipped map into yours.

```ts
const data = await countryData();
<Countries value={{ ...data, aliases: { ...countryAliases, nz: ['aotearoa'] } }}>
```

**A row is chosen with a click on it, or with Enter or Space while the keyboard is on it**, which is
the listbox map every list in this package shares: the arrows and Home and End move, Escape closes,
and the search box keeps every printable character. Writing the cell chooses too, and that is the
whole of the control's state: a page that knows the country already writes `country.set('CA')` and
never opens the dialog.

**The rows are ordered, and the first one is a suggestion.** `Intl.Locale(navigator.language).region`
says which country the browser's language settings point at, so a browser set to `en-CA` has Canada
first, marked; a language with no region in it is maximized by the host, so `pt` is Brazil. It is the
language setting and not the location: nothing is asked for, nothing is fetched, and a person filling
a form for somewhere else is ordinary, which is why it suggests and never selects. `suggest={false}`
turns it off. After it comes `priority`, in the order you wrote it, and then every other country by
its name in the page's language.

**`Region` is the same control and not a dropdown**, because one country has 217 subdivisions and the
middle one has 11 (design 251). Handed no country it is disabled; the country changing does not clear
it, because a form clearing a field a person filled is the page's decision. Its names are English,
which is what the data has.

**Known limits.** The grid is drawn the first time the dialog opens, so a static render emits the
control, its dialog and the element a form posts, and none of the rows. A country that is already
chosen when a page is rendered statically has its name in the markup, and if the server's ICU data
and the browser's disagree about that name, the hydration refuses with both names in the message
rather than showing the wrong one.

**A file drop holds entries, and no upload.**

```tsx
const picked = mutableArray();
<FileDrop files={picked} extensions={['image/png', '.csv']} limit={4_000_000} ready={file} />
```

An entry is `{ name, file, status, error, reason }`. A file the zone accepted starts as `ready` and
one it refused as `error`, with `error` the sentence a person reads and `reason` the code you branch
on: `type` for a file the `extensions` do not cover, `size` for one over `limit`, and `count` for a
second file while `multiple` is false (design 214). An accepted entry carries neither. A refused
file stays in the list rather than disappearing. Move `status` to `loading` while you upload by
writing the entry back, `files[0] = { ...files[0], status: 'loading' }`, which is the edit a list
can hear; that row then shows the `LoaderContext`'s loader beside the file's name, and the dots
when nothing named one (design 219). `ready` is written null while anything is loading, and otherwise the file, or the array of
files when `multiple` is true, counting every entry that is not in error. The transport is yours:
`ui` decides nothing about storage, transport or the server, so the entry carries the platform
`File`.

**With `multiple` false, a second pick replaces the entry that is there**, in place, so a `watch` on
the list hears a `replace` and not an `add`. A page listening for `add` alone sees the first file and
none of the ones after it.

**`limit` is bytes, and every sentence naming it is written for a person**: KB, MB or GB, 1024 to a
step, so a `limit` of `4_000_000` reads as 3.8 MB. The prompt names the accepted types the same way,
so `image/png` reads as `png`, `image/*` reads as `image` and `.csv` reads as `csv`.

The zone listens for `dragenter`, `dragleave` and `drop`, and reads the dropped files off the
event's `dataTransfer.files`; the input listens for `change` and reads its own `files`. Those four
are the whole of what reaches this component from the host.

Children replace the prompt line and the listing. The input itself is visually hidden rather than
`display: none`, so it is still focusable, and its label is the zone's prompt whichever chrome is
showing. A `FileDrop.Button` standing on its own names its own input the same way, with an offscreen
`<label>` carrying the button's `label` or its `aria-label`, because the button beside it is what
shows the words. `look.test.ts` reads the `filedrop_picker` rule and fails if it ever becomes
`display: none`.

**`FileDrop.Button` is two things, decided by where it is** (design 214). Inside a `FileDrop` it is
a `Button` that opens that zone's input, and it takes no checking props of its own, because the zone
already has them; giving it one is an assert. Outside a zone it is the picker with no chrome: its
own hidden input, the same checks, and `files`, `extensions`, `multiple`, `limit`, `onDrop` and
`ready` on the button itself.

```tsx
<FileDrop.Button label="Change photo" extensions={['image/*']} multiple={false} ready={photo} />
```

**A check is given the cell, not the value.**

```tsx
<ValidateContext value={allValid}>
	<Validate value={email} validate="email" signal={submitted}>
		<TextField label="Email" value={email} />
	</Validate>
</ValidateContext>
```

**`validate` is handed the cell, not what the cell holds.** So a check reads it with `cell.get()`,
and one that formats what was typed writes it back with `cell.set(...)`, which is how four of the
eight built-ins work. It returns the problem as a string, or `''` or `null` for no problem.

```tsx
<Validate value={confirm} validate={(cell) => (cell.get() === password.get() ? '' : 'They do not match.')}>
```

`validate` is that function, or the name of one of eight built-ins: `phone`, `email`, `pan`, `expDate`, `postalCode`, `date`, `number` and `float`. Four
of them write a formatted value back into the cell: `phone`, `pan`, `expDate` and `postalCode`. Each
answers nothing for an empty value, so a field is not invalid before anybody has typed in it. They
are small on purpose, each doing what its name promises and no more: `email` is a shape check,
`date` is `YYYY-MM-DD`, `postalCode` is the Canadian one, and `pan` is a Luhn check on thirteen to
nineteen digits. Write a function for anything else.

**A `signal` is read, not counted.** While that cell holds something falsy nothing is checked, and
while it is truthy every change to `value` is checked (design 208). So a form that clears itself
after a successful submit writes `submitted` back to false and goes quiet again, rather than marking
every emptied field. Going quiet clears the message, writes `valid` true and writes `error` null.
With no `signal`, checking is live from the start.

**Under a `ValidateContext`, a check runs again when any cell that form is checking changes.** That
is what confirm-must-match needs: the validator above reads the other password's cell, and nothing
else would tell it that the other password moved. A validator that reads a cell no `Validate` in the
same form is checking is not re-run for it; put that control in a `Validate` too.

The message is rendered once, after the children, as a live region. It also reaches the control:
a control of this package that was given no `error` of its own goes `aria-invalid` and its
`aria-describedby` names that message. A `Validate` around a plain `<input>` still shows and
announces the message and leaves the input unmarked, because nothing read it. `showError` false takes
the message off the screen and leaves it announced.

A `Validate` wraps one control. Every control under it takes the message, so a `Validate` around two
of them marks both invalid and points both at the one message. Two controls want two `Validate`s.

A validator that throws is reported on a microtask the host sees, the way every handler a page wrote
is reported here, and the value counts as invalid with the error's message. A form is never quietly
valid because its check crashed.

`ValidateContext` writes its `value` cell true while every `Validate` under it is happy. Each one
registers when it mounts and leaves when it unmounts, so a field that goes away stops holding the
form invalid.

**A colour picker is a square and one or two sliders.** `value` is a cell holding CSS colour text,
anything `readColour` reads, and it is written back as `rgb()` or `rgba()`; text that is not a
colour is an assert naming the text. Saturation and brightness are one place in a square, dragged
with a pointer or driven with the arrows; hue and opacity, the last only when `hasAlpha` is not
false, are labelled range inputs with the platform's keyboard on them. The swatch beside them is
`aria-hidden`, because it says what the rest already say. There is no eyedropper and no hex field;
a `TextField` on the same cell is the hex field, because the cell is text.

The square's thumb is the one element in this package with a role written on it, because there is
no two-axis role in ARIA (design 222). It is `role="slider"`, focusable, and says both axes:
`aria-valuenow` carries the saturation and `aria-valuetext` reads `saturation 40%, brightness 80%`.
Left and right move saturation, up and down move brightness, `Home` and `End` take saturation to
its ends, and Shift makes any of them coarse. A press anywhere in the square moves the thumb there,
and a drag that leaves the square keeps working.

A drag, a key or a slider writes the cell, and nothing else does, so mounting a picker on a colour
leaves that colour and its notation alone. With `hasAlpha` false a write keeps the alpha the cell
already had: nothing on the screen can change an alpha nobody can see.

The square is `$planeSize` on each side and there is no `size` prop: it is a composite with no one
height, so an application that wants a bigger square redefines `$planeSize`. The two gradients over
it are the `colorpicker_plane` entry's, and the hue track's six hues are `colorpicker_track_hue`'s.
What the component writes is the colour that is chosen now, which does not exist until it runs: the
hue under the square, the thumb's own fill and the opacity track are inline `style`. The theme check
reads source text, and each of those is arithmetic rather than a value anybody typed, so there is
nothing there for it to refuse.

**A state prop takes a cell.** `open`, `enabled`, `value` and `files` are cells or absent; give one a
plain value and it is a loud assert naming the prop and the fix, because a component that quietly
kept a cell of its own would look as though it had honoured what you asked for.

**The theme entries these add**, on top of the ones above: `dialog` with its `::backdrop`, `tooltip`,
`disclosure` and `disclosure_summary`, `filedrop` with `dragging`, `prompt`, `list` and `entry`,
`validate`, and `colorpicker` with `swatch`, `plane`, `plane_thumb`, `track` and `hue`. The country
fields add `chooser` and its parts: `wrap`, `chosen`, `panel`, `head`, `search`, `grid`, `option`
with `selected`, `active` and `suggested`, `flag`, `lines`, `note` and `none`. `offscreen` is one more, and it is
yours to use: it takes an element off the screen and leaves it in the reading order.

Each of them has a page on
[`recipes/ui/catalogue.html`](/docs/recipes/ui/files/catalogue.html),
in both modes, driven in Chromium by
[`recipes/ui/main.ts`](/docs/recipes/ui/files/main.ts) with
axe-core over it.

## Text

```tsx
import { TextModifiers, Typography } from '@aweftjs/ui';
```

| export | what it is | its own props | example |
|---|---|---|---|
| `Typography` | one run of themed text: one element on the `text` entry | `type`, `label`, `element`, `theme` | `typography` |
| `TextModifiers` | a context holding the list `Typography` runs over its label | `value` | `typography` |
| `TypographyProps` | what `Typography` takes | | |
| `TextModifier` | one entry of that list: `{ check, return }` | | |

**`type` is theme segments joined by `_`, and the first segment also picks the element.**
`<Typography type="h2_bold" label="Today" />` is an `<h2>` themed `text h2 bold`, so `text_h2` and
`text_bold` both apply and the later segment wins where they disagree. `h1` to `h6` give that
heading, `p`, `p1` and `p2` give a `<p>`, and everything else gives a `<span>`, so a word this
package never defined is a segment like any other and your own `text_eyebrow` needs nothing from
here. With no `type` it is a `<span>` on `text` alone. `type` may be a cell: the theme follows it,
and the element does not, because an element lasts as long as the component.

**`label` and `children` both render, label first**, and neither is required. `label` may be a
cell. `element` hands in the node to decorate instead of building one, which is how a heading's
look goes on a level the document outline wanted instead.

**`TextModifiers` turns parts of a label into something else.** A modifier is
`{ check, return }`: `check` is a plain string, matched everywhere and case-insensitively, or a
global regex, and `return(match)` answers whatever that piece becomes. Matches are collected in the
order the modifiers are written, sorted by where they start, and an overlap goes to the one that
started first, a tie to the one written first. A gap between matches is text.

```tsx
<TextModifiers value={[{ check: /@\w+/g, return: (name) => <Tooltip label={who(name)}>{name}</Tooltip> }]}>
	<Typography type="p1" label={note} />
</TextModifiers>
```

Keys beyond those two are ignored, so a list written for something else passes through. A provider
replaces the list above it rather than adding to it; a subtree that wants both writes both. A list
held in a cell is read when each `Typography` below it mounts, as `Icons` is; a later write reaches
what mounts after it. The
list runs over `label` only, because a child is already markup and has nothing for a pattern to run
over, and a label that is neither a string nor a number renders as given. A regex without the `g`
flag is a loud assert naming the flag, because a pattern that finds one match is almost never what
was meant. A cell label runs the pass again when it changes.

**The label goes through one resolve step first.** A text token resolves to its string in the
render's language before the modifiers run, so a modifier matches the translated word; anything
else is the label as given (design 278).

**What it never does.** No editing: nothing swaps an input in on a click and nothing measures text
with a span, because editing is `TextField`'s job and a page composes the two. No width cap: a
measure is yours, as `maxWidth` on `text_p1`. No fonts: the theme engine already emits `@font-face`
and `@import` from your own theme's directives.

[`recipes/ui/preview.html`](/docs/recipes/ui/files/preview.html)
shows the whole family in both modes, and the gallery's modifier demo is
[`recipes/ui/page.tsx`](/docs/recipes/ui/files/page.tsx), both
driven by
[`recipes/ui/main.ts`](/docs/recipes/ui/files/main.ts).

## The text a page shows

```tsx
import { context, isText, localeOf, mount, text, textOf } from '@aweftjs/ui';

mount(document.body, <App />, undefined, context({ locale: 'fr', catalog: fr }));
```

`text('Save')` is a token: what a string a person reads becomes, so it can be looked up where the
page mounts. With the build's `text` option on ([`packages/build/README.md`](/docs/packages/build)), every literal a page
shows is one already, and a page writes the call itself for a string built in code, a plural, or a
sentence with an element inside it. A render with no `catalog` shows every token's source, so a
page written this way runs before it has a second language.

| export | what it is |
|---|---|
| `text(source, values?)` | a token: mountable as a child, and resolved by `h` in a prop, by `Typography` in a label, and by a head component in a tag |
| `context({ locale, catalog })` | the language a render shows and its translations, a plain object from key to message |
| `textOf(context, value)` | the string a token, a string or a number shows in a render, for code that needs characters: a document title set by hand. Takes the mount context or the render `context()` made, so a server module resolves against a render of its own |
| `localeOf(context)` | the render's BCP 47 tag, for a date or a number formatted with `Intl` |
| `isText(value)` | whether a value is a token |
| `usedText(render)` | the keys a render's tokens looked up |

**A token resolves where it mounts.** As a child it is a component, so `dom` mounts it and a
hydration pairs it beside a static sibling. An act module's `title` may be a token too, and the
stage announces it in the page's language. In any prop, `h` claims it the way it claims `theme`:
the resolved string is written into a cell `dom` binds under the same name, so `placeholder`,
`title`, `alt` and `aria-label` carry the translation on the server's node after a hydration and
on a fresh one after a mount. `Typography` resolves a token `label` before its modifiers, and a
head component resolves a token child or attribute when it declares its tag. A token that mounts
under `dom`'s own `mount`, with no `ui` systems, shows its source. The key is the source string,
or `source|context` when the call names a `context` word: `text('Close', { context: 'dialog' })`
is the key `Close|dialog`, which is how one word with two meanings gets two entries.

**The message syntax** is a subset of ICU MessageFormat, read here with no dependency:

| written | means |
|---|---|
| `Hello {name}` | the value under `name`; a cell is followed |
| `{n, plural, one {# item} other {# items}}` | the branch `Intl.PluralRules(locale)` picks for `n`, `=0` and friends matching the exact number first; `#` is `n` formatted for the locale; `other` is required |
| `{kind, select, book {a book} other {a thing}}` | the branch named by the value of `kind`, `other` when none matches; `other` is required |
| `Read <link>the docs</link>` | the value under `link`, a function from the inner content to what to mount; with no function under that name the inner content stands alone |
| `'{'`, `'}'`, `'<'`, `'#'`, `''` | that character; an apostrophe quotes up to the next apostrophe, and stands for itself in front of anything else |

Branches nest. A message that cannot be read is a loud assert naming the offset and what was
expected there; in a release build it shows as written. Every parse is cached by source.

```tsx
<p>{text('{n, plural, one {# item} other {# items}}', { n: count })}</p>
<p>{text('Read <link>the docs</link>', { link: (inner) => <a href="/docs">{inner}</a> })}</p>
<Button label={text('Save')} />
```

**A cell among the values is followed.** A plural over a count (`mutable(1)` from
`@aweftjs/core`) re-renders as the count moves, through one subscription per cell; a token with
plain values costs no subscription at all.

**This package's own strings are tokens too**, and `text.json` beside its `package.json`,
reachable as `@aweftjs/ui/text.json`, lists their keys, so an application's catalog can carry
`Close`, `Search`, `Previous` and the rest beside its own words. It ships no translation. The
build folds those keys into the source catalog it writes. `auth` ships the same for its sign-in
form.

**What it never decides.** Which language a visitor gets: a page reads a URL, a header, a cookie
or a setting and hands `context()` the answer. Where a catalog comes from: a file the entry
imports, a document a store holds, or an object the page built; this package reads none of them.
No fallback chain: `fr-CA` is one catalog, not `fr` with overrides, and an application that wants
the chain merges the two objects. The language is fixed per render: a switch is a remount or a
navigation, never a cell every token follows.

**`text` is also the name of the theme family `Typography` renders on.** A theme entry and an
export are different namespaces, and both are the plain word for what they are.

[`recipes/translated-site`](/docs/recipes/translated-site)
is a site written once and launched in three languages, hydrated in Chromium in each.

## Markdown

```tsx
import { Markdown } from '@aweftjs/ui';

<Markdown source={readme} />
```

| component | what it is | its own props | example |
|---|---|---|---|
| `Markdown` | a `<div>` on `markdown` holding one element per block of a markdown string | `source`, `modifiers`, `code`, `element`, `theme` | `markdown` |

**What it renders** (design 288). A markdown string, or a cell holding one, as themed blocks
whose text runs are `Typography`, for the subset a README uses:

| written | rendered as |
|---|---|
| `#` to `######` | `Typography` `h1` to `h6` on `markdown_heading`, with an `id` in GitHub's scheme: lowercased, marks and punctuation dropped, spaces to hyphens, a repeat suffixed `-1` |
| a paragraph | `Typography` `p1` on `markdown_paragraph`; lines joined by a space, two trailing spaces a line break |
| a fenced block | a `<pre>` on `markdown_code` with the language on `data-language`, holding what `code` answers; focusable, because it scrolls sideways |
| `-`, `*`, `+` or `1.` items | a `<ul>` or `<ol>` on `markdown_list` (`ordered` for the second, with `start` when the first number is not 1), each item an `<li>` on `markdown_item` |
| `- [ ]` and `- [x]` | an item on `markdown_item_task` with a `Checkbox` that follows the source |
| a table with a delimiter row | a `<table>` on `markdown_tabular` in the `table_scroll` box, with the `table_head`, `table_line`, `table_heading` and `table_cell` parts; `:--:` and `--:` align a column |
| `>` lines | a `<blockquote>` on `markdown_quote`, one paragraph |
| `---`, `***`, `___` | an `<hr>` on `markdown_rule` |

Inline, a code span (one or two backticks, the double form holding a backtick) is `<code>` on
`markdown_inline`; `**bold**` and `__bold__` are `<strong>` on `markdown_bold`; `*italic*` and
`_italic_` are `<em>` on `markdown_italic`; `***both***` is the two nested; `[text](https://github.com/torrinworx/aweft/tree/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/href)` is
`<a>` on `markdown_link` with the `href` as written, unless its scheme is not `http`, `https`,
`mailto` or `tel`, in which case the link is text. Each is a modifier in `TextModifiers`' shape,
listed after `modifiers`, so an application's own patterns run inside markdown with nothing
wired, inside a bold run included:

```tsx
<Markdown source={note} modifiers={[{ check: /@\w+/g, return: (who) => <Mention name={who} /> }]} />
```

With no `modifiers` prop the list is the render's `TextModifiers`; a cell is read when the
component mounts, as `TextModifiers` reads one. An overlap goes to the match that started first,
so a code span holding an asterisk is a code span and a bold run holding a code span is both,
and emphasis of mixed length that opens inside another (`***a** b*`) is read outermost first
rather than the way CommonMark reads it.

**What is text.** A nested list (an indented item joins the item above it, as written), an
image, a footnote, an HTML tag, an autolink, a reference link, a setext heading, an indented
code block and a backslash escape each stay in the paragraph as the characters written. HTML is
never markup: a paragraph is a text node and nothing here sets `innerHTML`, so an untrusted
string renders as text.

**The `code` hook** is `(text, language) => anything mountable` and is called once per fenced
block; absent, the block holds a `<code>` with the text. Highlighting is the application's, and
this package ships none.

**A cell source re-renders every block when it changes**; there is no diff. A task item's box is
disabled unless the source is a writable cell, and then a tick rewrites that item's `[ ]` or
`[x]` in the source and sets the cell, so a document a page shares is what moved.

**The theme** is the `markdown_*` entries above, every value a role or a size name, so both
modes come from the one set and an application overrides an entry the way it overrides
`button`. Every heading, paragraph, cell and item text is on the `text` family too, so
`text_h2` and `text_p1` reach them as they reach any `Typography`.

**What it never decides.** How code is highlighted. Where a link goes, or whether it opens
elsewhere. What HTML means. The width of a column or a block. Whether the source is trusted:
HTML in it is text, and a link whose scheme would run something is text, so an untrusted
string renders without running anything. CommonMark conformance: the subset above is what is read,
and the [`Markdown`](/docs/recipes/ui/files/catalogue.html)
example in the catalogue shows every form of it in both modes.

## Display

Six things a page shows and nobody operates (design 199). Each is one native element with a theme
on it, none takes a handler, and none takes a value the way a control does.

```tsx
import { Alert, Avatar, Badge, Empty, Progress, Skeleton } from '@aweftjs/ui';
```

| component | the element | its own props | `size` | example |
|---|---|---|---|---|
| `Badge` | `<span>` on `badge` | `label`, `type` (`quiet`, `danger`, `success`, `outline`), `icon`, `element` | `sm`, `lg` | `badge` |
| `Alert` | `<div role="alert">` or `<div role="status">` on `alert` | `title`, `icon`, `type` (`danger`, `success`), `element`, children as the body | | `alert` |
| `Avatar` | `<span>` on `avatar`, holding an `<img>` and a `<span>` | `src`, `alt`, `fallback`, `round` | `sm`, `lg`, or any CSS length | `avatar` |
| `Skeleton` | `<div aria-hidden="true">` on `skeleton` | `width`, `height`, `round` | | `skeleton` |
| `Progress` | `<progress max="1">` on `progress` | `value` (0 to 1, or nothing), `label`, `element` | `sm`, `lg` | `progress` |
| `Empty` | `<div>` on `empty` | `icon`, `title`, `description`, `element`, children as the actions | | `empty` |

**An avatar shows its letters until its picture loads, and again if it fails.** Both children stay
in the tree and whichever is not showing carries `hidden`, so it is out of the accessibility tree as
well as off the screen. `round` is true unless you set it false.

With a plain `src` of nothing there is no `<img>` at all. With a `src` that is a cell the `<img>` is
in the markup from the first paint whatever the cell holds, and its `src` follows the cell: a cell
that has not resolved yet is not the same as no picture.

**`size` takes a CSS length as well as `sm` and `lg`** (design 215), the way `Icon`'s does. A step
name is a theme segment; a length is written as the element's width and height. The fallback letters
are 40% of the box, through a container query on the `avatar` entry, so they follow it at every size
and at any length. Move `$avatarLetter` to change the proportion.

**A progress with no value is indeterminate.** `value` is a fraction of 1, so nothing has to divide;
`null` or nothing leaves the attribute off, which is what the platform reads as waiting and draws as
the moving bar. A number outside 0 to 1 is clamped to it, and anything that is not a finite number,
`NaN` and a string of digits included, is indeterminate rather than written through. Give it a
`label`: without one it has no name for a screen reader.

**A skeleton announces nothing.** It is `aria-hidden`, because the thing that is loading is what
says so and three grey boxes saying it three times is worse than silence. `width` and `height` go
through `style`, so a bare number is pixels and any CSS length works.

**A badge's size is padding and text, not a height.** A badge is not a control, so `$control` is
the wrong number for it: `sm` tightens the padding and `lg` widens it and takes the next text step.

**An alert's icon is yours.** This package ships no drawings, so an `Icon` by name needs an `Icons`
provider above it and there is no default icon here. The box is one column until you give it one.

**`danger` interrupts and `success` waits.** A `danger` alert is `role="alert"`, so a screen reader
breaks off to read it; every other type, `success` included, is `role="status"` and waits its turn
(design 216). A thing that went right is not an interruption.

## Grouping

One component whose whole job is what it puts around other components (designs 200, 211).

```tsx
import { Card } from '@aweftjs/ui';
```

| component | the element | its own props | example |
|---|---|---|---|
| `Card` | `<div>` on `card`, with the `stack` segment when it has parts | `title`, `description`, `foot`, `type`, `tight`, `element`, children as the body | `card` |

**A `Card` with none of `title`, `description` and `foot` is the bare block**: the `card` entry, and
the children directly inside it (design 211). Given any of the three it takes the `stack` segment
and builds the parts: the head, the body around your children, the foot, and `$space4` between them.
Each part renders only where it was given something.

```tsx
<Card><h2 theme={['text', 'lg']}>Today</h2><p theme="text">Nothing yet.</p></Card>
<Card title="Today" description="What is due" foot={<Button label="Add" />}>Nothing yet.</Card>
```

`tight` drops the padding, for a card whose children reach the edge. Write `<Card theme="stack">`
to get the column spacing on a card that has no parts.

**A row of buttons joined into one control is yours to write** (design 212). There is no component
for it: a class list is written by the element that wears it, so a group could never put a segment
in its children's lists anyway.

**A text field with something beside it inside the same box is `TextField`'s `leading` and
`trailing`** (design 210), not a component of its own. See Controls.

## Navigation and data

Where a person is, and what they are looking at (designs 201, 203).

```tsx
import { Breadcrumb, Pagination, Tab, TabPanel, Table, Tabs } from '@aweftjs/ui';
```

| component | the element | its own props | example |
|---|---|---|---|
| `Table` | a `<table>` on `table`, inside a `<div>` on `table_scroll` | `columns`, `rows`, `cell`, `caption`, `foot`, `label`, `striped`, `tight`, `type`, `element` | `table` |
| `Breadcrumb` | a `<nav>` on `breadcrumb` around an `<ol>` | `items` of `{ label, href }`, `label`, `element` | `breadcrumb` |
| `Pagination` | a `<nav>` on `pagination` of quiet `Button`s | `page` (a cell), `count`, `siblings`, `onChange`, `size`, `label`, `element` | `pagination` |
| `Tabs` | a `<div>` on `tabs` around a `<div role="tablist">` on `tabs_list` and the panels | `value` (a cell), `tabs` of `{ value, label, disabled, content }`, `orientation`, `type` (`line`), `size`, `label`, `onChange`, `element` | `tabs` |
| `Tab` | a `<button type="button" role="tab">` on `tab` | `value`, `label`, `disabled`, `element` | `tabs` |
| `TabPanel` | a `<div role="tabpanel" tabindex="0">` on `tabs_panel` | `value`, `element` | `tabs` |

**The table's entries work without the component.** Write your own
`<table theme="table">` with `<thead theme="table_head">`, `<tr theme="table_line">`,
`<th theme="table_heading">` and `<td theme="table_cell">` and you get the whole look. `Table` is
the common case: `columns` is a list of `{ key, label, align, width }` or of plain strings, `rows`
is a list or a cell of one, and `cell` is `(row, column) => anything mountable`, defaulting to
`String(row[column.key])`.

**Rows go through `each`, so pushing one inserts one `<tr>`.** A `rows` cell holding anything but a
list, which is what one holds before the first fetch answers, reads as no rows.

**A `cell` may return a different thing on every row.** A button on the rows that can be acted on
and nothing on the rest, text on some and a `Badge` on others: `cell` is called per row and what it
returns is mounted per row, so nothing about the one-shape rule under `each` reaches it.

```tsx
<Table rows={files} columns={[{ key: 'name', label: 'Name' }, { key: 'act', label: '' }]}
	cell={(row, column) => (column.key !== 'act' ? row.name
		: row.mine ? <Button label="Delete" type="quiet" size="sm" onClick={() => remove(row)} /> : null)} />
```

What `each` asks one shape of is a row component you write yourself, which is a different thing;
[`@aweftjs/dom`](/docs/packages/dom)'s README
says what that costs.

**A wide table scrolls in its own box**, which carries `tabindex="0"` so a keyboard can reach the
scroll. Give the table a `caption` or a `label`: without one it has no name for a screen reader.

**A breadcrumb's links are plain anchors with no `target`.** That is what lets a router take the
click: `createRouter(...).links(root)` intercepts same-origin anchors and leaves alone anything with
a target. So a breadcrumb inside a routed page navigates with no reload and no handler.

**Pagination shows the first page, the last, and `siblings` each side of the current one**, with an
`aria-hidden` ellipsis where a run was left out. `page` is a cell counted from 1; previous is
disabled on page 1 and next on the last. The page showing now carries `aria-current="page"`.

**A page outside the count is clamped, and the cell is written with the clamp.** `count` shrinking
under the page it was on is what a filter does every time, and the page it leaves behind has no
button, no `aria-current` and a dead Next. So the component moves to the last page, writes `page`
and calls `onChange` with it (the event argument is null, because nobody pressed anything). A
`count` of 0 renders no page buttons and both arrows off, and writes nothing: there is no page to
be on.

**A strip of tabs is one stop in the Tab order, and the arrows move inside it** (design 203). Tab
lands on the tab showing, and the next Tab leaves the strip for that tab's panel rather than walking
to the next tab. Right and Left move one tab, wrapping at each end; Down and Up do instead when
`orientation` is `vertical`; Home and End go to the ends. A `disabled` tab is stepped over rather
than landed on, and it says so with `aria-disabled` so a screen reader still reads it out.

**Arriving on a tab chooses it.** The arrows move the selection as well as the focus, so holding
Right shows each panel in turn. That is what the roving `tabindex` is for: the tab showing carries
`0` and every other carries `-1`. All of it is one internal behaviour, `tablist.ts`, the fifth
beside the field wiring, the dismiss, the dialog and the tooltip trigger (design 129); none of the
five is exported, and each is tested once on its own.

```tsx
<Tabs label="Views" value={view} tabs={[
	{ value: 'all', label: 'All', content: <All /> },
	{ value: 'mine', label: 'Mine', content: <Mine /> },
]} />
```

**Every panel stays mounted, and the ones not showing carry `hidden`**, so coming back to a panel
finds it as it was left. Wrap a panel's contents in a `Shown` to have them built again instead.

**`tabs` is the common case; two marks are the long way.** The tabs go inside the strip and the
panels go outside it, and a component may not read another component's props to tell them apart, so
a caller who writes them out says which is which:

```tsx
<Tabs label="Sections">
	<mark.tabs><Tab value="left" label="Left" /></mark.tabs>
	<mark.panels><TabPanel value="left">the first</TabPanel></mark.panels>
</Tabs>
```

With no `value` the component keeps a cell of its own and starts on the first tab, and a cell you
passed holding nothing is left holding nothing: choosing for you would be a write you did not ask
for. Each tab and its panel name each other with `aria-controls` and `aria-labelledby`, off ids
minted from the render's counter, so a page rendered on a server and the hydration that adopts it
agree.

**The tab showing leaving the `tabs` list moves the selection to the first tab anyone can choose.**
Otherwise the value names nothing: every tab reads `tabindex="-1"`, so the strip is not in the Tab
order at all, and every panel is `hidden`, so the page shows nothing. The cell is written and
`onChange` is called with the new value (the event argument is null). With every tab `disabled`,
nothing is chosen and the first tab keeps the stop, so the strip is still reachable. Tabs written
out in the two marks are your own markup and are not read this way; taking one out is a change you
made to your own tree.

**A `TabPanel` whose `value` no `Tab` has is refused**, because its `aria-labelledby` would name an
id that is not on the page, which no browser reports.

## The look

A default theme ships in light and dark. It is what makes `theme="button"` a button, and it is the
contract a component of this package is allowed to use. Designs 115 to 120 are the whole of it.

**Four colour scales**, `neutral`, `accent`, `danger` and `success`, twelve steps each, `$neutral1`
to `$neutral12` and so on. The steps follow one job list: 1 and 2 are backgrounds, 3 to 5 component
fills by state, 6 to 8 lines, 9 and 10 solids, 11 and 12 text.

**Twenty-two roles**, each set from one step. A component uses a role, never a step:

| role | pairs with | what it is for |
|---|---|---|
| `$background` | `$foreground` | the page |
| `$surface` | `$surfaceForeground` | a raised block |
| `$muted` | `$mutedForeground` | a quiet fill, and quiet text on any background |
| `$accent` | `$accentForeground` | a filled control |
| `$accentSubtle` | `$accentSubtleForeground` | a tinted control |
| `$danger` | `$dangerForeground` | a destructive control |
| `$dangerSubtle` | `$dangerSubtleForeground` | a warning block |
| `$success` | `$successForeground` | a thing that went right |
| `$successSubtle` | `$successSubtleForeground` | a block saying so |
| `$border` | | the line around a block |
| `$input` | | the edge of a control |
| `$ring` | | the focus ring |
| `$link` | | text that goes somewhere |

Every pair meets WCAG 2 AA in both modes, 4.5:1 for text and 3:1 for a line, asserted in
[`packages/ui/tests/contrast.test.ts`](https://github.com/torrinworx/aweft/blob/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/tests/contrast.test.ts)
with a ratio the test computes itself.

**The default is monochrome.** `$accent` is the neutral scale's text step and `$accentForeground`
is its page step, so a filled button is near-black on near-white in light and the same line read
the other way round in dark. `$ring` and `$accentSubtle` are neutral too. The accent scale is
still defined, all twelve steps of it, and `$link` is the one role that uses it. Colour is a
decision your application makes: one provider at the root redefining `$accent` and
`$accentForeground` puts it back on every filled control, because no component names a step.
`$danger` is unchanged and still red, and `$success` is its counterpart in green (design 216): the
two tones are for a message about what happened, and no control here is coloured by either.

**Type** is `$textXs`, `$textSm`, `$textMd`, `$textLg`, `$textXl`, `$text2xl`, `$text3xl` and
`$text4xl`, in `rem`, each with `$textXsLine` and so on beside it. `$font` and `$fontMono` are the
families.

**Sizes** are `$space` (4px) and its six multiples, `$space2`, `$space3`, `$space4`, `$space6`,
`$space8` and `$space12`, with no step between them; `$radiusSm`, `$radius` and `$radiusLg`;
`$controlSm` (32px), `$control` (36px) and `$controlLg` (40px); `$target` (24px, the smallest a
pointer target may be, which sizes the things that are not controls); and `$borderWidth`,
`$ringWidth` and `$shadowSm`.

**Every control is `$control` tall.** A button, a text field, a select, a slider's hit area and
the row a checkbox sits in beside its words are all 36px, so a line of controls is a line. A text
area is sized by what is in it and keeps a minimum of its own. `$controlSm` and `$controlLg` are
the same rule at the two other sizes.

**The height is the box, so your own padding fits inside it rather than adding to it.** Controls
declare `box-sizing: border-box`, which is what makes the declared height the height whatever
element wears the entry. A `TextField` given `padding: '12px'` through a theme override is still
36px tall, with a 10px content box; `padding: '20px'` is more than fits, so the control grows to
42px. Before this the padding was added to the height every time. `$target` is the smallest a pointer target may be and now
sizes nothing in this theme: it is the number your own entries reach for.

**Motion** is `$fast` (150ms), `$slow` (240ms), `$ease` (`cubic-bezier(0.4, 0, 0.2, 1)`) and
`$easeOut`. The root sets one transition for every themed element, over `background-color,
background-image, border-color, color, transform`, and `popup`, `dialog`, its `::backdrop` and
`tooltip` each set one more for the opacity and scale they arrive with (design 192). `box-shadow`
is deliberately not on the list: the focus ring is drawn as one, and a ring that fades in is a
ring that is not there yet (design 217). Every one of these is written inside `@media
(prefers-reduced-motion: no-preference)`, so a person who asked for less motion gets none and no
component has to remember the query. The default theme reads neither `$slow` nor `$easeOut`; they
are there for you.

**Three things move that are not a colour.** A switch's thumb crosses its pill over `$fast`, a
slider's thumb grows under the pointer over `$fast`, and a skeleton and the loading dots run their
own cycles: a skeleton breathes between full and half opacity over 2s, and the three dots run a 1s
cycle a third apart each, so the bright point travels along the row (design 218). The two thumbs
are pseudo-elements, which the root's transition on the element does not reach, so each declares
its own transition. The skeleton and the dots are not transitions at all: each is an animation on
the element itself, with its own keyframes, and the same query around it.

**States are one rule.** `hovered` and `pressed` lay a translucent tint of the element's own
foreground over whatever background it has, at two fixed strengths. No component names a hover
colour, and there is no ripple. `disabled` clears the tint.

**Focus is one rule.** The root gives every themed element, on `:focus-visible`, a border in
`$ring` and a `$ringWidth` halo of `$ring` at half strength, drawn as a box shadow. An outline is
drawn outside the border box and cannot be soft, so it reads as a second border; the halo reads as
focus. The focus rule is the one place in this package that writes `outline: none`, and it names
`$ring` twice in the same block, which is what the gate check asks of an entry that turns an
outline off.

**The border is what meets the contrast target, not the halo.** `$ring` on `$background` is 6.08:1
in light and 7.39:1 in dark, well past the 3:1 a non-text indicator has to reach. The halo as it is
actually painted is `$ring` at 50% over whatever is behind the control, which measures 2.15:1 in
light and 2.74:1 in dark. So the halo is what makes the focus easy to see and the border is what
makes it pass; a theme that keeps the halo and drops the border colour has an indicator nobody has
measured.

**Disabled dims.** `opacity: 0.5` and `cursor: not-allowed`, with the state tint off. It does not
repaint the control, because a disabled danger button repainted in `$muted` is no longer the
control it is.

**Inputs and quiet buttons carry a hairline.** `$shadowSm` is one pixel of offset and two of blur
in the element's own foreground at 6%. It is an edge, not elevation: nothing here lifts a block
off the page with a shadow.

**Overlays arrive.** `dialog`, `popup` and `tooltip` transition their opacity and a small scale in
over `$fast`, from an `@starting-style`, inside the reduced-motion query. The dialog leaves the
same way and its `::backdrop` fades with it. A popup and a tooltip arrive and do not leave, because
what hides either is a `display: none` written on the box the popup sink places, above the element
the theme reaches.

**The tick box, the radio and the select's arrow are drawn here** (design 195). Each is still the
native element: `appearance: none` takes the host's drawing and leaves the keyboard, the form and
the label. A tick box is a `$box` square with `$radiusSm` corners and the `$input` edge, filled
`$accent` when it is ticked, and the tick is two sides of an empty `::before` turned 45 degrees in
`$accentForeground`. Indeterminate is a bar. A radio is the same box as a circle with a centred
dot. A select's control says `appearance: none`, so no host draws an arrow on it, and the component
puts an empty box at the right of the control for `select_chevron` to draw one in, the same way: a
`$chevron` square with two of its sides turned a quarter turn. Nothing here is an image, an icon
pack or a name asked of one.

**The entries it ships.** These are the theme names a component of this package, or of yours, can
ask for. Everything else is yours to define.

| entry | what it is for |
|---|---|
| `button` | a filled control: the accent fill, its foreground, `$control` tall |
| `button_sm`, `button_lg`, `button_square` | the size axis, and a square for a button whose label is an icon. `size="icon"` is the prop; `square` is the segment, because `icon` is an entry of its own |
| `input_sm`, `input_lg`, `select_sm`, `select_lg`, `checkbox_sm`, `checkbox_lg`, `radio_sm`, `radio_lg`, `toggle_sm`, `toggle_lg`, `slider_sm`, `slider_lg` | the same axis on the rest of the controls |
| `button_quiet` | the same control with no fill: a border and accent-subtle text |
| `button_danger` | the same control in the danger colours |
| `input` | a text field: the surface fill, the `$input` edge, the hairline, a placeholder in `$mutedForeground` |
| `input_invalid` | that field with a danger edge |
| `select` | `input` again on a `<button>`, laid out in a line, with room on the right for its own arrow |
| `card` | a raised block: the surface fill, a border, the larger radius, `$space4` of padding |
| `popup` | the same block at popup size: the smaller radius, tighter padding |
| `text` | body copy at `$textMd`, with no margin, and a newline kept as a line break |
| `text_xs`, `text_sm`, `text_lg`, `text_xl`, `text_2xl`, `text_3xl`, `text_4xl` | that copy, one size step at a time |
| `text_h1` to `text_h6` | a heading: one step of the scale from `$text4xl` down to `$textMd`, at weight 600, with balanced lines |
| `text_p1`, `text_p2` | a paragraph at `$textMd` and at `$textSm`, with no lone last word and no width cap |
| `text_bold`, `text_regular`, `text_italic`, `text_center`, `text_inline` | one declaration each, to put after any of the above |
| `text_mono` | that copy in `$fontMono` |
| `muted` | text in `$mutedForeground`, readable on any of the three backgrounds |
| `button_round`, `button_inline` | that control as a circle, and as a run of `$link` text with no fill or padding at all |
| `textarea` | the text field again, growing to its content up to `$textAreaMax` |
| `checkbox`, `radio` | a `$box` square and a circle, drawn here: the box, the tick and the dot |
| `field_label`, `field_hint`, `field_error` | a control's label, the line under it, and its message. Parts, so each is one class token. A label under a `[data-invalid]` field takes the message's colour |
| `select_wrap`, `select_chevron` | the box a select's own arrow is placed in, and the arrow: a `$chevron` square with two sides drawn, turned a quarter turn |
| `toggle` | a switch: a pill and a thumb drawn with `::before` |
| `slider` | a range input: the track and the thumb on the vendor pseudo-elements, with `$space` of room at each end for the thumb |
| `slider_hovered`, `slider_pressed` | the one entry pair that answers the two state segments itself: the root tint goes off the box, the track takes the tint and the thumb scales (design 220) |
| `listbox` | the list a select opens: the popup's surface with rows in it, scrolling past `$listMax` |
| `listbox_item` | one row of it, a `$control`-tall line. `listbox_item_selected` is the chosen one and `listbox_item_active` is the one the keys and the pointer are on |
| `menu`, `menu_item` | the same list and the same row for a `Menu`'s actions |
| `menu_item_danger`, `menu_heading`, `menu_group` | the dangerous row in `$danger`, a group's small heading in `$mutedForeground`, and the column a group is |
| `card_tight` | that card with no padding |
| `field`, `field_inline`, `field_responsive` | what a labelled control puts around itself, and what a form is laid out with: a column, a `$control`-tall row, or a column that turns into one from 28rem of its container |
| `field_group`, `field_set`, `field_legend` | the stack a form is, a fieldset with the host's frame taken off, and its heading |
| `dots`, `dot` | the three dots of `LoadingDots`, and one of them: a 1s wave, the second and third a third of a cycle behind |
| `pulse` | a slow breathe over `$pulseCycle`, which `skeleton` extends and any block of yours can |
| `badge` and its `quiet`, `danger`, `success`, `outline`, `sm` and `lg` | a short label on a fill, sized by its padding and its text rather than by `$control` |
| `alert`, `alert_lead`, `alert_danger`, `alert_success`, `alert_symbol`, `alert_title`, `alert_body` | a message about the page: a grid of one column, two when it was given an icon |
| `avatar`, `avatar_image`, `avatar_fallback` and its `sm`, `lg`, `round` | a picture of a person and the letters shown without one; the part that is not showing carries `hidden`, and the letters are `$avatarLetter` of the box |
| `skeleton`, `skeleton_round` | a grey box standing in for something that has not arrived, breathing on `pulse` |
| `progress` and its `sm` and `lg` | a bar filling up, drawn on the three vendor pseudo-elements |
| `empty`, `empty_symbol`, `empty_title`, `empty_description`, `empty_actions` | nothing here yet, and what to do about it |
| `card_stack`, `card_head`, `card_title`, `card_description`, `card_body`, `card_foot` | what a `Card` adds when it was given a title, a description or a foot; one with none of the three reaches none of them |
| `button_current` | the one of a run that is showing now: the page a `Pagination` is on |
| `table`, `table_scroll`, `table_head`, `table_line`, `table_heading`, `table_cell`, `table_caption`, `table_foot` and the `striped`, `right`, `center`, `tight` modifiers | a table: collapsed borders, a hairline under the head and under each row, and a box a wide one scrolls in. The row's part is `line` and not `row`, because `row` is an entry of its own |
| `breadcrumb`, `breadcrumb_list`, `breadcrumb_item`, `breadcrumb_link`, `breadcrumb_current`, `breadcrumb_separator` | a trail of links and the chevron between them, drawn out of the same `$chevron` box the select's arrow is |
| `pagination`, `pagination_gap` | a row of page buttons, and the ellipsis where a run was left out |
| `input_group`, `input_group_control`, `input_group_addon` and its `sm`, `lg`, `invalid` | the `input` look on the box a `TextField` with an addon builds, and the input inside it with none of it |
| `icon` | an icon, sized in `em` so it follows the text |
| `row`, `column`, `divider` | laying things out in a line, with the seven modifiers above |
| `dialog`, `dialog_head`, `dialog_body` | a modal dialog, its heading row and its content; the scrim is on `::backdrop` |
| `dialog_sheet` and its `left`, `right`, `top`, `bottom` | the same dialog against an edge, anchored by margin and sliding in from it |
| `tooltip` | a tip: the page's own colours the other way up |
| `disclosure`, `disclosure_summary` | a `<details>` and the summary that wears the `button` entry |
| `filedrop` and its `dragging`, `prompt`, `input`, `list` and `entry` | a drop zone, its prompt and its listing |
| `validate` | the message a `Validate` shows, beside its icon |
| `colorpicker`, `colorpicker_swatch`, `colorpicker_plane`, `colorpicker_plane_thumb` and its `hovered` and `pressed`, `colorpicker_track` and its `hue` | the saturation and brightness square, its thumb, the sliders beside it and the colour they name |
| `offscreen` | off the screen and still in the reading order |

`hovered`, `pressed` and `disabled` are three more entries, and you put them in a class list
yourself: `theme={['button', hovered.bool('hovered', null)]}`. They match anywhere, so they apply
to any entry above. They are meant for the controls, `button` and its two variants, `input` and its
variant, and `select`; `card`, `popup`, the `text` sizes and `muted` have no state to show.

`slider` is the one entry that answers the first two itself. The tint is a rectangle the width of
the row and a slider's thumb is a 16px circle inside it, so `slider_hovered` and `slider_pressed`
turn the tint off the box and put the state where the control is: the track takes the tint and the
thumb scales, on both vendor pseudo-elements (design 220). Write a `slider_hovered` of your own to
have it back. Any entry of yours can do the same: two segments beat one in the chain.

**Your root entry sets the page's background and its colour.** The root `*` entry of this theme
sets neither, because it is on every themed element: a colour written there lands on an element
inside a control as well as on the control, and an `Icon` inside a filled `Button` took the page's
foreground on the button's own fill, which is an icon nobody can see (design 198). So the page's
own entry says both, once, and everything under it inherits:

```ts
Theme.define({
	page: {
		// As tall as the viewport, so a short page is painted to the bottom. The host's own body
		// margin still shows as a band of its default colour around the entry, white against a dark
		// page, and a theme entry cannot reach `body`: the page's HTML takes it off with one line,
		// `<style>body { margin: 0 }</style>`.
		minHeight: '100vh',
		background: '$background',
		color: '$foreground',
	},
});
```

Without one the page reads in the host's own default colours, which is right in light and wrong in
dark, so a page that offers dark mode needs this entry.

**Every themed element honours `hidden`** (design 207). The root entry carries the rule inside
`@layer aweft`, because an unlayered rule loses to a layered one whatever its specificity and every
entry that lays an element out declares a `display` in there. So `hidden` on a themed element takes
it off the screen and out of the accessibility tree, the way it does on an unthemed one.

**Dark is a theme.** `dark` and `light` are partial themes handed to the provider:

```tsx
mount(document.body, <Theme value={dark}><App /></Theme>);
```

Either may nest inside the other, and each subtree resolves its own roles, because a provider's
subtree generates its own classes. Following the operating system is your call, in your own theme,
with the `_media_` directive the engine already has.

**A cell in the value is what a switch is.** `<Theme value={mode}>` with `mode` a cell holding
`light` or `dark` moves every class below it when the cell moves, on a page that is already up. A
plain object subscribes to nothing and costs nothing extra.

**No unnamed value.** A component of this package writes `$name`, never a colour, a size or a
duration. `node packages/testing/scripts/check-theme.ts` refuses one that does, over
[`packages/ui/src`](https://github.com/torrinworx/aweft/tree/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/src) and
[`recipes/`](/docs/recipes), and
[`packages/ui/tokens.txt`](https://github.com/torrinworx/aweft/blob/9a5bb24770dc7555257f8a307d79917efc58df70/packages/ui/tokens.txt)
is the committed list of every name there is. A value with no name yet gets one in the entry that
needs it:

```ts
Theme.define({ splash: { $splashHeight: '320px', minHeight: '$splashHeight' } });
```

**No segment that is an entry name.** The same check refuses a theme key whose segments after the
first name a top-level entry that lays an element out, because a class list is matched segment by
segment and that entry is compiled onto the same element: the square button was `button_icon` and
took the `icon` entry's `display: inline-block; width: 1em` along with it. The segment is `square`
now, and `size="icon"` is still what you write.

Point the check at your own source to adopt the same rule; nothing makes you. A path is resolved
against the repository root, so an absolute one is taken as it is, and a path with no `.ts` or
`.tsx` file under it fails instead of passing over nothing.

```
node packages/testing/scripts/check-theme.ts /home/me/app/src
```

**Overriding it.** `Theme.define` adds to the theme; it refuses one property of one entry given two
different values, so it is not how you replace what the default already says. A `<Theme value={...}>`
provider is, and it is also what scopes an override to part of a page. Entry names the default
theme does not use are yours to define outright.

**A dev-mode warning.** When a chain resolves both `color` and `background` from named values and
the pair is below 4.5:1, this package says so in the console, with the ratio, the target and the
role to use. Theme-derived pairs only, and the call is not in a release build.

**Two dev-mode throws, on the page itself** (design 266). The first `mount` or `hydrate` into a
browser page throws when `<html>` has no `lang`, and then when the document has no title, each with
its fix: `lang="en"` (or the page's language) on the root, and a `<title>` in the shell or a `Title`
on the page. The page is read once, in the same tick as the mount, so a shell that carries a
`<title>` is what a page should have for the first paint; a `Title` the page mounts is attached
before the check reads it. A light document, a server render and a document inside a frame are not
read. A release build has neither check.

What these and the contrast warning cannot see, a test can: `audit` and `walk` from
[`@aweftjs/testing/browser`](/docs/packages/testing)
run axe and a Tab walk over the page a test drives, and the build refuses the faults the source
settles ([`@aweftjs/build`](/docs/packages/build),
The access rules).

## Routing

```tsx
import { createRouter } from '@aweftjs/dom/router';
import { Stage, StageContext, mount } from '@aweftjs/ui';

const acts = {
	'': Home,
	'posts/:id': Post,
	docs: Docs,               // renders a StageContext of its own
	about: 'site/About',      // a module, loaded when the URL reaches it
	join: 'auth/SignIn',      // a module from a battery, on the URL you chose
	missing: NotFound,
};

const router = createRouter();
mount(document.body, (
	<StageContext
		router={router}
		sources={[app, authClient]}
		client={client}
		acts={acts}
		template={Layout}
		fallback="missing"
		refused="join"
	>
		<Nav /><Stage />
	</StageContext>
));
router.links(document.body);
```

`StageContext` holds the acts and, given a router, the URL; a nested stage under a routed act
needs none, because it follows its parent's. `Stage` renders whichever act is
current, inside the template. They are two components because one that did template selection, URL
matching, child coordination and the accessibility work at once would be unchangeable.

`stage.current` names the act the URL chose, from the moment it matches. When that act is refused,
the `refused` act's component is what shows under that name, so a test asking what a page is
showing reads the page rather than `current`.

**An act key** is a path with no leading slash. `''` is the index and matches `/` only. `:name`
takes one segment, and one trailing `*name` takes the rest. A trailing bare `*` takes no segment
and no parameter: it matches any path and parks everything from it as the tail for a stage inside
the act, which is not rebuilt when that tail moves. A key whose whole text is the path wins
outright, and otherwise a literal segment beats `:name`, `:name` beats `*name` and `*`, and the
longer pattern breaks a tie. There are no optional segments and no patterns.

**An act** is the component, or the name of a module. A component act may carry `entries()`, an
async function returning the parameter sets a static walk should render it at; nothing in this
package calls it.

**An act module** is an ordinary module: `deps`, `defaults`, and a factory answering
`{ component, title? }`. It is the only form of act that can declare what it needs.

**A module an act depends on is built once per page and reused on every later visit**, so its
factory must hand back live cells and handles rather than an awaited snapshot: the value it
returned on the first visit is the value the fifth visit reads.

```ts
// A getter over the handle, not `const document = await handle.ready`.
export default ({ client }) => {
	const handle = client.share('board');
	return { ready: handle.ready, get document() { return handle.document; }, stop: handle.stop };
};
```

```ts
// modules/notes/Page.tsx
export const deps = ['auth/Session', 'notes/Current'];
export const entries = async () => (await listNotes()).map((note) => ({ id: note.id }));

export default ({ imports }) => ({
	title: 'Notes',
	component: () => <Notes user={imports.Session.user} notes={imports.Current.document} />,
});
```

- **`component`** is what the stage renders, handed the same props a component act gets, `stage`
  included. An instance with no `component` function is a loud assert.
- **`title`** is what the live region announces when the act arrives, and nothing else. The
  browser tab still follows the `<Title>` the component writes.
- **`entries`** is an export beside `deps`, so a static walk reads it without running the factory:
  listing a site's URLs opens no connection and builds no page. A module that exports none answers
  `null`, which tells a walk it cannot say what its URLs are.

**`sources` and `client`.** `sources` is where named acts come from, in precedence order, as
`createLoader` takes them. The stage builds one loader over them for the whole routing tree, and a
stage inside an act inherits it and takes no `sources` of its own. `client` is the page's
connection, handed to every module as its `client` prop.

That is the page mirroring the server: the platform hands a factory `client` and nothing else, and
everything the application makes is a module that others name in `deps`. A shared document, a
session, a rules table, a gate: each is a module, built in dependency order, and none of them is
built in the boot file and threaded around by hand.

**When a named act is loaded and unloaded.** It is loaded, with its dependencies first, when the
stage decides it, and it goes through the same `suspend` everything slow goes through, with the
`LoaderContext`'s loading and failed components. It is unloaded once the next act is showing, so a
module both of them depend on is never torn down between them, and the instance's own `stop` runs
there. The modules it depended on stay loaded for the page; when the stage that built the loader is
removed, everything it loaded is unloaded in reverse load order.

**`refused`** is an act name, shown when loading a named act rejects with a refusal: an error a
factory threw that carries a `reason`. The refused act reads the error as its `refusal` prop, and
gets a `retry` beside it: call it and the act the URL chose is built again, in place, with the
address exactly where it was. `auth/SignIn` calls it after a successful `enter`, so a gated page
appears once the visitor signs in without anyone navigating. The URL never moves, so the visitor
keeps the address they asked for, and `refused` must name a key with no `:name` or `*name` segment,
since the refused act renders under the refusing URL's parameters. Anything else that goes wrong (a
name no source lists, a cycle, a factory that threw a bare `Error`) propagates as before.

**A static render has no `client`**, so the loader's props carry no `client` key at all and a
module that wants one decides what its absence means. What it must not do is wait for an answer
that will never come: `render` waits on every pending promise, so a factory awaiting one hangs the
render. `auth/Session` is anonymous at once instead, so a static render of a gated page is a gated
act refused and the sign-in act in the markup, while a module reading a document with no client
renders its waiting state.

**`{ load }` is gone.** A name is the lazy form. `about: { load: () => import('./about.tsx') }`
becomes `about: 'site/About'` in the acts map, a source that lists it, and a small module:

```ts
// modules/site/About.tsx
export default () => ({ title: 'About', component: About });
```

**The stage value** is `StageContext.read(context)`, or `StageContext.use(stage => ...)`, and every
act is handed it as its **`stage` prop**, so `const Post = (props) => <h1>{props.stage.params.get().id}</h1>`
needs no context at all. It holds `current`, `params` (the `:name` values), `query` (a cell: write
to it and the URL's query is updated with `replace`, so a filter leaves one history entry), `open`
and `close`. A prop named `stage` in an `open` does not reach the act; the stage does.

**Nesting.** An act that renders a `StageContext` of its own gets what the act above it did not
match: `/docs/install` reaches the [`docs`](https://github.com/torrinworx/aweft/tree/9a5bb24770dc7555257f8a307d79917efc58df70/docs) act, whose child stage sees `install`. A deep link that
arrives before the child has mounted waits for it, and is dropped on the next navigation. One child
per stage claims it; a second stage under one act is a content swapper, not a route.

**`open({ name, template, history, ...props })`** shows an act now whatever the URL says, wrapped in
the template you name for this open. Props do not accumulate: each `open` replaces the last one's.
`history: true` pushes a history entry **at the URL the page is already on**, so back dismisses it
and a copied link is the link to the page. It is held in memory against that entry, so a reload
lands on the page under it. A stage owns one entry at a time: a second `history: true` open while
one is showing replaces that entry rather than pushing a second, so one back closes whatever is
open and lands on the page.

**`fallback`** is the 404: an act name, matched last, rendered when nothing matched. **`initial`**
is what shows when no URL decides: no router, or a parent that took the whole path.

**On every act change, in a browser**, focus moves to the act's root element (given `tabindex="-1"`
if it cannot take focus), a visually hidden live region announces the act module's `title` or, with
none, the new head title, and the page goes
to the top, or to the URL's hash, unless the router has a position saved for this entry. The first
act is not a change: a page load should not steal focus. All three are no-ops with no `window`.

`context().stage` holds one entry per live `StageContext`: its declared `acts` with a `loader` flag
saying which were declared as module names, and each act's `entries`, its `prefix` (what its parent actually matched, `posts/3` and not
`posts/:id`) and its `parent`. That is what a static walk reads to know which URLs a site has. The
acts come in the order the `acts` object itself lists them, which puts a whole-number name such as
`404` first however it was written.

A page that mounts takes its entry out again when the stage unmounts, so a live page's registry is
what is on the page now. `render` is the exception: it holds the list for the length of the call,
because it takes the page down as soon as it has serialized it and a walk reads the list afterwards
(design 145). `@aweftjs/ssg` is what does the walking.

## Head tags

```tsx
const Layout = (props) => <div><Title>My site</Title>{props.children}</div>;

const Post = () => (
	<article>
		<Head>
			<Title>{post.title}</Title>
			<Meta name="description" content={post.summary} />
			<Link rel="canonical" href={`https://example.com/posts/${post.id}`} />
		</Head>
		…
	</article>
);
```

`Head`, `Title`, `Meta`, `Link`, `Script` and `Style` render nothing where they are written and put
a tag in the render's head list. `Head` opens a deeper scope, and within a group the deepest tag
wins, then the latest, so a page beats the layout it is inside without knowing the layout is there.

A group is the tag's own identity, or an explicit `key`: a `title` is one per page; a `meta` by its
`charset`, `http-equiv`, `name` or `property`; a `link` by `rel` and `href` together, with
`rel="canonical"` a singleton; a `script` by its `src` and `type`, or inline plus type; a `style` by
its `media`. A tag with none of those and no `key` is additive, so every one of them is emitted.
**Two inline scripts of one type are one group**: give each a `key` to keep both.

Every value takes a value or a cell, and a cell rewrites the tag in place.

Tags come out in one fixed order however they were written: charset, viewport, other meta, title,
links that preload or preconnect, styles, other links, then scripts.

```ts
const ui = context();
const body = await render(<App />, { context: ui });
const page = `<!doctype html><html><head>${ui.head.markup()}</head><body>${body}</body></html>`;
```

`head.markup()` stamps each tag with `data-aweft-head`, its group. `mount` writes the list into
`document.head` as one run **at the front of it**, because `document.title` is the first title
element there is and one appended after a page shell's would do nothing. A `<meta charset>` the
shell wrote first stays first, because a charset read late is not read at all. `hydrate` adopts a
stamped tag whose group matches, updating it in place rather than removing and re-adding it.

Two renders in one page neither adopt nor remove each other's tags, and each writes its own title
into the head. `document.title` is whichever of them is nearest the front, so the one mounted last
takes the tab. **Use one render per page** and mount the whole page into it, which is what the
default shared render already does.

A static render holds its list, because `render` takes the page down as soon as it has serialized
it and `markup()` is read afterwards. Use one `context()` per page.

## What it never decides

Storage, transport, and anything server-side. Data fetching: `suspend` takes a promise and does
not make one. Upload transport. Auth. Where analytics go: `InputContext` fires and the application
listens. What your application looks like: a default theme ships so a bare `theme="button"` renders
as something readable, and every value in it is yours to replace. What your own components name
their own values, and whether you run the theme check. Which acts a site has, what a template looks
like, and whether links outside the routed root are intercepted. Writing pages to disk, which is
`ssg`. Your faces, your sizes and your measure, and what a text modifier renders. Whether text is
editable: `Typography` never becomes an input.

## Boundaries

Tier 7, client plane. It imports `@aweftjs/core` and `@aweftjs/dom` and nothing else, and nothing
in the stack imports it.

Nothing at module scope here can be seen through from one page to another. The theme definitions
are the one store, and they are data written once at import. The rest is caches keyed on an object
the caller already holds: the tag identity behind each `<mark.name>`, what a theme says as a
string, and the merge of a render's own theme with a provider's. A page's own state, its class
cache and stylesheet, its id counter and its popup sink, is on the object each render makes.
The render every default `mount` into a page shares is kept on that page's document, not here.

## Known limits

**A `Modal` in server markup does not hydrate.** `Modal` builds its `<dialog>` and hands that
element to the control that opens it, so a static render writes the `open` attribute into the
markup, the client has not written it when the pairing walk reaches the element, and `dom`
reports the mismatch; and because a hydration keeps the server's element and drops the client's,
the control would be left driving a node nobody can see. You meet it only when a page rendered
on a server names `Modal` as a stage template, since a modal is otherwise opened by an
interaction and is not in a page's markup at all. `Tooltip` and `Popup` avoid this by reading
their element back out of the mount that put it in the document (design 153), which `Modal`
cannot do until `dom` says which node a mount put in the document and lets a component write an
attribute a hydration reconciles rather than compares.

**A token is not a string.** `label.length`, `'' + label` and `String(label)` on a token answer a
function's, because the string only exists in a render. `textOf(context, label)` is the string,
and every place inside this package that reads a label uses it. A token in a prop that is not
text is written as its string all the same: in `class` beside a `theme` it is lost to the theme's
class, and in a handler it is a string where a function was wanted.

**Where a token's cells are not followed.** A head tag resolves a token once, when it declares
its tag, so a `<Title>` over a plural whose count moves keeps its first string; and a
`Typography` whose `label` is a cell holding a token follows the cell, not a cell inside the
token's values. A token child, a token in a prop, and a token `label` written directly all follow
their cells.

**The parse cache grows with distinct sources.** A message parses once per source string and the
parts are kept for the life of the process, so a token whose source is built at run time
(``text(`${word} items`)``) parses and keeps one entry per distinct string. Write the message
once and put the word in a hole.

**A `Button` whose `label` cell starts empty throws in development.** The name check reads the
button as it first mounts, and a cell that fills in later is a nameless button at that moment.
Give the button a static `aria-label` for the empty state, which a screen reader wants in any case.

## The design notes

A `design NNN` above is the note of that number in
[`docs/design/`](https://github.com/torrinworx/aweft/tree/9a5bb24770dc7555257f8a307d79917efc58df70/docs/design), which says what was
decided, why, what it costs, and what would reverse it.

## API

Every export of `@aweftjs/ui`, its signature as the compiler resolves it, and its block comment.

### `@aweftjs/ui`

#### `Act`

```ts
type Act = ActComponent | string;
```

What an act key maps to: the component itself, or the name of a module that makes one.

#### `ActComponent`

```ts
type ActComponent = Component<Record<string, unknown>> & { entries?: ActEntries; };
```

An act that is the component itself, with the static walk's parameter source on it.

#### `ActEntries`

```ts
type ActEntries = () => Promise<readonly Readonly<Record<string, string>>[] | null>;
```

The parameter sets an act should be rendered at.

Declared on an act, or exported by an act module beside its `deps`, and read by a static walk.
Nothing in this package calls it. `null` means the act cannot say what its URLs are, which is
what an act module that exports no `entries` answers (design 242).

#### `ActInstance`

```ts
interface ActInstance { readonly component: Component<Record<string, unknown>>; readonly title?: unknown; }
```

What an act module's factory answers (design 242).

#### `Alert`

```ts
Alert: (props: AlertProps) => unknown
```

A message about the page.

**Params**

- `props`: `title`, `icon`, `type`, `element`, and anything else, which goes to the element
- `children`: the body

**Returns** a `<div>` on the `alert` entry, `role="alert"` when `type` is `danger` and `role="status"` otherwise, so a message that matters interrupts a screen reader and a message that does not waits its turn. `success` is one of the latter: a thing that went right waits (design 216). The title is on `alert_title`, the body on `alert_body`, and an icon on `alert_symbol` in a first column the box grows only when it was given one. The icon is yours. This package ships no drawings (design 144), so an `Icon` by name needs an `Icons` provider above it, and there is no default icon here.

**Example**

```ts
<Alert title="Saved" icon={<Icon name="check" />}>Everything went through.</Alert>
<Alert type="danger" title="Nothing was saved">The server refused the write.</Alert>
<Alert type="success" title="Saved">Your changes are live.</Alert>
```

#### `AlertProps`

```ts
interface AlertProps { readonly title?: unknown; readonly icon?: unknown; readonly type?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Alert` takes. Everything not named here goes to the element.

#### `Avatar`

```ts
Avatar: (props: AvatarProps) => unknown
```

A picture of a person.

**Params**

- `props`: `src`, `alt`, `fallback`, `size`, `round`, and anything else, which goes to the outer `<span>`

**Returns** a `<span>` on the `avatar` entry holding an `<img>` on `avatar_image` and a `<span>` on `avatar_fallback`. The fallback shows until the image fires `load` and shows again if it fires `error`; with no `src` there is no image at all and the fallback is what renders. Whichever of the two is not showing carries `hidden`, so it is out of the accessibility tree as well as off the screen. `size` is `sm` (`$controlSm`), nothing (`$control`), `lg` (`$controlLg`), or any CSS length, which is written as the element's width and height (design 215). The fallback's letters are a fraction of the box, so they follow it at every size. A `src` that is a cell builds the `<img>` from the first paint, whatever the cell holds, and the image's `src` follows it: a cell that has not resolved yet is not the same as no picture. A plain `src` of nothing builds no `<img>` at all.

**Example**

```ts
<Avatar src={person.photo} alt={person.name} fallback="TL" />
<Avatar fallback="AB" size="sm" round={false} />
<Avatar src={photo} fallback="AB" size="120px" />
```

#### `AvatarProps`

```ts
interface AvatarProps { readonly src?: unknown; readonly alt?: unknown; readonly fallback?: unknown; readonly size?: unknown; readonly round?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Avatar` takes. Everything not named here goes to the outer `<span>`.

#### `Badge`

```ts
Badge: (props: BadgeProps) => unknown
```

A short label on a fill.

**Params**

- `props`: `label`, `type`, `size`, `icon`, `element`, and anything else, which goes to the element

**Returns** a `<span>` on the `badge` entry: the accent fill at `$textXs`, or the `quiet`, `danger`, `success` and `outline` variants of it. Its size axis is padding and text rather than a control height, because a badge is not a control and a row of 36px blocks is not what a caller asked for. It is never interactive. A badge somebody presses is a `Button` with `size="sm"`.

**Example**

```ts
<Badge label="New" />
<Badge label="3 failed" type="danger" size="sm" />
```

#### `BadgeProps`

```ts
interface BadgeProps { readonly label?: unknown; readonly type?: unknown; readonly size?: unknown; readonly icon?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Badge` takes. Everything not named here goes to the element.

#### `Breadcrumb`

```ts
Breadcrumb: (props: BreadcrumbProps) => Mounter
```

Where a page sits, as a trail of links.

**Params**

- `props`: `items`, `label`, `element`, and anything else, which goes to the `<nav>`

**Returns** a `<nav aria-label>` holding an `<ol>`. Every item but the last is an `<a href>`; the last is a `<span aria-current="page">`, because you do not link to the page you are on. A `<span aria-hidden="true">` sits between them, drawn by the theme as a chevron, so no icon pack is needed to render one. An item before the last with no `href` renders as a span too: it reads as a level with nowhere to go rather than as a link that does nothing.

**Example**

```ts
<Breadcrumb items={[{ label: 'Home', href: '/' }, { label: 'Files', href: '/files' },
  { label: 'shot.png' }]} />
```

#### `BreadcrumbItem`

```ts
interface BreadcrumbItem { readonly label?: unknown; readonly href?: unknown; }
```

One level of the trail.

#### `BreadcrumbProps`

```ts
interface BreadcrumbProps { readonly items?: unknown; readonly label?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Breadcrumb` takes. Everything not named here goes to the `<nav>`.

#### `Button`

```ts
Button: (props: ButtonProps, _cleanup: Cleanup, mounted: Mounted) => Mounter
```

A button, or a link drawn as one.

**Params**

- `props`: `label`, `type`, `size`, `icon`, `iconPosition`, `disabled`, `loading`, `round`, `inline`, `href`, `hrefNewTab`, `onClick`, `track`, `element`, and anything else, which goes to the element

**Returns** a `<button type="button">`, or an `<a>` when `href` is given. With `href` and `hrefNewTab` left alone it also carries `target="_blank"` and `rel="noopener noreferrer"`, because a new tab that can reach back at the page it came from is a hole nobody meant to open. While it is loading it is disabled and shows the `LoaderContext` loader in place of its icon, so a form that would submit twice on a double click cannot. Fires the `InputContext` `click` event with `{ component: 'Button', label, href }`, unless `track` is false.

**Example**

```ts
<Button label="Save" onClick={() => save(form)} />
<Button label="Docs" href="https://example.com/docs" type="quiet" />
```

#### `ButtonProps`

```ts
interface ButtonProps { readonly label?: unknown; readonly type?: unknown; readonly size?: unknown; readonly icon?: unknown; readonly iconPosition?: string; readonly disabled?: unknown; readonly loading?: unknown; readonly round?: unknown; readonly inline?: unknown; readonly href?: unknown; readonly hrefNewTab?: unknown; readonly onClick?: (event: unknown) => unknown; readonly track?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Button` takes. Everything not named here goes to the element.

#### `Card`

```ts
Card: (props: CardProps) => unknown
```

A raised block with a heading, a body and a foot.

**Params**

- `props`: `title`, `description`, `foot`, `type`, `tight`, `element`, and anything else, which goes to the element
- `children`: the body

**Returns** a `<div>` on the `card` entry: the surface fill, a border, the larger radius and `$space4` of padding, which `tight` takes away. Given a `title`, a `description` or a `foot` it also takes the `stack` segment and builds the parts: the title on `card_title`, the description on `card_description` in `$mutedForeground`, the children in a `card_body`, and the foot as a row, with `$space4` between them. Each part renders only where it was given something. Given none of the three it is the bare block and the children are its own children (design 211).

**Example**

```ts
<Card title="Today" description="What is due" foot={<Button label="Add" />}>
  <p theme="text">Nothing yet.</p>
</Card>

<Card><h2 theme={['text', 'lg']}>Today</h2><p theme="text">Nothing yet.</p></Card>
```

#### `CardProps`

```ts
interface CardProps { readonly title?: unknown; readonly description?: unknown; readonly foot?: unknown; readonly type?: unknown; readonly tight?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Card` takes. Everything not named here goes to the element.

#### `Catalog`

```ts
type Catalog = Readonly<Record<string, string>>;
```

A language's translations: the key a text token looks up, to the message to show for it.

#### `Category`

```ts
interface Category { readonly items: unknown[]; readonly props: Record<string, unknown>; }
```

One slot's children, with every mark of that name merged into `props`.

#### `Checkbox`

```ts
Checkbox: (props: CheckboxProps) => Mounter
```

A tick box.

**Params**

- `props`: `value`, `label`, `description`, `error`, `invert`, `indeterminate`, `disabled`, `type`, `size`, `onChange`, `element`, and anything else, which goes to the `<input>`

**Returns** an `<input type="checkbox">`, inside a `<div>` with its label when it was given one. Space toggles it, because it is the platform's own box. `invert` flips what ticked means: the box is ticked while the cell is false. What the cell holds is unaffected, so a caller reading it reads the same thing either way. `indeterminate` is a property rather than an attribute in the platform, so it is not in the markup a static render writes and it is set again on the first mount, which is where it can be set at all.

**Example**

```ts
<Checkbox label="Remember me" value={remember} />
```

#### `CheckboxProps`

```ts
interface CheckboxProps { readonly value?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly invert?: unknown; readonly indeterminate?: unknown; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly onChange?: (next: boolean, event: unknown) => void; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Checkbox` takes. Everything not named here goes to the element.

#### `ColorPicker`

```ts
ColorPicker: (props: ColorPickerProps, cleanup: (...fns: (() => void)[]) => void) => Mounter
```

A colour, picked on a square and two sliders.

**Params**

- `props`: `value`, `hasAlpha`, `disabled`, `type`, `element`, and anything else, which goes to the wrapper

**Returns** a `<div>` holding a swatch, a saturation and brightness square with a thumb in it, and one or two `Slider`s: hue, and opacity when `hasAlpha` is not false. Each slider is labelled, so each is found by name and announces its number; the swatch is `aria-hidden`, because it says what the rest already say. The square's thumb is `role="slider"`, focusable, and carries both axes: `aria-valuenow` is the saturation and `aria-valuetext` reads "saturation 40%, brightness 80%". There is no two-axis role in ARIA and one control that says both beats two a person has to switch between (design 222). Left and right move saturation, up and down move brightness, Home and End take saturation to its ends, and Shift makes any of them coarse. A press anywhere in the square moves the thumb there. The cell holds CSS colour text, anything `readColour` reads, and is written back as `rgb()` or `rgba()`. A drag, a key or a slider writes it, and nothing else does: mounting this on a colour leaves that colour alone. Writing the cell from outside moves the thumb and the sliders. With `hasAlpha` false there is no opacity slider, and a write keeps the alpha the cell already had rather than making the colour opaque.

**Throws** an assert, loud in development and stripped in a release build, for text that is not a colour this package can read, naming the text.

**Example**

```ts
<ColorPicker value={picked} hasAlpha={false} />
```

#### `ColorPickerProps`

```ts
interface ColorPickerProps { readonly value?: unknown; readonly hasAlpha?: unknown; readonly disabled?: unknown; readonly type?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `ColorPicker` takes. Everything not named here goes to the wrapper.

#### `Component`

```ts
type Component<P = Record<string, unknown>> = (props: P & { children?: unknown[]; each?: unknown; }, cleanup: Cleanup, mounted: Mounted, pending: Pending) => unknown;
```

A component: called once with its props, and what it returns is mounted.

#### `Context`

```ts
interface Context<T> { (props: ProviderProps): unknown; readonly def: T; read(context: unknown): T; node(context: unknown): ContextNode<T> | null; use<P extends Record<string, unknown>>(build: (value: T) => Component<P>): Component<P>; }
```

A context: the provider component, and the four ways to reach what it holds.

#### `ContextNode`

```ts
interface ContextNode<T> { readonly id: string; readonly parent: ContextNode<T> | null; readonly children: MutableArray<ContextNode<T>>; value(): T; }
```

One live provider.

#### `ContextOptions`

```ts
interface ContextOptions { readonly locale?: string | undefined; readonly catalog?: Catalog | undefined; }
```

What a page may say about the language a render shows. Both take `undefined` as well as
nothing, so an entry writes `context({ locale, catalog })` whether or not this page has a
catalog.

#### `Countries`

```ts
Countries: Context<CountryData | null>
```

The codes, subdivisions and aliases the country fields below read.

Nothing is here by default, because this package ships no country data (design 251). Fill it from
`@aweftjs/ui/countries`, which reads the optional `country-region-data` peer, or hand over a
`CountryData` of your own: five codes and their regions is a whole value.

**Example**

```ts
const data = await countryData();
<Countries value={data}><Country value={country} /></Countries>
```

#### `Country`

```ts
Country: (props: CountryProps, _cleanup: (...fns: (() => void)[]) => void, mounted: (...fns: (() => void)[]) => void) => Mounter
```

The country field: a button that opens a searchable list of every country the provider has.

**Params**

- `props`: `value`, `locale`, `priority`, `flags`, `suggest`, and everything `Chooser` takes: `label`, `description`, `error`, `placeholder`, `title`, `search`, `none`, `open`, `disabled`, `type`, `size`, `name`, `autocomplete`, `onChange`, `element`, `theme`

**Returns** the chooser, over one row per code the provider holds. The cell holds the two-letter code, and a form posts that. The rows are ordered: the suggested country first, then `priority` in the order it was given, then everything else by its name in the page's own language. Nothing is chosen to begin with, and the suggestion never becomes the value: a person from one country filling a form for another is ordinary, and a guess that fills the field is a mistake nobody notices. A search matches the name, the code and this package's aliases, without accents: `uk` finds the United Kingdom, `cote` finds Côte d'Ivoire.

**Throws** the assert for a page with no `Countries` provider above it.

**Example**

```ts
<Countries value={data}>
  <Country label="Country" value={country} name="country" autocomplete="country" />
</Countries>
```

#### `CountryData`

```ts
interface CountryData { readonly codes: readonly string[]; regions(code: string): readonly Subdivision[]; readonly aliases?: Readonly<Record<string, readonly string[]>>; }
```

What `Countries` holds: the codes a form may offer, their subdivisions, and the aliases.

#### `CountryProps`

```ts
interface CountryProps { readonly value?: unknown; readonly locale?: unknown; readonly priority?: readonly string[]; readonly flags?: unknown; readonly suggest?: unknown; readonly [prop: string]: unknown; }
```

What `Country` takes. Everything not named here goes to `Chooser` and on to the button.

#### `Default`

```ts
Default: (props: { children?: unknown[] | undefined; }) => unknown
```

The template that adds nothing: the act, and no element around it.

This is what a stage uses when the page named no `template`, and it is exported so a page that
names one for some acts can name this one for the rest rather than leaving a gap.

**Params**

- `props`: `children`, the act

**Returns** the children, unchanged.

**Example**

```ts
stage.open({ name: 'preview', template: Default });
```

#### `Definitions`

```ts
type Definitions = Readonly<Record<string, Entry>>;
```

A whole theme: entries by `_`-joined selector path.

#### `Detached`

```ts
Detached: (props: { enabled?: unknown; locations?: readonly Placement[] | undefined; onResize?: ((rect: Rect) => void) | undefined; style?: Record<string, unknown> | undefined; children?: unknown[] | undefined; }, cleanup: (...fns: (() => void)[]) => void) => unknown
```

A popup placed next to its own children.

**Params**

- `enabled`: the open state, a cell. Setting it false closes the popup, and so does scrolling
- `locations`: the placements to consider, in order of preference. Omitted, the eight corner modes
- `onResize`: called when the anchor changes size and the popup is re-placed
- `style`: merged onto the popup's box
- `children`: the anchor, with `<mark.popup>` for what floats

**Returns** the anchor where it was written, and the popup at the sink. The anchor is an ordinary mount, so a page taken over from a server adopts the nodes the server sent (design 153).

**Throws** the assert `categories` makes for a slot it does not know.

**Example**

```ts
<Detached enabled={open}>
  <button onClick={() => open.set(!open.get())}>menu</button>
  <mark.popup><Card>what floats</Card></mark.popup>
</Detached>
```

#### `DropDown`

```ts
DropDown: (props: DropDownProps) => Mounter
```

A button and the block it shows.

**Params**

- `props`: `open`, `label`, `icon`, `iconOpen`, `iconClose`, `arrow`, `name`, `type`, `disabled`, `element`, and anything else, which goes to the `<details>`
- `children`: what shows while it is open, in the page's flow

**Returns** a `<details>` with a `<summary>` wearing the `button` theme. Space and Enter toggle it, a screen reader reads it as a button and says whether it is expanded, and none of that is written here. The `open` cell goes both ways: writing it opens and closes the element, and a person opening it writes the cell. `name` puts it in a group: the platform keeps one `<details>` of a name open and closes the rest, and the one that closes fires its own `toggle`, so every cell in the group follows. A stack of disclosures that keeps one open is a run of these sharing a name, and there is no component for it (design 212). A floating menu is not this: that is `Detached` with a `Button` anchor (design 136).

**Throws** the assert `elementFor` makes for an `element` that is not a `<details>`.

**Example**

```ts
<DropDown label="Filters" open={shown}><Filters /></DropDown>
```

#### `DropDownProps`

```ts
interface DropDownProps { readonly open?: unknown; readonly label?: unknown; readonly icon?: unknown; readonly iconOpen?: unknown; readonly iconClose?: unknown; readonly arrow?: unknown; readonly name?: unknown; readonly type?: unknown; readonly disabled?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `DropDown` takes. Everything not named here goes to the `<details>`.

#### `Empty`

```ts
Empty: (props: EmptyProps) => unknown
```

Nothing here yet, and what to do about it.

**Params**

- `props`: `icon`, `title`, `description`, `element`, and anything else, which goes to the element
- `children`: the action row, usually one or two `Button`s

**Returns** a `<div>` on the `empty` entry: a centred column with `$space6` of padding, holding `empty_symbol`, `empty_title`, `empty_description` and `empty_actions`. Each part renders only where it was given something.

**Example**

```ts
<Empty icon={<Icon name="inbox" />} title="No messages" description="They will show up here.">
  <Button label="Refresh" onClick={reload} />
</Empty>
```

#### `EmptyProps`

```ts
interface EmptyProps { readonly icon?: unknown; readonly title?: unknown; readonly description?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Empty` takes. Everything not named here goes to the element.

#### `Entry`

```ts
type Entry = Readonly<Record<string, unknown>>;
```

One theme entry: CSS declarations, `$var` definitions, `extends`, and `_directive_` blocks.

#### `FileDrop`

```ts
FileDrop: FileDropComponent
```

A place to drop files.

**Params**

- `props`: `files`, `extensions`, `multiple`, `limit`, `clickable`, `disabled`, `onDrop`, `ready`, `type`, `element`, and anything else, which goes to the zone
- `children`: given, they replace the prompt line and the listing this draws for itself

**Returns** a `<div>` holding a `<label>`, a visually hidden `<input type="file">` and either the default chrome or your children. A drop, a click and the keyboard all reach the same input. The zone listens for `dragenter`, `dragleave` and `drop` (reading `dataTransfer.files`), and the input for `change` (reading `files`). An entry is `{ name, file, status, error, reason }`. A file the zone accepted starts as `ready` and one it refused as `error`, with `error` the sentence a person reads and `reason` the code a page branches on: `type` for a file the `extensions` do not cover, `size` for one over `limit`, and `count` for a second file while `multiple` is false (design 214). A refused file stays in the list. Move `status` to `loading` while you upload by writing the entry back into the list (`files[0] = { ...files[0], status: 'loading' }`), which is the edit the list can hear. `limit` is bytes, and every sentence naming it writes it in KB, MB or GB, so a `limit` of 4_000_000 reads as 3.8 MB. The prompt names the accepted types the way a person says them, so `image/png` reads as `png` and `image/*` reads as `image`. `ready` is written null while any entry is loading, and otherwise the file, or the array of files when `multiple` is true, counting every entry that is not in error. `FileDrop.Button` is a `Button` that opens a file dialog. Inside a `FileDrop` it opens that zone's input and takes no checking props of its own; outside one it is the picker with no chrome, taking `files`, `extensions`, `multiple`, `limit`, `onDrop` and `ready` itself. There is no upload here: the entry carries the platform `File` and the rest is yours.

**Throws** the assert `elementFor` makes for an `element` that is not a `<div>`, the assert `files` makes for a value that is not a cell, and the assert a `FileDrop.Button` inside a zone makes when it was given a checking prop the zone already owns. All three are loud in development and stripped in a release build.

**Example**

```ts
<FileDrop files={picked} extensions={['image/png', 'image/jpeg']} limit={4_000_000} />
<FileDrop.Button label="Change photo" extensions={['image/*']} multiple={false} ready={photo} />
```

#### `FileDropButtonProps`

```ts
interface FileDropButtonProps extends ButtonProps { readonly files?: unknown; readonly extensions?: unknown; readonly multiple?: unknown; readonly limit?: unknown; readonly onDrop?: (files: unknown[]) => void; readonly ready?: unknown; }
```

What `FileDrop.Button` takes: the `Button` props, and the checks when it stands on its own.

#### `FileDropComponent`

```ts
interface FileDropComponent { (props: FileDropProps): Mounter; Button(props: FileDropButtonProps): Mounter; }
```

`FileDrop`, with the button that opens a file dialog.

#### `FileDropEntry`

```ts
interface FileDropEntry { readonly name: string; readonly file: unknown; readonly status: 'ready' | 'loading' | 'error'; readonly error?: string; readonly reason?: 'type' | 'size' | 'count'; }
```

One file the zone was given, as the application reads it.

#### `FileDropProps`

```ts
interface FileDropProps { readonly files?: unknown; readonly extensions?: unknown; readonly multiple?: unknown; readonly limit?: unknown; readonly clickable?: unknown; readonly disabled?: unknown; readonly onDrop?: (files: unknown[]) => void; readonly ready?: unknown; readonly type?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `FileDrop` takes. Everything not named here goes to the zone.

#### `Head`

```ts
Head: (props: { children?: unknown[] | undefined; }) => unknown
```

A deeper override scope for the head.

Everything inside is one level deeper than everything outside, and within a group the deepest
tag wins. A layout writes its defaults outside a `Head` and a page overrides them from inside
one; two pages that both override are decided by which mounted last.

**Params**

- `children`: the subtree whose head tags are one level deeper

**Example**

```ts
<Head><Title>{post.title}</Title></Head>
```

#### `HeadKind`

```ts
type HeadKind = 'title' | 'meta' | 'link' | 'script' | 'style';
```

The elements a head component makes.

#### `HeadList`

```ts
interface HeadList extends Registry<HeadTag> { markup(): string; title(): string | null; }
```

The render's head tags: a registry, plus the two questions something else asks of it.

#### `HeadTag`

```ts
interface HeadTag { readonly kind: HeadKind; readonly group: string | null; readonly depth: number; readonly attrs: Readonly<Record<string, unknown>>; readonly text?: unknown; }
```

One tag, as a head component declared it.

#### `Hydrated`

```ts
type Hydrated = Remove & { readonly ready: Promise<void>; }; (from @aweftjs/dom)
```

What `hydrate` answers: the remove function, and when the pairing walk finished.

#### `Icon`

```ts
Icon: (props: IconProps, cleanup: (...fns: (() => void)[]) => void, _mounted: unknown, pending: (promise: Promise<unknown>) => void) => Mounter
```

One icon, drawn from icon data.

**Params**

- `props`: `name`, either icon data or a name to look up through `Icons`; `size`, a CSS length; `label`, its name for a screen reader; `rot`, degrees; and anything else, which goes to the `<svg>`

**Returns** one `<svg>`, filled with `currentColor` and sized in `em` so it matches the text beside it. With a `label` it carries `role="img"` and that label; without one it is `aria-hidden` and unfocusable, because an icon beside the word it means is otherwise read out twice. The element lasts as long as the component. A `name` that is a cell swaps the drawing inside it and never the element itself.

**Throws** an assert, loud in development and stripped in a release build, for a name no source in the stack answers. A resolver that answers a promise says so late, so that assert is thrown where the host reports it rather than into the promise nobody holds; a resolver that fails reports the same way, naming the reason, and the element stays empty.

**Example**

```ts
<Icon name="check" label="done" />
<Icon name="chevron-down" rot={90} />
```

#### `IconAlias`

```ts
interface IconAlias { readonly parent: string; readonly rotate?: number; readonly hFlip?: boolean; readonly vFlip?: boolean; }
```

One name pointing at another, with its own turns and flips on top.

#### `IconData`

```ts
interface IconData { readonly body: string; readonly width?: number; readonly height?: number; readonly left?: number; readonly top?: number; readonly rotate?: number; readonly hFlip?: boolean; readonly vFlip?: boolean; }
```

One icon, in the shape the icon sets publish.

#### `IconPack`

```ts
interface IconPack { readonly prefix?: string; readonly icons: Readonly<Record<string, IconData>>; readonly aliases?: Readonly<Record<string, IconAlias>>; readonly width?: number; readonly height?: number; }
```

A set of icons under one prefix.

#### `IconProps`

```ts
interface IconProps { readonly name?: unknown; readonly size?: unknown; readonly label?: unknown; readonly rot?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Icon` takes. Everything not named here goes to the `<svg>`.

#### `IconResolver`

```ts
type IconResolver = (name: string) => IconData | Promise<IconData | null> | null;
```

A function that finds an icon, or answers null so the next source in the stack is asked.

#### `IconSource`

```ts
type IconSource = IconPack | IconResolver;
```

What `Icons` holds: packs and resolvers, newest first.

#### `Icons`

```ts
Icons: Context<IconSource[]>
```

The icon packs and resolvers everything below can look a name up in.

The stack starts empty: this package ships no drawings (design 144). A provider stacks what it
names in front of what it inherited, so the nearest one wins. A pack is
`{ prefix, icons, aliases?, width?, height? }`, where the root size covers every icon in it
that declares none; a resolver is `(name) => data | Promise<data> | null`, and answering null
passes the question on to the next source.

**Example**

```ts
<Icons value={myPack}><App /></Icons>
<Icons value={(name) => fetch(`/icons/${name}.json`).then((r) => r.json())}><App /></Icons>
```

#### `Ids`

```ts
interface Ids { next(prefix?: string): string; }
```

The id source behind an `aria-labelledby` and its friends.

#### `Input`

```ts
interface Input { readonly meta?: Readonly<Record<string, unknown>>; readonly on?: (event: Record<string, unknown>) => void; readonly [handler: string]: unknown; }
```

What an application puts on the context.

#### `InputContext`

```ts
InputContext: Context<Input> & { fire: (context: unknown, type: string, payload?: Record<string, unknown>) => void; }
```

The input handlers and the metadata for everything below.

`meta` merges one level deeper than the rest, so a page adds a tag without replacing the tags
above it.

**Example**

```ts
<InputContext value={{ meta: { page: 'home' }, on: (e) => analytics.send(e) }}>{app}</InputContext>
```

#### `Link`

```ts
Link: (props: TagProps) => unknown
```

One `<link>`.

**Params**

- `key`: a group of your own

**Returns** nothing where it is written. Its group is `rel` and `href` together, except `rel="canonical"`, which is one per page whatever it points at. A `<link>` with no `rel` is additive.

**Example**

```ts
<Link rel="canonical" href={`https://example.com${path}`} />
```

#### `Loader`

```ts
type Loader<P> = (props: P, cleanup: (...fns: (() => void)[]) => void) => unknown;
```

What a loader is handed, and what it hands back.

#### `LoaderContext`

```ts
LoaderContext: Context<Loaders>
```

The loading and failure components for everything below.

Fields are inherited one at a time, so a provider that names only `failed` keeps whatever
`loading` was already in effect.

**Example**

```ts
<LoaderContext value={{ loading: Spinner, failed: ErrorPanel }}>{app}</LoaderContext>
```

#### `Loaders`

```ts
interface Loaders { readonly loading?: Component | null; readonly failed?: Component<{ error?: unknown; }> | null; }
```

The loading and failure components an application sets once, for everything below.

#### `LoadingDots`

```ts
LoadingDots: (props: LoadingDotsProps) => unknown
```

Three pulsing dots.

**Params**

- `props`: `type`, `size`, `label`, and anything else, which goes to the `<span>`

**Returns** a `<span>` holding three more. With a `label` it is a polite live region carrying that text; without one it is `aria-hidden`.

**Example**

```ts
<LoaderContext value={{ loading: LoadingDots }}><App /></LoaderContext>
```

#### `LoadingDotsProps`

```ts
interface LoadingDotsProps { readonly type?: unknown; readonly size?: unknown; readonly label?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `LoadingDots` takes. Everything not named here goes to the element.

#### `Mark`

```ts
interface Mark { (name: string, props?: Record<string, unknown> | null, ...children: unknown[]): Marked; readonly then: MarkMaker; readonly else: MarkMaker; readonly case: MarkMaker; readonly default: MarkMaker; readonly popup: MarkMaker; readonly anchor: MarkMaker; readonly tabs: MarkMaker; readonly panels: MarkMaker; readonly [slot: string]: MarkMaker; }
```

What `mark` is: callable by name, and a tag under any slot name.

The eight slots this package's own components read are declared one by one rather than left to
the index signature, because `noUncheckedIndexedAccess` widens every read of an index signature
with `undefined`, and a JSX tag whose type may be undefined is not a tag the compiler will call.
A slot name outside that list works at run time and is written `mark('name', props, ...children)`
in a TypeScript file.

#### `MarkMaker`

```ts
type MarkMaker = ((props?: Record<string, unknown>, ...children: unknown[]) => unknown) & { readonly [MAKER]: string; };
```

What `mark.name` is: a tag `ui`'s `h` turns into a mark rather than an element.

#### `Markdown`

```ts
Markdown: (props: MarkdownProps) => Mounter
```

A markdown string as themed blocks.

**Params**

- `props`: `source`, `modifiers`, `code`, `element`, `theme`, and anything else, which goes to the element

**Returns** a `<div>` on the `markdown` entry holding one element per block. A heading is a `Typography` `h1` to `h6` on `markdown_heading` with an `id` in GitHub's scheme; a paragraph is `p1` on `markdown_paragraph`; a fenced block is a `<pre>` on `markdown_code` with the language on `data-language`, holding what `code` answers; a list is a `<ul>` or `<ol>` on `markdown_list` of `<li>` on `markdown_item`, a task item with a `Checkbox` that follows the source and writes it back when the source is a writable cell; a table is a `<table>` on `markdown_tabular` in the `table_scroll` box with the `table_*` parts; a blockquote is on `markdown_quote`; a rule is an `<hr>` on `markdown_rule`. Inline, a code span is `<code>` on `markdown_inline`, bold `<strong>` on `markdown_bold`, italic `<em>` on `markdown_italic`, a link `<a>` on `markdown_link` with the `href` as written, unless its scheme is not `http`, `https`, `mailto` or `tel`, in which case the link is text. Each is a modifier in the render's `TextModifiers` shape, listed after `modifiers`, so an application's own patterns run inside markdown. A nested list, an image, a footnote, an HTML tag, an autolink and an escape are text, as written.

**Throws** the assert for a `source` that is neither a string, a number nor a cell holding one.

**Example**

```ts
<Markdown source={readme} />
<Markdown source={note} modifiers={[{ check: /@\w+/g, return: (who) => <Mention name={who} /> }]} />
<Markdown source={doc} code={(text, language) => <Highlighted text={text} language={language} />} />
```

#### `MarkdownProps`

```ts
interface MarkdownProps { readonly source?: unknown; readonly modifiers?: unknown; readonly code?: (text: string, language: string | null) => unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Markdown` takes. Everything not named here goes to the element.

#### `Marked`

```ts
interface Marked { readonly [MARKED]: string; readonly name: string; readonly props: Record<string, unknown> & { readonly children: unknown[]; }; }
```

One slot's contents, as the parent component reads it.

#### `Menu`

```ts
Menu: (props: MenuProps, cleanup: (...fns: (() => void)[]) => void, mounted: (...fns: (() => void)[]) => void) => Mounter
```

A button and the actions it opens.

**Params**

- `props`: `items`, `open`, `label`, `icon`, `type`, `size`, `disabled`, `locations`, `element`, and anything else, which goes to the anchor button
- `children`: the anchor's contents, beside or instead of `label`

**Returns** a `Button` where it was written, and a `<div role="menu">` of `<div role="menuitem">` rows in a popup placed under it, flipping above when there is no room below. An item is `{ label, icon?, type?, disabled?, onSelect }`, and `type: 'danger'` draws the row in the danger colour. A group is `{ heading, items }` and draws a small heading over its own rows. The keys are design 223's, the same map a `Select` uses: the arrows move and wrap, Home and End go to the ends, typing moves by what a row reads, Enter and Space choose, Escape and Tab close. Escape and an outside click close it, and Escape and choosing put the focus back on the anchor. Opening moves the focus onto the `role="menu"` element, which is the ARIA menu-button pattern and where `aria-activedescendant` names the row the keys are on. The anchor carries none of that: a `role="button"` may not (design 225). The anchor is the button this component builds, so the ARIA is in the markup rather than written onto somebody else's node. Give it your own contents as children, or hand the element in. The list goes in the nearest `<dialog>` above it, then a `PopupContext`, then the page, so a menu inside a modal opens inside that modal and can be clicked.

**Example**

```ts
<Menu label="Actions" items={[
  { label: 'Rename', onSelect: rename },
  { label: 'Delete', type: 'danger', onSelect: remove },
]} />
```

#### `MenuGroup`

```ts
interface MenuGroup { readonly heading?: unknown; readonly items?: readonly MenuItem[]; }
```

A run of actions under a heading.

#### `MenuItem`

```ts
interface MenuItem { readonly label?: unknown; readonly icon?: unknown; readonly type?: unknown; readonly disabled?: unknown; readonly onSelect?: (event: unknown) => void; }
```

One action in a menu.

#### `MenuProps`

```ts
interface MenuProps { readonly items?: unknown; readonly open?: unknown; readonly label?: unknown; readonly icon?: unknown; readonly type?: unknown; readonly size?: unknown; readonly disabled?: unknown; readonly locations?: readonly Placement[]; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Menu` takes. Everything not named here goes to the anchor button.

#### `Meta`

```ts
Meta: (props: TagProps) => unknown
```

One `<meta>`.

**Params**

- `key`: a group of your own

**Returns** nothing where it is written. Its group is its `charset`, `http-equiv`, `name` or `property`, in that order; a `<meta>` with none of those is additive and every one is emitted.

**Example**

```ts
<Meta name="description" content={summary} />
```

#### `Modal`

```ts
Modal: (props: ModalProps, cleanup: (...fns: (() => void)[]) => void, mounted: (...fns: (() => void)[]) => void) => Mounter
```

The act, in a modal dialog.

**Params**

- `props`: `label`, `noEsc`, `noClickEsc`, `type`, `side`, `element`, `theme` and `class`. A prop this does not name reaches the act and goes no further here

**Returns** a `<dialog>`, opened as this mounts, holding a heading, a close button and the act. `type="sheet"` is the same dialog against an edge, sliding in from it rather than growing in the middle (design 202). `side` says which edge, `right` by default, and is read for no other type. Every close is the stage's `close()`: Escape through the element's own `cancel` event, a mousedown on the backdrop, and the close button. So a modal opened with `history: true` is taken down by the history entry going away, whichever of the three the person used (design 124). A stage calls a template as `h(template, props, act)`, where `props` is what the `open` carried past `name`, `history`, `template` and `children` (design 213). The act is handed the same props, so a prop this component names, `label` or `type` or `side`, is read here as well as there.

**Throws** an assert, loud in development and stripped in a release build, when there is no stage above it, and the one `elementFor` makes for an `element` that is not a `<dialog>`.

**Example**

```ts
stage.open({ name: 'edit', history: true, template: Modal, label: 'Edit' });
stage.open({ name: 'filters', template: Modal, label: 'Filters', type: 'sheet' });
```

#### `ModalProps`

```ts
interface ModalProps { readonly label?: unknown; readonly noEsc?: unknown; readonly noClickEsc?: unknown; readonly type?: unknown; readonly side?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly class?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Modal` takes. A stage hands its template the props the `open` carried (design 213), so this
component reads the ones it names and passes nothing else to the `<dialog>`: an `open` carrying a
row for the act would otherwise write it on the element as an attribute.

#### `OpenOptions`

```ts
interface OpenOptions { readonly name: string; readonly template?: Component; readonly history?: boolean; readonly [prop: string]: unknown; }
```

What `open` takes. Everything past the three named fields is props for the act.

#### `Pagination`

```ts
Pagination: (props: PaginationProps, cleanup: (...fns: (() => void)[]) => void) => Mounter
```

The pages of a long list, as buttons.

**Params**

- `props`: `page` (a cell, counted from 1), `count`, `siblings`, `onChange`, `size`, `label`, `element`, and anything else, which goes to the `<nav>`

**Returns** a `<nav aria-label>` holding a previous button, the page buttons, and a next button, all `type="quiet"`. The page showing now carries `aria-current="page"` and the `current` segment, which is the filled look. Previous is disabled on page 1 and next on the last page. Pages left out are a `<span aria-hidden="true">` on `pagination_gap`, because an ellipsis is not something to read out.

**Example**

```ts
<Pagination page={page} count={12} onChange={(at) => load(at)} />
```

#### `PaginationProps`

```ts
interface PaginationProps { readonly page?: unknown; readonly count?: unknown; readonly siblings?: number; readonly onChange?: (page: number, event: unknown) => void; readonly size?: unknown; readonly label?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Pagination` takes. Everything not named here goes to the `<nav>`.

#### `Placed`

```ts
interface Placed { readonly mode: Placement; readonly left: number; readonly top: number; readonly maxWidth: number; readonly maxHeight: number; readonly transformOrigin: string; }
```

Where the popup ends up, and what it may grow to.

#### `Placement`

```ts
type Placement = 'below-start' | 'below-end' | 'above-start' | 'above-end' | 'right-start' | 'right-end' | 'left-start' | 'left-end' | 'below' | 'above' | 'right' | 'left';
```

The twelve places a popup can go.

The eight corner modes put one of the popup's corners on one of the anchor's, so the popup
hangs off a corner: `below-start` is under the anchor with their left edges lined up.
The four side modes centre the popup on one edge of the anchor.

#### `Popup`

```ts
Popup: (props: { placement?: unknown; canClose?: ((event: unknown) => boolean) | undefined; style?: Record<string, unknown> | undefined; ref?: ((element: unknown) => void) | undefined; children?: unknown[] | undefined; }) => Mounter
```

A floating box, rendered at the popup sink rather than where it is written.

**Params**

- `placement`: where it goes, a cell so it can move; `null` hides it
- `canClose`: given the event, whether an outside click should close it. Omitted, any outside click closes it by setting `placement` to null
- `style`: merged onto the popup's own box
- `children`: what is inside

**Returns** nothing where it is written. The element is in its sink until this unmounts. Its sink is the nearest `<dialog>` above where it was written, then the sink a `PopupContext` gave, then the element the page was mounted into (design 113, amended). So a popup opened inside a modal is inside that dialog and can be clicked, and a page with no `PopupContext` still works.

**Example**

```ts
<Popup placement={where}><Card>what floats</Card></Popup>
```

#### `PopupContext`

```ts
PopupContext: (props: { popups?: Registry<unknown> | undefined; children?: unknown[] | undefined; }) => Mounter
```

Where every popup below mounts, unless it is inside a `<dialog>`, which wins.

Renders its children, then the popups, so a popup is after the page in DOM order. Together with
the `popover` attribute that is the whole of the stacking story: no z-index anywhere. A page
needs one of these only to choose where its popups go; a page with none still opens them.

**Params**

- `popups`: a registry to share with something else. Omitted, this render's own is used by the first `PopupContext` to ask for it and a fresh one by every later one
- `children`: the page

**Example**

```ts
mount(document.body, h(PopupContext, {}, h(App, {})));
```

#### `PopupPlacement`

```ts
type PopupPlacement = Placed | null;
```

Where a popup sits, as `Popup` takes it. `null` hides it.

#### `Progress`

```ts
Progress: (props: ProgressProps) => unknown
```

How far along something is.

**Params**

- `props`: `value`, `label`, `size`, `element`, and anything else, which goes to the `<progress>`

**Returns** a `<progress max="1">` on the `progress` entry: a `$muted` track and an `$accent` bar, `$space2` thick. `value` is a fraction of 1, so nothing has to divide; `null` or nothing leaves the attribute off, which is what the platform reads as indeterminate and draws as the moving bar. A number outside 0 to 1 is clamped to it, and anything that is not a finite number, `NaN` and a string included, is indeterminate.

**Throws** an assert, loud in development and stripped in a release build, when `element` is anything but a `<progress>`.

**Example**

```ts
<Progress value={done} label="Uploading" />
<Progress label="Working" />
```

#### `ProgressProps`

```ts
interface ProgressProps { readonly value?: unknown; readonly label?: unknown; readonly size?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Progress` takes. Everything not named here goes to the element.

#### `ProviderProps`

```ts
interface ProviderProps { readonly value?: unknown; readonly children?: unknown[]; }
```

What a provider takes: the value for the subtree below it.

#### `Radio`

```ts
Radio: (props: RadioProps) => Mounter
```

One choice out of a group.

**Params**

- `props`: `value`, the cell the group shares; `option`, this one's value; `label`, `description`, `error`, `disabled`, `type`, `size`, `onChange`, `element`, and anything else, which goes to the `<input>`

**Returns** an `<input type="radio">`, inside a `<div>` with its label when it was given one. Every radio handed the same `value` cell is one group, so the arrow keys move between them and Tab steps over the group as a whole, both from the platform.

**Example**

```ts
<Radio value={size} option="small" label="Small" />
<Radio value={size} option="large" label="Large" />
```

#### `RadioProps`

```ts
interface RadioProps { readonly value?: unknown; readonly option?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly onChange?: (next: unknown, event: unknown) => void; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Radio` takes. Everything not named here goes to the element.

#### `Rect`

```ts
interface Rect { readonly left: number; readonly top: number; readonly width: number; readonly height: number; }
```

A box on the screen, in the coordinates `getBoundingClientRect` uses.

#### `Region`

```ts
Region: (props: RegionProps) => Mounter
```

The subdivision field: the provinces, states or regions of one country.

**Params**

- `props`: `value`, `country`, and everything `Chooser` takes

**Returns** the chooser, over the subdivisions the provider has for that country. The cell holds the subdivision's short code. It is the same control as `Country` and for the same reason: one country has 217 subdivisions and the middle one has 11, and a threshold that opened a dropdown for a small country and a dialog for a large one would change the control under the person as they picked a country. Handed no country, or one the provider has no subdivisions for, it is disabled and says so. It does not clear the cell when the country changes: the page owns that write, and a form that clears a field the person filled has to be the page's decision. The names are the data's, which is English. Hand `Countries` a `CountryData` of your own to translate them.

**Throws** the assert for a page with no `Countries` provider above it.

**Example**

```ts
<Region label="Province" value={region} country={country} name="region"
        autocomplete="address-level1" />
```

#### `RegionProps`

```ts
interface RegionProps { readonly value?: unknown; readonly country?: unknown; readonly [prop: string]: unknown; }
```

What `Region` takes. Everything not named here goes to `Chooser` and on to the button.

#### `Registry`

```ts
interface Registry<T = unknown> { readonly items: MutableArray<T>; add(item: T): () => void; claim(): boolean; }
```

A list a component pushes into and something else renders.

#### `Render`

```ts
interface Render { readonly theme: Sheet; readonly head: HeadList; readonly stage: Registry<StageEntry>; readonly ids: Ids; readonly popups: Registry; readonly locale?: string; readonly catalog?: Catalog; }
```

Everything one render owns.

#### `Script`

```ts
Script: (props: TagProps) => unknown
```

One `<script>`.

**Params**

- `children`: the inline source, when there is no `src`
- `key`: a group of your own

**Returns** nothing where it is written. Its group is its `src` and `type`, or, with no `src`, that it is inline and its `type`. **Two inline scripts of one type are therefore one group and only the winner is emitted**; give each a `key` to keep both.

**Throws** an assert naming the fix when the inline source contains `</script`, which would end the element early and cannot be escaped inside JavaScript.

**Example**

```ts
<Script src="https://example.com/a.js" async />
```

#### `Select`

```ts
Select: (props: SelectProps, cleanup: (...fns: (() => void)[]) => void, mounted: (...fns: (() => void)[]) => void) => Mounter
```

A choice from a list.

**Params**

- `props`: `value`, `options`, `display`, `placeholder`, `open`, `label`, `description`, `error`, `disabled`, `type`, `size`, `name`, `autocomplete`, `onChange`, `element`, and anything else, which goes to the button

**Returns** a `<button role="combobox">` and a drawn list, inside a `<div>` with its label when it was given one. The cell holds the item, never the text the person reads, so an object list comes back as objects. The list is a `<div role="listbox">` of `<div role="option">` rows in a popup placed under the control at the control's width, and it is this package's on every host. It opens on a click, on ArrowDown, ArrowUp, Enter and Space, and on a printable character, which opens it and runs the type-ahead in the one press. The whole keyboard map is design 223's. A hidden `<select>` carries the same options and the same choice, takes `name` and `autocomplete`, and writes the cell when anything writes it. So a form posts the value and autofill reaches the control, and what it cannot do is draw its own highlight over a button. `placeholder` shows while the cell holds nothing and is not a row in the list. A cell holding an item the list does not have shows the placeholder and selects nothing.

**Throws** the assert `elementFor` makes for an `element` that is not a `<button>`.

**Example**

```ts
<Select label="Size" value={size} options={['small', 'large']} />
<Select value={user} options={users} display={(u) => u.name} placeholder="Pick someone" />
```

#### `SelectProps`

```ts
interface SelectProps { readonly value?: unknown; readonly options?: unknown; readonly display?: unknown; readonly placeholder?: unknown; readonly open?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly name?: unknown; readonly autocomplete?: unknown; readonly onChange?: (next: unknown, event: unknown) => void; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Select` takes. Everything not named here goes to the button.

#### `Sheet`

```ts
interface Sheet { classes(definitions: Definitions, classes: readonly string[]): string; variable(definitions: Definitions, classes: readonly string[], name: string): string | null; call(definitions: Definitions, classes: readonly string[], name: string): ThemeFunction | null; value(definitions: Definitions, classes: readonly string[], text: string): string; base(): Definitions; markup(): string; watch(fn: (css: string) => void): () => void; readonly text: Derived<string>; }
```

The theme systems for one render: its class cache and its stylesheet.

#### `Shown`

```ts
Shown: (props: { value?: unknown; invert?: unknown; children?: unknown[] | undefined; }) => unknown
```

One of two subtrees, by a condition.

**Params**

- `value`: the condition, a value or a cell
- `invert`: flip it
- `children`: the truthy branch, with `<mark.else>` for the other

**Returns** whichever branch applies, following `value` when it is a cell.

**Example**

```ts
<Shown value={open}>
  <Menu />
  <mark.else><p>nothing open</p></mark.else>
</Shown>
```

#### `Skeleton`

```ts
Skeleton: (props: SkeletonProps) => unknown
```

A grey box standing in for something that has not arrived.

**Params**

- `props`: `width`, `height`, `round`, and anything else, which goes to the `<div>`

**Returns** a `<div aria-hidden="true">` on the `skeleton` entry: the `$muted` fill, pulsing inside `prefers-reduced-motion: no-preference` and still outside it. `width` and `height` go through `style`, so a bare number is pixels and any CSS length works. It says nothing to a screen reader. The thing that is loading is what announces that, and three boxes announcing it three times is worse than silence.

**Example**

```ts
<Skeleton width="12rem" />
<Skeleton width={40} height={40} round={true} />
```

#### `SkeletonProps`

```ts
interface SkeletonProps { readonly width?: unknown; readonly height?: unknown; readonly round?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Skeleton` takes. Everything not named here goes to the element.

#### `Slider`

```ts
Slider: (props: SliderProps) => Mounter
```

A number on a line.

**Params**

- `props`: `value`, `label`, `description`, `error`, `min`, `max`, `step`, `disabled`, `type`, `size`, `track`, `element`, and anything else, which goes to the `<input>`

**Returns** an `<input type="range">`, inside a `<div>` with its label when it was given one. The cell holds a number, not the text the element carries. `step: 0` is written out as `any`, which is what the platform calls a range with no steps in it. An `onInput` of your own is called with the event, after the cell has been written. Fires the `InputContext` `slide` event with `{ component: 'Slider', label, value }` on every change, unless `track` is false.

**Example**

```ts
<Slider label="Volume" value={volume} min={0} max={11} />
```

#### `SliderProps`

```ts
interface SliderProps { readonly value?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly min?: unknown; readonly max?: unknown; readonly step?: unknown; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly track?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Slider` takes. Everything not named here goes to the element.

#### `Source`

```ts
interface Source { candidates(): Promise<readonly Candidate[]>; } (from @aweftjs/modules)
```

Somewhere modules come from. Listing evaluates nothing.

#### `Stage`

```ts
Stage: () => Mounter
```

Where the current act is rendered.

**Returns** the act `current` names, inside the template chosen for it. An act named as a module runs through `suspend`, so it shows the `LoaderContext`'s loading component while it arrives and its failed component if it never does. Inside a hydration it shows neither: the server's markup stays until the act arrives (design 243).

**Throws** an assert, loud in development and stripped in a release build, when there is no `StageContext` above it.

**Example**

```ts
<StageContext router={router} acts={acts}><Nav /><Stage /></StageContext>
```

#### `StageAct`

```ts
interface StageAct { readonly name: string; readonly loader: boolean; readonly entries: ActEntries | null; }
```

One declared act, as the registry reports it.

#### `StageContext`

```ts
StageContext: StageContextComponent
```

Hold a set of acts, and the URL when a router is given.

**Params**

- `acts`: the acts, by path. `''` is the index, `:name` takes one segment, one trailing `*name` takes the rest, and a trailing bare `*` takes nothing and parks the rest as the tail for a stage inside the act. A value is the component, or the name of a module whose factory answers `{ component, title? }` (design 242)
- `template`: what wraps the act. A pass-through when it is left off
- `fallback`: the act shown when nothing matched. This is the 404, and it is matched last
- `initial`: the act shown when no URL decides: no router, or a parent that took the whole path
- `router`: the router this stage reads. Without one the stage is a content swapper driven by `open` and `close`, and a stage inside an act takes what its parent did not match
- `sources`: where a named act comes from. The stage builds one loader over these for the whole routing tree, so a stage inside an act inherits it and takes no `sources`
- `loader`: the loader the modules are already in, instead of `sources`, from a platform that built it; the stage loads acts from it and never closes it (design 282)
- `client`: the page's connection, handed to every module as its `client` prop
- `refused`: the act shown when loading a named act rejects with a refusal (design 244)
- `children`: the page, with a `Stage` somewhere in it

**Returns** the subtree, with the stage in scope for everything under it. Reach it with `StageContext.read(context)` or `StageContext.use(stage => ...)`. Every act is also handed the stage as its `stage` prop, so an act that only wants `params` or `query` takes it as an argument. A named act is loaded, with its dependencies first, when the stage decides it, and unloaded once the next act is showing; the modules it depended on stay loaded for the page, and go when the stage is removed.

**Throws** an assert, loud in development and stripped in a release build, for an act key that is not relative, has an empty segment, has a `:` with no name, has a `*` segment anywhere but last, or cannot be told apart from another key; for a `fallback`, `initial` or `refused` naming an act that is not declared; for a `refused` naming a key with a `:name` or `*name` segment, which would render under another act's parameters; for a nested stage given `sources` or a `loader`; for `sources` beside a `loader`; for a `client` with no `sources`; for a named act with no `sources` or `loader` anywhere above it; and for an act module whose instance carries no `component`.

**Example**

```ts
<StageContext router={router} sources={[app]} client={client}
  acts={{ '': Home, 'posts/:id': 'posts/Page' }} fallback="404" refused="join">
  <Nav /><Stage />
</StageContext>
```

#### `StageContextComponent`

```ts
interface StageContextComponent { (props: StageProps): unknown; read(context: unknown): StageValue | null; node(context: unknown): ContextNode<StageValue | null> | null; use<P extends Record<string, unknown>>(build: (stage: StageValue | null) => Component<P>): Component<P>; }
```

`StageContext`, with the three ways to reach the stage from outside a consumer.

#### `StageEntry`

```ts
interface StageEntry { readonly acts: readonly StageAct[]; readonly prefix: string; readonly parent: StageEntry | null; readonly fallback: string | null; readonly current: string | null; }
```

One live `StageContext`, read-only, so a static walk can enumerate the pages of a site from one
render of it.

#### `StageProps`

```ts
interface StageProps { readonly acts: Readonly<Record<string, Act>>; readonly template?: Component; readonly fallback?: string; readonly initial?: string; readonly router?: Router; readonly sources?: readonly Source[]; readonly loader?: Loader; readonly client?: unknown; readonly refused?: string; readonly children?: unknown[]; }
```

What `StageContext` takes.

#### `StageValue`

```ts
interface StageValue { readonly current: Derived<string | null>; readonly params: Derived<Readonly<Record<string, string>>>; readonly query: Derived<Record<string, string>>; open(options: OpenOptions): void; close(): void; }
```

The stage an act is in: what is showing, what the URL said, and the two ways to change it.

#### `Style`

```ts
Style: (props: TagProps) => unknown
```

One `<style>`.

**Params**

- `children`: the CSS
- `key`: a group of your own

**Returns** nothing where it is written. Its group is its `media`, so a page overrides a layout's styles for one media query and leaves the others alone.

**Throws** an assert naming the fix when the CSS contains `</style`.

**Example**

```ts
<Style media="print">{'@page { margin: 2cm; }'}</Style>
```

#### `Subdivision`

```ts
interface Subdivision { readonly code: string; readonly name: string; }
```

One subdivision of one country: a province, a state, a region, a prefecture.

#### `Switch`

```ts
Switch: (props: { value?: unknown; cases?: Record<string, unknown> | undefined; children?: unknown[] | undefined; }) => unknown
```

One subtree of several, by a value or by the first truthy cell.

**Params**

- `value`: matched against each `<mark.case value=...>`, a value or a cell
- `cases`: an object of cells, the first truthy key winning; not to be given with `value`
- `children`: `<mark.case value=...>` for each branch, and `<mark.default>` for the rest

**Returns** the branch that matched, following whichever input is reactive.

**Throws** an assert, loud in development and stripped in a release build, when both `value` and `cases` are given, or when a case has no `value`.

**Example**

```ts
<Switch value={status}>
  <mark.case value="loading"><Spinner /></mark.case>
  <mark.case value="ready"><List /></mark.case>
  <mark.default><p>nothing yet</p></mark.default>
</Switch>
```

#### `Tab`

```ts
Tab: (props: TabProps) => Mounter
```

One of the tabs in a strip.

**Params**

- `props`: `value`, `label`, `disabled`, `element`, and anything else, which goes to the element
- `children`: the label as markup, for anything `label` cannot say

**Returns** a `<button type="button" role="tab">` on the `tab` entry, naming its panel with `aria-controls` and saying whether it is the one showing with `aria-selected`. Only meaningful inside a `Tabs`: the variant, the height, the ids and the value cell all come from the group.

**Throws** an assert, loud in development and stripped in a release build, when there is no `Tabs` above it.

**Example**

```ts
<Tab value="drafts" label="Drafts" />
```

#### `TabItem`

```ts
interface TabItem { readonly value?: unknown; readonly label?: unknown; readonly disabled?: unknown; readonly content?: unknown; }
```

One tab and the panel under it, for the common case where both are one line to write.

#### `TabPanel`

```ts
TabPanel: (props: TabPanelProps) => Mounter
```

What one tab shows.

**Params**

- `props`: `value`, `element`, and anything else, which goes to the element
- `children`: the panel's contents

**Returns** a `<div role="tabpanel" tabindex="0">` on the `tabs_panel` entry, named by its tab through `aria-labelledby`. It is focusable because the second Tab press out of the strip has to land somewhere, and a panel holding nothing focusable would swallow it. A panel that is not showing carries `hidden` and stays mounted, so coming back to it finds it as it was left. Wrap the contents in a `Shown` to have them built again instead.

**Throws** an assert, loud in development and stripped in a release build, when there is no `Tabs` above it.

**Example**

```ts
<TabPanel value="drafts"><DraftList /></TabPanel>
```

#### `TabPanelProps`

```ts
interface TabPanelProps { readonly value?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `TabPanel` takes. Everything not named here goes to the element.

#### `TabProps`

```ts
interface TabProps { readonly value?: unknown; readonly label?: unknown; readonly disabled?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Tab` takes. Everything not named here goes to the `<button>`.

#### `Table`

```ts
Table: (props: TableProps) => Mounter
```

A table of rows and columns.

**Params**

- `props`: `columns`, `rows`, `cell`, `caption`, `foot`, `label`, `striped`, `tight`, `type`, `element`, and anything else, which goes to the `<table>`

**Returns** a `<table>` inside a `<div>` on `table_scroll`, so a table wider than its box scrolls in place rather than widening the page. The box is focusable, because one that scrolls and cannot be focused is unreachable from a keyboard. `rows` goes through `each`, so pushing a row inserts one `<tr>` and moves nothing else. A `cell` function that returns a different shape for different rows steps outside what a list can clone; [`packages/dom/README.md`](/docs/packages/dom) says what that costs. Give it a `caption` or a `label`: without one the table has no name for a screen reader.

**Example**

```ts
<Table columns={['name', 'size']} rows={files} caption="Everything in this folder" />
<Table
  columns={[{ key: 'name', label: 'Name' }, { key: 'bytes', label: 'Size', align: 'right' }]}
  rows={files}
  cell={(file, column) => (column.key === 'bytes' ? readable(file.bytes) : file.name)}
/>
```

#### `TableColumn`

```ts
interface TableColumn { readonly key: string; readonly label?: unknown; readonly align?: string; readonly width?: unknown; }
```

One column: which value it shows, what it is called, and how it sits.

#### `TableProps`

```ts
interface TableProps { readonly columns?: readonly (TableColumn | string)[]; readonly rows?: unknown; readonly cell?: (row: unknown, column: TableColumn) => unknown; readonly caption?: unknown; readonly foot?: unknown; readonly label?: unknown; readonly striped?: unknown; readonly tight?: unknown; readonly type?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Table` takes. Everything not named here goes to the `<table>`.

#### `Tabs`

```ts
Tabs: (props: TabsProps, cleanup: (...fns: (() => void)[]) => void, mounted: (...fns: (() => void)[]) => void) => Mounter
```

A set of panels with one showing, and the strip that picks between them.

**Params**

- `props`: `value`, `tabs`, `orientation`, `type`, `size`, `label`, `onChange`, `element`, and anything else, which goes to the element
- `children`: `<mark.tabs>` holding `Tab`s and `<mark.panels>` holding `TabPanel`s, for anything `tabs` cannot say

**Returns** a `<div>` on the `tabs` entry holding a `<div role="tablist">` on `tabs_list` and one panel per tab. Each tab and its panel name each other with `aria-controls` and `aria-labelledby`, off ids minted from the render's counter, so a page rendered on a server and the hydration that adopts it agree (design 109). The arrows move the selection as well as the focus, so Right steps to the next panel and shows it. Home and End go to the ends, a disabled tab is stepped over, and Tab leaves the strip for the panel rather than for the next tab. `type="line"` drops the filled strip for an underline under the tab showing. `orientation` `vertical` stands the strip beside the panels and turns the arrows to Up and Down.

**Throws** an assert, loud in development and stripped in a release build, for a bare child: the tabs and the panels go in two different places, so each says which it is.

**Example**

```ts
<Tabs label="Views" value={view} tabs={[
  { value: 'all', label: 'All', content: <All /> },
  { value: 'mine', label: 'Mine', content: <Mine /> },
]} />
```

#### `TabsProps`

```ts
interface TabsProps { readonly value?: unknown; readonly tabs?: unknown; readonly orientation?: unknown; readonly type?: unknown; readonly size?: unknown; readonly label?: unknown; readonly onChange?: (next: unknown, event: unknown) => void; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Tabs` takes. Everything not named here goes to the element.

#### `TagProps`

```ts
interface TagProps { readonly key?: string; readonly children?: unknown[]; readonly [attribute: string]: unknown; }
```

Everything a head component takes beyond its own named props: attributes, and a `key`.

#### `TailClaim`

```ts
interface TailClaim { readonly tail: Derived<string>; readonly base: string; readonly router: Router | null; release(): void; }
```

What `claimTail` hands a routed child that is not a stage.

#### `TextArea`

```ts
TextArea: (props: TextAreaProps) => Mounter
```

Several lines of text, growing to fit them.

**Params**

- `props`: `value`, `label`, `description`, `error`, `placeholder`, `maxHeight`, `onEnter`, `onKeyDown`, `disabled`, `type`, `size`, `element`, and anything else, which goes to the `<textarea>`

**Returns** a `<textarea>` on its own, or the textarea inside a `<div>` with its label, description and error, when it was given any of the three. It measures itself after every change and takes the height of its content, up to `maxHeight`, after which it scrolls. On a host with no layout, which is a static render and the light tree, there is nothing to measure and it keeps the height the theme gives it.

**Example**

```ts
<TextArea label="Notes" value={notes} maxHeight="12rem" />
```

#### `TextAreaProps`

```ts
interface TextAreaProps { readonly value?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly placeholder?: unknown; readonly maxHeight?: unknown; readonly onEnter?: (event: unknown) => void; readonly onKeyDown?: (event: unknown) => void; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `TextArea` takes. Everything not named here goes to the element.

#### `TextField`

```ts
TextField: (props: TextFieldProps) => Mounter
```

One line of text.

**Params**

- `props`: `leading`, `trailing`, `value`, `label`, `description`, `error`, `placeholder`, `password`, `onEnter`, `onKeyDown`, `disabled`, `type`, `size`, `element`, and anything else, which goes to the `<input>`

**Returns** an `<input>` on its own, or the input inside a `<div>` with its label, description and error, when it was given any of the three. The cell and the element follow each other: typing writes the cell, and writing the cell writes the element. With a `leading` or a `trailing`, the input goes inside a `<div>` on the `input_group` entry which carries the border, the radius, the fill and the height, and the input itself carries none of them, so the two read as one control (design 210). The focus ring is on the box. An addon that is text is wrapped on `input_group_addon`; anything else is mounted as it is, which is what lets an `Icon` or a `Button` of `size="icon"` go there. With neither, none of that is rendered. While `error` says something the field carries `aria-invalid`, its `aria-describedby` names the message, and the message is a live region, so it is read out when it arrives.

**Example**

```ts
<TextField label="Email" value={email} error={emailError} />
<TextField label="Price" leading="$" trailing="CAD" value={price} />
```

#### `TextFieldProps`

```ts
interface TextFieldProps { readonly leading?: unknown; readonly trailing?: unknown; readonly value?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly placeholder?: unknown; readonly password?: unknown; readonly onEnter?: (event: unknown) => void; readonly onKeyDown?: (event: unknown) => void; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `TextField` takes. Everything not named here goes to the element.

#### `TextModifier`

```ts
interface TextModifier { readonly check: string | RegExp; readonly return: (match: string) => unknown; readonly [key: string]: unknown; }
```

One rule for turning part of a label into something else. Keys beyond these two are ignored.

#### `TextModifiers`

```ts
TextModifiers: Context<TextModifier[]>
```

The modifiers every `Typography` below runs over its label.

A provider replaces the list above it rather than adding to it, so a subtree that wants both
writes both. The list applies to `label` only: `children` are already markup and have nothing
for a pattern to run over.

**Example**

```ts
<TextModifiers value={[{ check: 'TODO', return: (word) => <b>{word}</b> }]}>
  <Typography label="TODO: write this" />
</TextModifiers>
```

#### `TextSource`

```ts
interface TextSource { readonly source: string; readonly key: string; readonly values: TextValues | undefined; }
```

What a text token was made from.

#### `TextToken`

```ts
interface TextToken { (...args: never[]): unknown; readonly [TEXT]: TextSource; }
```

A text token: mountable as a child, and readable through `isText` and `textOf`.

#### `TextValues`

```ts
interface TextValues { readonly context?: string; readonly [name: string]: unknown; }
```

The values a message names: a hole's value, a plural's number, a select's word, a tag's
function. A cell among them is followed. `context` is not a value: it disambiguates the key.

#### `Theme`

```ts
Theme: ThemeApi
```

A partial theme for everything below, and the registry every render starts from.

**Example**

```ts
Theme.define({ card: { padding: 16 } });
mount(document.body, h(Theme, { value: { card: { padding: 24 } } }, h(App, {})));
```

#### `ThemeApi`

```ts
interface ThemeApi extends Context<Definitions> { define(entries: Definitions): void; }
```

The provider, and the one call that writes definitions.

#### `ThemeCascade`

```ts
interface ThemeCascade { (props: ProviderProps): unknown; readonly def: string[]; read(context: unknown): string[]; node(context: unknown): ContextNode<string[]> | null; use<P extends Record<string, unknown>>(build: (h: Themed) => Component<P>): Component<P>; }
```

The theme prefix every element below inherits, and the `h` that applies it.

#### `ThemeContext`

```ts
ThemeContext: ThemeCascade
```

The theme every element below inherits.

**Params**

- `value`: one segment, or a list of them, added to what is already inherited
- `children`: the subtree that inherits it

**Example**

```ts
<ThemeContext value="primary"><Panel /></ThemeContext>
```

#### `ThemeFunction`

```ts
type ThemeFunction = (args: string[]) => string;
```

A theme function: the resolved arguments in, the value out.

A function is theme data rather than a table in this package. It sits in an entry beside the
variables, under a `$name` key, and the same precedence walk finds it, so an application's
theme can add one and a nested theme can shadow one (design 111). The colour and arithmetic
functions ship as entries of the default theme.

#### `Themed`

```ts
type Themed = (tag: unknown, props?: Record<string, unknown> | null, ...children: unknown[]) => unknown;
```

An `h` that carries the inherited theme. Same signature as `h`.

#### `Title`

```ts
Title: (props: TagProps) => unknown
```

The page's title.

**Params**

- `children`: the text, a string or a cell. A cell rewrites the tag in place
- `key`: a group of your own, for the rare page that wants two titles in the list

**Returns** nothing where it is written. The tag goes in the render's head list.

**Example**

```ts
<Head><Title>{post.title}</Title></Head>
```

#### `Toggle`

```ts
Toggle: (props: ToggleProps) => Mounter
```

An on and off switch.

**Params**

- `props`: `value`, `label`, `description`, `error`, `disabled`, `type`, `size`, `onChange`, `element`, and anything else, which goes to the `<input>`

**Returns** an `<input type="checkbox" role="switch">`, inside a `<div>` with its label when it was given one.

**Example**

```ts
<Toggle label="Email me" value={subscribed} />
```

#### `ToggleProps`

```ts
interface ToggleProps { readonly value?: unknown; readonly label?: unknown; readonly description?: unknown; readonly error?: unknown; readonly disabled?: unknown; readonly type?: unknown; readonly size?: unknown; readonly onChange?: (next: boolean, event: unknown) => void; readonly element?: unknown; readonly theme?: unknown; readonly [prop: string]: unknown; }
```

What `Toggle` takes. Everything not named here goes to the element.

#### `Tooltip`

```ts
Tooltip: (props: TooltipProps, cleanup: (...fns: (() => void)[]) => void, mounted: (...fns: (() => void)[]) => void) => Mounter
```

A tip beside its anchor, on hover and on focus.

**Params**

- `props`: `label`, `enabled`, `locations`, `type`, and anything else, which goes to the panel
- `children`: the anchor, with an optional `<mark.popup>` holding markup instead of the label

**Returns** the anchor where it was written, and the panel at the popup sink, inside the box `Detached` places. The panel is `role="tooltip"` and every element in the anchor carries `aria-describedby` naming it. The box is what reaches the top layer, so the panel carries no `popover` attribute: a popover inside a popover is laid out by the browser and leaves the box the solver placed. The pause before a hover shows it belongs to the behaviour, so every tip on a page waits the same amount of time and there is no prop for it. Focus shows it at once. `aria-describedby` is written onto the anchor's nodes when the page comes alive, rather than built into the markup, because those nodes are the caller's. A static render leaves it out, so markup a page is taken over from carries the anchor and the panel with no link between them and the link appears on the first live mount. It is taken off again when this unmounts.

**Throws** the assert `categories` makes for a slot this component does not know.

**Example**

```ts
<Tooltip label="Delete this for good"><Button label="Delete" type="danger" /></Tooltip>
```

#### `TooltipProps`

```ts
interface TooltipProps { readonly label?: unknown; readonly enabled?: unknown; readonly locations?: unknown; readonly type?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Tooltip` takes. Everything not named here goes to the panel.

#### `Transform`

```ts
type Transform<T> = (raw: unknown, parent: T, children: MutableArray<ContextNode<T>>) => T;
```

How a provider's raw value becomes the value below it.

`raw` is what the provider was given, exactly as written: a cell arrives as the cell, because a
transform that writes back into one needs it. Read one with `.get()`. The result is cached until
`raw` changes, so a cell keeps the value live and a plain value costs one call.

#### `Typography`

```ts
Typography: (props: TypographyProps) => Mounter
```

One run of themed text.

**Params**

- `props`: `type`, `label`, `element`, `theme`, and anything else, which goes to the element

**Returns** one element on the `text` entry plus a segment per word in `type`. `h1` to `h6` give that heading, `p`, `p1` and `p2` give a `<p>`, and everything else gives a `<span>`. With no `type` at all it is a `<span>` on `text` alone. The later segment wins where two disagree, so `h2_bold` is a heading at the bold weight. `label` renders first and `children` after it, and neither is required. The `TextModifiers` above it run over `label` only; a label that is neither a string nor a number renders as given. A `type` that is a cell moves the theme when it changes. It does not move the element: the element lasts as long as the component, as `Icon`'s does. A page that needs the tag to change puts the two spellings in a `Switch`.

**Throws** the assert `elementFor` makes for an `element` that is not an element, such as a component or a themed element written as `<p theme="card" />`, which is a function.

**Example**

```ts
<Typography type="h2_bold" label="Today" />
<Typography type="p2" label={note} />
```

#### `TypographyProps`

```ts
interface TypographyProps { readonly type?: unknown; readonly label?: unknown; readonly element?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Typography` takes. Everything not named here goes to the element.

#### `Validate`

```ts
Validate: (props: ValidateProps, cleanup: (...fns: (() => void)[]) => void) => Mounter
```

Check what is in a control, and say what is wrong with it.

**Params**

- `props`: `value`, `validate`, `signal`, `valid`, `error`, `showError`, `icon`, `type`
- `children`: the control

**Returns** the control, and after it the message while there is one, as a live region in the `field_error` entry. The message reaches the control too: a control of this package that was given no `error` of its own goes `aria-invalid` and its `aria-describedby` names the message rendered here (design 138). A `Validate` around a plain `<input>` still shows and announces the message, and the input itself stays unmarked, because nothing read it. The children are one control. Every control under a `Validate` takes the message, so one wrapped around two of them marks both invalid and points both at the same message; two controls want two `Validate`s. With a `signal`, checking follows what that cell holds: nothing is checked while it is falsy, and every change to `value` is checked while it is truthy (design 208). A form that clears itself and writes its signal back to false is quiet again until the next submit, and going quiet clears the message, writes `valid` true and writes `error` null. With no `signal`, checking is live from the start. Under a `ValidateContext`, this check runs again when any cell that form is checking changes, so a validator that compares its cell with another field's follows that other field. `showError` false takes the message off the screen and leaves it announced, because a control that says it is invalid and then says nothing else is a dead end. The validator is given the cell, so one that formats what was typed can write it back. Four of the eight do: `phone`, `pan`, `expDate` and `postalCode`. A validator that throws is reported the way a handler that throws is reported anywhere in this package, on a microtask the host sees, and the value counts as invalid with the error's message.

**Throws** an assert, loud in development and stripped in a release build, for a `value` that is not a cell and for a name that is not one of the eight.

**Example**

```ts
<Validate value={email} validate="email" signal={submitted}>
  <TextField label="Email" value={email} />
</Validate>
```

#### `ValidateContext`

```ts
ValidateContext: (props: ValidateContextProps) => Mounter
```

The form's answer: true while every `Validate` below it is happy.

**Params**

- `props`: `value`, a cell this writes
- `children`: the form

**Returns** the children. Each `Validate` under it registers when it mounts and leaves when it unmounts, so a field that goes away stops holding the form invalid. It also follows every cell those `Validate`s are checking, and runs all their checks again when any one of them changes (design 208). That is what a validator comparing its cell with another field's needs: confirm-must-match reads the other password, and nothing else would tell it that the other password moved.

**Example**

```ts
<ValidateContext value={allValid}><Form /></ValidateContext>
```

#### `ValidateContextProps`

```ts
interface ValidateContextProps { readonly value?: unknown; readonly children?: unknown[]; }
```

What `ValidateContext` takes.

#### `ValidateProps`

```ts
interface ValidateProps { readonly value?: unknown; readonly validate?: unknown; readonly signal?: unknown; readonly valid?: unknown; readonly error?: unknown; readonly showError?: unknown; readonly icon?: unknown; readonly type?: unknown; readonly theme?: unknown; readonly children?: unknown[]; readonly [prop: string]: unknown; }
```

What `Validate` takes.

#### `categories`

```ts
categories: (children: readonly unknown[], names: readonly string[], fallback?: string | undefined) => Category[]
```

Split a component's children into its named slots.

**Params**

- `children`: the component's own `props.children`
- `names`: the slots this component knows, in the order the caller wants them back
- `fallback`: the slot a bare child goes in. Omitted, a bare child is refused

**Returns** one category per name, in that order. A category with nothing in it is empty rather than absent, so a caller can destructure without checking.

**Throws** an assert, loud in development and stripped in a release build, naming the slots this component knows, for a mark it does not know or a bare child with no slot to go in, and for a name given twice in `names`.

**Example**

```ts
const [then, otherwise] = categories(props.children, ['then', 'else'], 'then');
```

#### `claimTail`

```ts
claimTail: (context: unknown) => TailClaim | null
```

Claim the parent stage's tail for a component that is a routed child without being a stage
(design 282): a frame that routes inside itself, say. It claims exactly what a nested
`StageContext` claims, once, and the tail is released with `release` when the component
unmounts (design 123).

**Params**

- `context`: the mount context the component was handed

**Returns** the claim, or null when there is no stage above or the tail is already claimed, which is what a second child under one act gets.

**Example**

```ts
const Room = (props): Mounter => (elem, _item, before, context) => {
  const claim = claimTail(context);
  const stop = claim?.tail.effect((tail) => { route.url = `/${tail}`; });
  ...
  return (arg) => { if (arg !== undefined) return remove(arg); stop?.(); claim?.release(); return remove(); };
};
```

#### `context`

```ts
context: (options?: ContextOptions) => Render
```

Make the systems for one render.

**Params**

- `options`: `locale`, the language the render shows, and `catalog`, its translations. Both optional; a render with neither shows every text token's source

**Returns** a fresh object sharing nothing with any other render. Hand it to `mount`, `render` or `hydrate`, or let those make their own.

**Example**

```ts
const ui = context({ locale: 'fr', catalog: fr });
const markup = await render(h(App, {}), { context: ui });
const css = ui.theme.markup();
```

#### `createContext`

```ts
createContext: <T>(def: T, transform?: Transform<T>) => Context<T>
```

Make a context.

**Params**

- `def`: the value where no provider is above
- `transform`: how a provider's raw value, the value above it, and its live children become the value below it. Omitted, the raw value wins and a null one inherits

**Returns** the provider component, carrying `def`, `read`, `node` and `use`.

**Example**

```ts
const Tone = createContext('plain');
const Button = Tone.use((tone) => (props) => h('button', { theme: ['button', tone] }, props['label']));
mount(document.body, h(Tone, { value: 'accent' }, h(Button, { label: 'Go' })));
```

#### `dark`

```ts
dark: Readonly<Record<string, Readonly<Record<string, unknown>>>>
```

The dark mode.

**Returns** a partial theme redefining every scale step and every role. Everything else, the type scale, the sizes, the motion, the component entries, is shared with the light mode.

**Example**

```ts
mount(document.body, <Theme value={dark}><App /></Theme>);

// following the operating system is the application's call, in its own theme:
Theme.define({ '*': { '_media_(prefers-color-scheme: dark)': { colorScheme: 'dark' } } });
```

#### `flagOf`

```ts
flagOf: (code: string) => string
```

The flag emoji for a country code: the two letters as regional indicator symbols.

**Params**

- `code`: a two-letter country code, in either case

**Returns** the two symbols, which a host with flag glyphs draws as a flag and one without draws as the two letters. Anything that is not two letters comes back empty, so a made-up code draws nothing rather than two boxes.

**Example**

```ts
flagOf('ca');  // '🇨🇦'
```

#### `h`

```ts
h: (tag: unknown, props?: Record<string, unknown> | null, ...children: unknown[]) => unknown
```

Make an element, a component's mounter, or a mark.

**Params**

- `tag`: an element name, an existing node, a component, or `mark.name`
- `props`: everything `dom`'s `h` takes, plus `theme`, `class` alongside it, `style` as an object, `isHovered`, `isFocused`, `isClicked`, `isTouched`, `onXxx`, and `each:name` on a component
- `children`: what goes inside, exactly as `dom` takes it

**Returns** the element itself when nothing is reactive and `ui` claimed nothing; a value `mount` binds when something inside is reactive; a mounter when `ui` claimed a prop; a mark when the tag is one.

**Throws** an assert, loud in development and stripped in a release build, for a null tag, a state prop that is not a writable cell, an `onXxx` that is not a function, and everything `dom`'s own `h` refuses.

**Example**

```ts
mount(document.body, h('button', { theme: ['button', tone], onClick: go }, 'Go'));
```

#### `html`

```ts
html: (strings: TemplateStringsArray, ...values: unknown[]) => unknown
```

Markup in a template literal, bound to `ui`'s `h`.

The same tag `dom` ships, over this package's `h`, so markup in a `ui` file is themed the same
way JSX in one is.

**Example**

```ts
mount(document.body, html`<button theme="button" onClick=${go}>Go</button>`);
```

#### `hydrate`

```ts
hydrate: (target: ParentLike, item: unknown, render?: Render | undefined) => Hydrated
```

Take over markup `render` wrote, with the `ui` systems under it.

**Params**

- `target`: the element whose children are the server's markup
- `item`: the same item the server rendered
- `render`: the systems to use. Omitted, the target's document is asked for the one every default mount into it shares, as `mount` does

**Returns** the remove function with `ready` on it, as `dom` does. The `<style data-aweft>` the server wrote is adopted rather than replaced, so a hydration makes no element for the theme. `ready` resolves once every act, page or panel the page was still loading has arrived and the markup has been checked (design 243).

**Example**

```ts
const page = hydrate(document.body, h(App, {}));
await page.ready;
```

#### `isText`

```ts
isText: (value: unknown) => value is TextToken
```

Whether a value is a text token.

**Params**

- `value`: anything

**Returns** true for what `text()` answered.

**Example**

```ts
if (isText(label)) label = textOf(context, label);
```

#### `light`

```ts
light: Readonly<Record<string, Readonly<Record<string, unknown>>>>
```

The light mode: the values the default theme already starts from.

It exists so a light island can sit inside a dark page, which is the same need read the other
way round. A page that is light throughout needs no provider at all.

**Example**

```ts
<Theme value={dark}><App><Theme value={light}><Preview /></Theme></App></Theme>
```

#### `localeOf`

```ts
localeOf: (context: unknown) => string | undefined
```

The language a render shows.

**Params**

- `context`: the opaque context `dom` handed a mounter, or the render `context()` made

**Returns** the BCP 47 tag the page gave `context()`, or undefined for a page that named none, which is what `Intl` takes for the host's own language.

**Example**

```ts
new Intl.DateTimeFormat(localeOf(context)).format(when);
```

#### `localeRegion`

```ts
localeRegion: () => string | null
```

The country the host looks like it is in, from the language settings and nothing else.

**Returns** a two-letter code, or null where there is no host to ask. `en-CA` is `CA`; a language with no region in it is maximized, so `fr` is `FR` and `pt` is `BR`, which is the host's own likely-region data rather than a table of guesses in this file. This is the language setting, not the location: no permission is asked, nothing is fetched, and an American in Toronto whose browser says `en-US` looks American. It is a suggestion for that reason, and it never becomes the value.

#### `mark`

```ts
mark: Mark
```

A named slot of children, for a component that takes more than one.

Written as a tag, `<mark.popup>...</mark.popup>`, which `ui`'s `h` reads. Called directly,
`mark('popup', props, ...children)`, for a file that does not write JSX. A mark never reaches
`mount`: the component it was written inside reads it with `categories`.

**Example**

```ts
<Detached enabled={open}>
  <button>menu</button>
  <mark.popup><Menu /></mark.popup>
</Detached>
```

#### `mount`

```ts
mount: (target: ParentLike, item: unknown, before?: Remove | undefined, render?: Render | undefined) => Remove
```

Mount an item, with the `ui` systems under it.

**Params**

- `target`: an element, or anything `dom`'s `mount` takes
- `item`: anything `dom`'s `mount` takes
- `before`: the anchor, as `dom` means it
- `render`: the systems to use. Omitted, the target's document is asked for the one every default mount into it shares, so two widgets on one page cannot mint the same class name for two different themes

**Returns** the remove function, as `dom` does. It also takes the stylesheet back out of the document head, once the last mount sharing it has gone.

**Example**

```ts
const stop = mount(document.body, h(App, {}));
```

#### `render`

```ts
render: (item: unknown, options?: { context?: Render | undefined; }) => Promise<string>
```

Render an item to markup, with the `ui` systems under it and no browser.

**Params**

- `item`: anything `dom`'s `render` takes
- `options`: `context`, the systems to use. Omitted, a fresh set is made and the CSS it generated is unreachable, so pass one whenever the page needs its stylesheet

**Returns** the item's markup. The theme's CSS and the page's head tags are not in it: read `context.theme.markup()` and `context.head.markup()` and put both in the page's own head. The head list and the stage list are both held for the whole render, because a static render takes the page down as soon as it has serialized it and both have to still be there afterwards. So a render object that has been through `render` keeps every tag its page declared and every stage its page mounted; use one per page, as design 109 says. `use(context).stage` after the call is what a static walk reads to learn which URLs the page declares (designs 126, 145).

**Example**

```ts
const ui = context();
const body = await render(h(App, {}), { context: ui });
const head = `<style data-aweft>${ui.theme.markup()}</style>${ui.head.markup()}`;
const page = `<html><head>${head}</head><body>${body}</body></html>`;
```

#### `sizeProperties`

```ts
sizeProperties: ReadonlySet<string>
```

The property names a bare number is written in pixels for.

A theme or a `style` prop may say `padding: 8`, and these are the properties where 8 means
eight pixels rather than the number eight. Everything else keeps the number as written, so
`flexGrow: 1` and `zoom: 2` are not broken by the convenience.

#### `standardIcons`

```ts
standardIcons: readonly string[]
```

The icon names the components in this package ask for, in the spelling the icon sets publish.

An application whose `Icons` stack answers all of these has every component covered.
`@aweftjs/icons/<set>/+standard` is this list taken from one installed set.

**Example**

```ts
const missing = standardIcons.filter((name) => myPack.icons[name] === undefined);
```

#### `suspend`

```ts
suspend: <P extends Record<string, unknown>>(fallback: Component | null, loader: Loader<P>, failed?: Component<{ error?: unknown; }> | null | undefined) => (props: P & { children?: unknown[] | undefined; }, cleanup: (...fns: (() => void)[]) => void, _mounted: unknown, pending: (promise: Promise<unknown>) => void) => Mounter
```

A component whose content arrives later.

**Params**

- `fallback`: what to show while the loader runs, or null to use the `LoaderContext`'s `loading`
- `loader`: called once with the component's props and its cleanup; whatever it resolves to is mounted in place of the fallback
- `failed`: what to show when the loader rejects, given `{ error }`. Omitted, the `LoaderContext`'s `failed` is used; with neither, the slot goes empty and the rejection is rethrown on a fresh task so the host reports it

**Returns** a component. It declares its promise `pending`, so a static render waits for it and a hydration keeps its pairing walk open for it, and a suspend removed before its loader settles mounts nothing and reports nothing. Inside a hydration no fallback is shown at all: the server's markup stays until the loader answers (design 243).

**Example**

```ts
const Article = suspend(Spinner, async ({ id }) => {
  const article = await fetch(`/articles/${id}`).then((r) => r.json());
  return <Body article={article} />;
});
```

#### `svg`

```ts
svg: (tag: unknown, props?: Record<string, unknown> | null, ...children: unknown[]) => unknown
```

Make an SVG element.

The same as `h` with two differences: the node is made in the SVG namespace, and there is no
theme, so `class` and `style` are written as plain attributes.

**Params**

- `tag`: an SVG element name
- `props`: as `h`, without `theme`
- `children`: as `h`

**Returns** what `h` returns.

**Example**

```ts
svg('svg', { viewBox: '0 0 24 24' }, svg('circle', { cx: 12, cy: 12, r: 10 }));
```

#### `template`

```ts
template: (spec: TemplateElement, edits: readonly TemplateEdit[]) => Template
```

The static shape of a `ui` subtree, made once per document and instanced per use.

The signature is `dom`'s, and so is everything about the shape: `build` emits a call to this
one instead when the file's `h` came from `@aweftjs/ui`. What differs is that each instance's
properties are split first, so `theme`, `style`, `onXxx` and the state cells do what `h` would
have done with them.

**Params**

- [`spec`](/spec): the element, its literal attributes and its static children, nested
- `edits`: where something varies, in the order the source evaluates the values

**Returns** a function taking one value per edit. It answers what `ui`'s `h` would have answered for the same subtree: the instance itself when `ui` claimed nothing anywhere in it, and otherwise a mounter.

**Throws** whatever `dom`'s `template` throws, and the asserts `h` makes about a claimed prop.

**Example**

```ts
const row = template(['li', null, ['span', null]], [['props', []], ['child', [0], -1]]);
mount(list, row([{ theme: 'row' }, title]));
```

#### `text`

```ts
text: (source: string, values?: TextValues | undefined) => TextToken
```

A piece of text a page shows, to be looked up in the render's catalog where it mounts.

**Params**

- `source`: the message as written, in the subset of ICU MessageFormat the package reads: `{name}` holes, `{n, plural, one {# item} other {# items}}`, `{kind, select, book {a book} other {a thing}}`, and `<link>the docs</link>` around part of it
- `values`: what the message names. A hole takes any value and a cell is followed; a plural takes a number; a select takes a word; a tag takes a function from the inner content to what to mount. `context` is one word that tells two meanings of the same source apart and becomes part of the key

**Returns** a token. As a child it mounts the message, translated when the render has a catalog with its key and as written when it has none. In a prop, `ui`'s `h` writes the resolved string on the element. `Typography` runs its modifiers over the resolved string, and a head component writes it into its tag. `textOf` answers the string for code that needs characters.

**Throws** an assert, loud in development and stripped in a release build, for a source that is not a string. A message that cannot be read asserts where it is first formatted, naming the offset.

**Example**

```ts
<p>{text('Hello {name}', { name })}</p>
<Button label={text('Save')} />
<p>{text('Read <link>the docs</link>', { link: (inner) => <a href="/docs">{inner}</a> })}</p>
```

#### `textOf`

```ts
textOf: (context: unknown, value: unknown) => string
```

The string a value shows, resolved in a render.

**Params**

- `context`: the opaque context `dom` handed a mounter, or the render `context()` made, so code outside any mount, a server module building a title, resolves against a render of its own
- `value`: a text token, a string, a number, or a cell holding one

**Returns** for a token, its message in the render's language with its values as they are now written in and a tag reduced to what is inside it; for a string or a number, that; for null or undefined, the empty string. A context with no `ui` systems shows a token's source.

**Example**

```ts
document.title = textOf(context, text('Inbox'));
```

#### `trackedMount`

```ts
trackedMount: () => [MutableArray<NodeLike>, (props: { children?: unknown[] | undefined; }) => unknown]
```

Mount children into a target that only records them.

**Returns** the array the real nodes appear in, and a mounter to render where the children belong. The caller renders the mounter and then the array, so the nodes still land in the document and the caller also has them, which is what measuring or listening on children you did not build needs.

**Example**

```ts
const [nodes, virtual] = trackedMount();
return [h(virtual, {}, ...props.children), nodes];
```

#### `use`

```ts
use: (value: unknown) => Render
```

The render a mount belongs to.

**Params**

- `value`: the opaque context `dom` handed the mounter

**Returns** the render's systems.

**Throws** an assert, loud in development and stripped in a release build, when the mount was not started by one of `ui`'s entry points, naming what to call instead.

**Example**

```ts
const sheet = use(context).theme;
```

#### `useAbort`

```ts
useAbort: <A extends unknown[]>(fn: (signal: AbortSignal, ...args: A) => void) => (...args: A) => () => void
```

Turn a function that wants an abort signal into one that hands back its abort.

**Params**

- `fn`: called with a fresh `AbortSignal` and whatever else the caller passed

**Returns** a function that runs `fn` and answers the abort, so it can go straight into `cleanup`. Every listener registered with `{ signal }` inside comes off in one call.

**Example**

```ts
const listen = useAbort((signal, node) => {
  node.addEventListener('scroll', onScroll, { signal });
  node.addEventListener('resize', onResize, { signal });
});
cleanup(listen(element));
```

#### `usedText`

```ts
usedText: (render: Render) => readonly string[]
```

The keys the text tokens of a render looked up, in the order they were first asked for.

**Params**

- `render`: a render that has mounted or rendered a page

**Returns** the keys. A key is here whether or not the catalog had an entry for it, which is what lets a static walk report the entries a catalog lacks and the ones nothing uses.

**Example**

```ts
const missing = usedText(ui).filter((key) => catalog[key] === undefined);
```

### `@aweftjs/ui/countries`

#### `CountryData`

```ts
interface CountryData { readonly codes: readonly string[]; regions(code: string): readonly Subdivision[]; readonly aliases?: Readonly<Record<string, readonly string[]>>; }
```

What `Countries` holds: the codes a form may offer, their subdivisions, and the aliases.

#### `Subdivision`

```ts
interface Subdivision { readonly code: string; readonly name: string; }
```

One subdivision of one country: a province, a state, a region, a prefecture.

#### `countryAliases`

```ts
countryAliases: Readonly<Record<string, readonly string[]>>
```

The words a person types that no data set carries.

Keyed by the two-letter code, lowercase, and searched with the country's own name. Two kinds of
entry: a name the country used to have or is also called (`Holland`, `Burma`, `Ivory Coast`), and
the letters a person actually types for it (`uk`, `usa`, `uae`, `nz`). A formal name is not here:
`Republic of India` is not what anybody types, and the search already matches the name the host
gives and the code itself.

#### `countryData`

```ts
countryData: (load?: (() => Promise<unknown>) | undefined) => Promise<CountryData>
```

The whole country list, from the package that has it.

**Params**

- `load`: where to get the data from. Omitted, the optional `country-region-data` peer. Hand one over for a copy of that data you got some other way: a file the page fetched, a module an application vendored, a subset it built. What comes back must have an `allCountries`

**Returns** a promise of `CountryData` over every territory the data has, with this package's aliases on it. Hand it to `Countries`. The peer is optional and is named here and nowhere else. In a bundler an import it cannot resolve fails the build before this runs, naming the same package; in Node a missing peer is the refusal below.

**Throws** `countries-not-installed` when the load fails, carrying the install command, and `countries-unreadable` when what it answers is not the list this reads. A `load` of your own that throws refuses the same way, with the same message: the common reason to be here is the peer, and a caller who handed over a loader can see what their own loader did.

**Example**

```ts
const data = await countryData();
<Countries value={data}><Country value={country} /></Countries>
```

### `@aweftjs/ui/icon-names`

#### `standardIcons`

```ts
standardIcons: readonly string[]
```

The icon names the components in this package ask for, in the spelling the icon sets publish.

An application whose `Icons` stack answers all of these has every component covered.
`@aweftjs/icons/<set>/+standard` is this list taken from one installed set.

**Example**

```ts
const missing = standardIcons.filter((name) => myPack.icons[name] === undefined);
```

### `@aweftjs/ui/text.json`

Data, not code: the file as the package ships it.

## Refusals

Every error this package raises carries a `reason` to switch on and a `fix` that says what to do. These are its reasons, from `errors.txt`.

| reason | fix |
|---|---|
| `countries-not-installed` | Install the package the message names, or hand your own CountryData to Countries. |
| `countries-unreadable` | Install a 3 or 4 release of the package, whose allCountries is a list of countries. |

## Recipes

The programs in the stack's gate that use this package, each a job someone would have.

- [`recipes/full-stack`](/docs/recipes/full-stack): A page and a server in one directory: the page reaching the server in development through the dev server's proxy, and a sign-in that sets the cookie on that one origin
- [`recipes/ui`](/docs/recipes/ui): A page with themes, contexts, control flow, a popup and a suspend, built by vite and driven in a real browser
- [`recipes/icons`](/docs/recipes/icons): Icons named three ways, and what each way puts in the bundle
- [`recipes/routed-site`](/docs/recipes/routed-site): A site with real URLs: nested pages, a page with a parameter, a page that arrives later, a dialog the back button dismisses, and a title per page
- [`recipes/ssg`](/docs/recipes/ssg): A routed site written out as files, served by anything, and taken over in place when the browser gets to it
- [`recipes/translated-site`](/docs/recipes/translated-site): A site written in one language and launched in three: the build finds every string and writes the catalog an agent fills, each language is a tree of pages with the plural rules of its own, a stored act is compiled where it runs, and the Ukrainian page hydrates in place
- [`recipes/static`](/docs/recipes/static): A generated site served by the stack's own server: one process is the whole deployment, and the page still comes alive where it stands
- [`recipes/logs`](/docs/recipes/logs): A page recorded end to end in a browser and the visit read back: an error, a rejection, a console line, a failed call on both sides, a commit's shape with a private slot absent, a typed character never stored, sign-in mid-visit
- [`recipes/uploads`](/docs/recipes/uploads): A page uploads pictures under the gate and they paint from `/files/<id>`; each refusal reaches the page with its reason, a module makes a file of its own, and the static battery behind it never sees a file
- [`recipes/notify`](/docs/recipes/notify): Two pages of one user hear a send live and mark it read for each other, a device registered from the page, email and push against two fakes, a failed mail kept, a forged write refused, a restart, and a server with no store sending a contact form's mail
- [`recipes/room`](/docs/recipes/room): An act module stored on the server, run in a frame on the page: the board it shares reaches the server, its ask carries the page's identity, its own stage navigates on the page's URL under the host act, an error inside reaches the page's logs, and the frame paints but cannot fetch
- [`recipes/posts-to-pages`](/docs/recipes/posts-to-pages): Pages written while the application runs: a post published over a socket becomes a page, and a scheduled full write refreshes the sitemap
- [`recipes/accessible-page`](/docs/recipes/accessible-page): A page everyone can use, driven by keyboard and audited in both modes, and each guardrail catching one page written wrong: the build refusing an element no one can read, the mount throwing on a nameless button and a page with no language, `audit` and `walk` reading what only the rendered page shows
