# Domphy — Full LLM Context
> One-shot dump for code generation. Contains: critical rules, quickstart, core runtime docs, theme docs, and every `@domphy/ui` patch source. Prefer the curated `llms.txt` index for targeted lookups.
---
## Critical rules
- Build UIs as plain objects keyed by HTML tag. Apply patches via `$`. Never wrap in components.
- Never inline typography styles. Use typography patches: `small()`, `paragraph()`, `heading()`, `link()`, `strong()`, `emphasis()`, `code()`, `keyboard()`.
- Forms use `@domphy/form` (`createForm` from `@domphy/form/domphy`): bind inputs with `value: (l) => field.value(l)` + `onInput: (e) => field.handleChange(e.target.value)`. The old ui `form()`/`field()` patches and `FormState`/`FieldState` were removed; only `formGroup()` (layout) remains in `@domphy/ui`.
- Reactive content uses `(listener) => state.get(listener)`. Controlled inputs (value bound to a state you also `.set()` in `onInput`) are safe.
- Data/logic = 1-1 TanStack core ports + a Domphy adapter at the `/domphy` subpath (`@domphy/core` peer dep): query, table, router, virtual, form. Drag & drop: `@domphy/dnd`. Animation: the `motion()` patch. App framework (Next-style): `@domphy/app` (incl. lazy code-split routes). Markdown→Domphy: `@domphy/markdown`. Mermaid: `@domphy/mermaid`.
- Derived reactivity: `computed`/`effect`/`effectScope`/`batch`/`untrack` in `@domphy/core`; `flushSync()` drains reactivity synchronously (tests/imperative). Self-check with `@domphy/doctor` `diagnose`/`validate`/`fix` (rules incl. raw-theme-value, unknown-tone).
- Build tool: tsup. Docs: DomphyPress (built on `@domphy/app` + `@domphy/markdown`).
---
## Landing
# Domphy
**The AI-friendly UI framework. Patch-based, native elements, no components.**
Framework-agnostic, no JSX, no virtual DOM, no build step required — and the most **AI-friendly** UI framework (learnable from one spec file, self-correcting via [`@domphy/doctor`](/docs/doctor/)).
**Runtime + design system** — tiny, tree-shakeable:
- `@domphy/core` — runtime: rendering, reactivity, lifecycle, SSR, CSS-in-JS (≈ `react-dom` + SSR + CSS-in-JS in one)
- `@domphy/theme` — context-aware color/size/spacing tokens
- `@domphy/ui` — 86 patches for native HTML (≈ MUI)
**Data & logic** — 1-1 ports of the TanStack cores (identical API) + a Domphy adapter at the `/domphy` subpath:
- `@domphy/query` — async state (TanStack Query core)
- `@domphy/table` — headless tables (TanStack Table core)
- `@domphy/router` — type-safe routing (TanStack Router core)
- `@domphy/virtual` — virtualization (TanStack Virtual core)
- `@domphy/form` — forms (TanStack Form core)
**App layer & tools:**
- `@domphy/dnd` — drag & drop / sortable lists
- `@domphy/palette` — color-palette engine (generate accessible ramps + measure quality); design-time companion to theme
- `@domphy/app` — Next.js App Router-style framework (routes, layouts, loaders+SWR, metadata, middleware, parallel/intercepting routes, lazy code-split routes, SSR + streaming, API routes)
- `@domphy/markdown` — Markdown → Domphy element trees for SSR/SSG (this docs site runs on it)
- `@domphy/mermaid` — render Mermaid diagrams (build-time SVG + client patch)
- `@domphy/doctor` — static analyzer that flags non-idiomatic code (`diagnose`/`validate`; powers AI self-correction)
- `@domphy/mcp` — MCP server exposing patches/packages/rules + doctor + app-block registry to agents
Domphy removes component boundaries, unifies SSR and CSR under one model, automates context-aware styling, and works with any JavaScript library without adapters or plugins. For anything outside these packages (charts, rich text, i18n…), use the vanilla library directly — see [Integrations](/docs/integrations/).
## Why Domphy
From the author:
> I published Domphy in February 2026, at 41 years old. I spent 10 years as a structural architect and 6 years teaching myself to code (2 years with js/ts). Every time I tried to learn React or Vue, something felt wrong: logic scattered between data and UI, too many abstractions, too many plugins just to ship a feature. So I built what I wished existed.
> I introduce Patch-based UI Architecture, a paradigm for composing web interfaces distinct from component-based, directive-based, and mixin-based approaches. A Patch is formally defined as a function returning a PartialElement: a composable, stateless descriptor that augments a host element's behavior without wrapping, replacing, or owning it. Unlike existing composition models, a Patch carries no rendering lifecycle, holds no state, and creates no DOM boundary.
## Installation
npm install @domphy/ui
```
```html [CDN]
```
## Quick Start
// source: /docs/demos/core/counting.ts
---
## Quickstart
# 5-Minute Quickstart
## Install
npm create domphy@latest my-app
cd my-app
npm install
npm run dev
```
```bash [Add to existing project]
npm install @domphy/ui @domphy/core @domphy/theme
```
```html [CDN]
```
`npm create domphy@latest` scaffolds a Vite + TypeScript starter with everything wired up. For an existing project, `npm install @domphy/ui @domphy/core @domphy/theme` is enough — `@domphy/ui` depends on both as peer packages.
## 1. Hello World
A Domphy element is a plain object. The key is the HTML tag, the value is the content.
// source: /docs/demos/quickstart/01-hello.ts
No classes, no components, no JSX. Just objects.
## 2. Add Patches
A **patch** is a function that adds styling and behavior to an element. Apply it with the `$` property.
// source: /docs/demos/quickstart/02-patches.ts
Every patch handles its own sizing, spacing, colors, and accessibility. You write the structure — patches do the rest.
## 3. Reactive State
Use `toState()` for reactive values. Read with `state.get(listener)` inside a reactive function to auto-subscribe.
// source: /docs/demos/quickstart/03-state.ts
No virtual DOM, no diffing. Changing state updates only the properties that read it.
## 4. Forms
Form state, validation, and submission live in [`@domphy/form`](/docs/form/) (`createForm`) — a 1-1 port of `@tanstack/form-core`. `@domphy/ui` provides the presentation: native inputs with the input patches, `label`, and `formGroup` for layout. Bind each field with `value: (l) => field.value(l)` and forward events to `field.handleChange(...)`.
// source: /docs/demos/quickstart/04-form.ts
## What's Next
- [Core concepts](/docs/core/) — Syntax, reactivity, lifecycle
- [Theme](/docs/theme/) — Tone, size, density
- [All 86 patches](/docs/ui/) — Buttons, inputs, cards, dialogs, and more
- [Research](/docs/research/) — The two papers behind the design system
---
## Core docs
### api/attribute-list.md
# AttributeList
Manages HTML attributes on an `ElementNode`. Accessed via `node.attributes`.
Attributes set via element definition are managed automatically. Use `AttributeList` directly when you need to read or mutate attributes imperatively inside lifecycle hooks.
## Methods
### `get(name)`
Returns the current value of an attribute.
```ts
node.attributes.get("class") // "btn active"
node.attributes.get("disabled") // true | undefined
```
---
### `set(name, value)`
Sets or updates an attribute value.
```ts
node.attributes.set("aria-expanded", "true")
node.attributes.set("tabindex", 0)
node.attributes.set("disabled", true)
```
---
### `has(name)`
Returns `true` if the attribute exists.
```ts
node.attributes.has("disabled") // boolean
```
---
### `remove(name)`
Removes an attribute from the element.
```ts
node.attributes.remove("disabled")
```
---
### `toggle(name, force?)`
Toggles a boolean attribute (`disabled`, `hidden`, `checked`, etc.).
```ts
node.attributes.toggle("disabled") // flip
node.attributes.toggle("disabled", true) // force on
node.attributes.toggle("disabled", false) // force off
```
---
### `addListener(name, callback)`
Subscribes to changes on a specific attribute after the node is mounted and the attribute already exists. The listener auto-releases when the node is removed.
```ts
node.attributes.addListener("aria-expanded", (value) => {
console.log("expanded:", value)
})
```
If you need to observe a value immediately, set the attribute first, then subscribe inside `_onMount` or another mounted lifecycle hook.
---
### `addClass(className)`
Adds a class to the element's class list.
```ts
node.attributes.addClass("active")
```
---
### `removeClass(className)`
Removes a class from the element's class list.
```ts
node.attributes.removeClass("active")
```
---
### `hasClass(className)`
Returns `true` if the class exists in the element's class list.
```ts
node.attributes.hasClass("active") // boolean
```
---
### `toggleClass(className)`
Toggles a class in the element's class list.
```ts
node.attributes.toggleClass("active")
```
---
### `replaceClass(oldClass, newClass)`
Replaces an existing class with a new one.
```ts
node.attributes.replaceClass("old", "new")
```
### api/element-list.md
# ElementList
Manages the ordered list of child nodes under an `ElementNode`. Accessed via `node.children`.
```ts
node.children // ElementList
node.children.items // NodeItem[]
```
## Properties
| Property | Type | Description |
|---|---|---|
| `items` | `NodeItem[]` | Ordered array of child nodes (`ElementNode` or `TextNode`) |
| `owner` | `ElementNode` | The `ElementNode` that this list belongs to |
## Methods
### `insert(input, index?, updateDom?, silent?)`
Creates a new child node and inserts it at the given index. Returns the new node.
```ts
// Append at end
const created = node.children.insert({ div: "Hello" })
// Insert at specific index
const created = node.children.insert({ div: "Hello" }, 2)
// Insert without touching DOM (DOM already updated externally)
const created = node.children.insert({ div: "Hello" }, 2, false)
```
| Parameter | Type | Default | Description |
|---|---|---|---|
| `input` | `DomphyElement \| string \| number \| null` | required | Element or scalar child content to insert |
| `index` | `number` | end | Position to insert at |
| `updateDom` | `boolean` | `true` | Whether to update the DOM |
| `silent` | `boolean` | `false` | Whether to suppress the `Update` hook |
Returns the created `ElementNode` or `TextNode`. Cast to `ElementNode` when you need methods like `.remove()`:
```ts
const toastNode = node.parent!.children.insert(Toast) as ElementNode
setTimeout(() => toastNode.remove(), 3000)
```
---
### `remove(item, updateDom?, silent?)`
Removes a specific node from the list.
```ts
node.children.remove(targetNode)
// Remove without touching DOM
node.children.remove(targetNode, false)
```
| Parameter | Type | Default | Description |
|---|---|---|---|
| `item` | `NodeItem` | required | The node to remove |
| `updateDom` | `boolean` | `true` | Whether to remove from DOM |
| `silent` | `boolean` | `false` | Whether to suppress the `Update` hook |
Triggers `BeforeRemove` hook if present — removal waits for `done()` to be called.
---
### `update(inputs, updateDom?, silent?)`
Reconciles the child list against a new array of inputs. Reuses keyed nodes, inserts new ones, removes stale ones — in order.
```ts
node.children.update(newItemsArray)
// Sync logical list without touching DOM (DOM already updated externally)
node.children.update(newItemsArray, false)
```
| Parameter | Type | Default | Description |
|---|---|---|---|
| `inputs` | `ElementInput[]` | required | New desired child list |
| `updateDom` | `boolean` | `true` | Whether to update the DOM |
| `silent` | `boolean` | `false` | Whether to suppress hooks |
Pass `false` for `updateDom` when an external library (e.g. SortableJS) has already updated the DOM — prevents double update.
---
### `move(fromIndex, toIndex, updateDom?, silent?)`
Moves a node from one index to another within the list.
```ts
node.children.move(0, 2)
// Move logical position only — DOM already moved externally
node.children.move(oldIndex, newIndex, false)
```
| Parameter | Type | Default | Description |
|---|---|---|---|
| `fromIndex` | `number` | required | Current position |
| `toIndex` | `number` | required | Target position |
| `updateDom` | `boolean` | `true` | Whether to move in DOM |
| `silent` | `boolean` | `false` | Whether to suppress the `Update` hook |
---
### `swap(aIndex, bIndex, updateDom?, silent?)`
Swaps two nodes at the given indices.
```ts
node.children.swap(0, 1)
```
| Parameter | Type | Default | Description |
|---|---|---|---|
| `aIndex` | `number` | required | Index of first node |
| `bIndex` | `number` | required | Index of second node |
| `updateDom` | `boolean` | `true` | Whether to swap in DOM |
| `silent` | `boolean` | `false` | Whether to suppress the `Update` hook |
---
### `clear(updateDom?, silent?)`
Removes all children.
```ts
node.children.clear()
```
---
### `generateHTML()`
Generates the HTML string for all child nodes. Used for Server-Side Rendering (SSR).
```ts
const html = node.children.generateHTML()
```
---
## The `updateDom` flag
All mutating methods accept `updateDom` (default `true`). Pass `false` when the DOM has already been updated by an external source — prevents double mutation.
Common case: SortableJS drag-and-drop.
```ts
Sortable.create(el, {
onEnd(evt) {
// SortableJS already moved the DOM node — sync logical tree only
node.children.move(evt.oldIndex!, evt.newIndex!, false)
}
})
```
## Common patterns
**Insert and auto-remove (toast):**
```ts
onClick: (_, node) => {
const toastNode = node.parent!.children.insert(Toast) as ElementNode
setTimeout(() => toastNode.remove(), 3000)
}
```
**Reactive list (state-driven):**
```ts
const items = toState- ([])
const List: DomphyElement<"ul"> = {
ul: (listener) => items.get(listener).map(item => ({
li: item.name,
_key: item.id,
}))
}
```
**Imperative reorder:**
```ts
node.children.move(from, to)
node.children.swap(a, b)
```
### api/element-node.md
# ElementNode
Core node representing a single HTML element in the Domphy tree.
```ts
import { ElementNode } from "@domphy/core"
const node = new ElementNode({ div: "Hello World" })
node.render(document.body)
```
## Constructor
```ts
new ElementNode(domphyElement: DomphyElement, parent?: ElementNode | null)
```
## Properties
| Property | Type | Description |
|---|---|---|
| `type` | `string` | Always `"ElementNode"` |
| `parent` | `ElementNode \| null` | Parent node. `null` if root |
| `tagName` | `TagName` | HTML tag name e.g. `"div"` |
| `children` | `ElementList` | Child nodes |
| `styles` | `StyleList` | Scoped CSS styles |
| `attributes` | `AttributeList` | HTML attributes |
| `domElement` | `HTMLElement \| null` | Mounted DOM element |
| `key` | `string \| number \| null` | Identity key for diffing |
| `nodeId` | `string` | Hash used for scoped CSS class generation |
| `_portal` | `((root) => Element) \| undefined` | Redirects DOM mount target when present |
The scoped CSS class is attached through `node.attributes` using the pattern ``${tagName}_${nodeId}``.
## Methods
### `render(domElement)`
Creates a DOM node and appends it to the target.
```ts
node.render(document.body)
node.render(document.getElementById("app")!)
```
### `mount(domElement, domStyle?)`
Hydrates onto an existing DOM element. Used for SSR.
```ts
const html = node.generateHTML()
const css = node.generateCSS()
// ... send to client ...
const domStyle = document.getElementById("domphy-style") as HTMLStyleElement
node.mount(document.getElementById("app")!, domStyle)
```
When doing SSR, render CSS into `` on the server, then pass that same style element to `mount()` on the client.
### `remove()`
Removes this node from its parent.
```ts
node.remove()
```
### `merge(partial)`
Updates this node from a partial element descriptor.
```ts
node.merge({ style: { color: "red" }, class: "active" })
```
### `addEvent(name, callback)`
Registers a DOM event listener. Multiple callbacks are chained.
```ts
node.addEvent("click", (e, node) => console.log(node.tagName))
```
### `addHook(name, callback)`
Registers a lifecycle hook. Multiple callbacks are chained.
```ts
node.addHook("Mount", (node) => console.log("mounted"))
node.addHook("BeforeRemove", (node, done) => {
animate(node.domElement).then(done)
})
```
| Hook | Trigger |
|---|---|
| `Schedule` | `(node, rawElement) => void` — fired before parsing; use to apply context-aware patches via `merge(rawElement, ...)` |
| `Init` | `(node) => void` — fired after parsing, before insertion into the tree |
| `Insert` | Node added to children list |
| `Mount` | DOM element created |
| `BeforeUpdate` | Before children diff |
| `Update` | After children diff |
| `BeforeRemove` | Before DOM removal — call `done()` to proceed |
| `Remove` | After DOM removal |
| `Error` | Caught error from a reactive child (`(node, error, reset) => void`) — call `reset()` to clear children and render fallback |
### `getRoot()`
Returns the root node of the tree.
```ts
const root = node.getRoot()
```
### `getContext(name)` / `setContext(name, value)`
Inherited context — walks up the tree to find the nearest value.
```ts
// Parent
node.setContext("theme", "dark")
// Any descendant
const theme = node.getContext("theme") // "dark"
```
### `getMetadata(name)` / `setMetadata(key, value)`
Local metadata — not inherited by children.
```ts
node.setMetadata("id", "user-123")
node.getMetadata("id") // "user-123"
```
### `generateHTML()`
Generates HTML string. Used for SSR.
```ts
const html = node.generateHTML()
// "
Hello
"
```
### `generateCSS()`
Generates CSS string for this node and all descendants. Used for SSR.
```ts
const css = node.generateCSS()
```
### api/notifier.md
# Notifier
Subscription utility used internally by `State` and `AttributeList`.
## Methods
### `addListener(event, listener)`
Registers a listener for an event. Returns a `release` function to unsubscribe.
```ts
const release = notifier.addListener("change", (value) => {
console.log(value)
})
// Unsubscribe
release()
```
If the listener has an `onSubscribe` callback, it is called immediately with the `release` function — useful for auto-cleanup:
```ts
const listener = (value: string) => console.log(value)
listener.onSubscribe = (release) => {
node.addHook("BeforeRemove", release) // auto-cleanup on node remove
}
notifier.addListener("change", listener)
```
### `removeListener(event, listener)`
Removes a specific listener from an event.
```ts
notifier.removeListener("change", listener)
```
### `notify(event, ...args)`
Calls all listeners registered for an event.
```ts
notifier.notify("change", newValue)
```
## `Handler` type
```ts
type Handler = ((...args: any[]) => any) & {
onSubscribe?: (release: () => void) => void
}
```
`onSubscribe` is called once when the listener is registered. Use it to tie the listener's lifetime to another object.
### api/state.md
# State
Reactive value container. When the value changes, all listeners are notified.
## `ReadableState`
A read-only view of a `State`. Exposes only `get(listener?)` — no `set`, `reset`, or `addListener`. Use it when you want to pass a state to a consumer that should read but not mutate it.
```ts
export type ReadableState = {
readonly _isState: true;
get(listener?: ValueListener): T;
};
```
The `_isState: true` discriminant lets runtime code and type guards distinguish a `ReadableState` from a plain value.
```ts
import type { ReadableState } from "@domphy/core"
function display(count: ReadableState) {
return { p: (l) => `Count: ${count.get(l)}` }
}
```
`ReadableState` is exported as a named type from `@domphy/core`. `State` satisfies `ReadableState` — any `State` can be passed where a `ReadableState` is expected. `toState()` also accepts `ReadableState` as input (returns it as-is).
```ts
import { toState } from "@domphy/core"
const count = toState(0)
count.get() // 0
count.set(1) // notify all listeners
count.get() // 1
count.reset() // back to 0
```
Create a `State` with `toState()` from `@domphy/core`. The `toState()` function is documented in the Utilities page.
## Methods
### `get(listener?)`
Returns the current value. If a listener is provided, subscribes it to future changes.
```ts
const value = count.get()
// With listener — auto-subscribe
const value = count.get(listener)
```
### `set(newValue)`
Updates the value and notifies all listeners.
```ts
count.set(5)
```
### `reset()`
Resets the value to `initialValue`.
```ts
const filter = toState("all")
filter.set("active")
filter.reset()
filter.get() // "all"
```
### `addListener(listener)`
Subscribes a listener to value changes. Returns a release function.
```ts
const release = count.addListener((value) => {
console.log(value)
})
// Unsubscribe
release()
```
## Reactive children
Pass a function as children to make an element reactive:
```ts
const count = toState(0)
const node: DomphyElement = {
p: (listener) => `Count: ${count.get(listener)}`
// ↑ subscribes automatically
}
```
When `count.set()` is called, the element re-renders automatically.
## `initialValue`
The value passed to the constructor. Used by `reset()`.
```ts
const count = toState(0)
count.initialValue // 0
```
## `ValueOrState`
A union type accepted by patch props and element attributes that can be either a plain value, a reactive `State`, or a read-only `ReadableState`.
```ts
export type ValueOrState = T | State | ReadableState;
```
Use it in function signatures when a prop should accept both static values and reactive states:
```ts
import type { ValueOrState } from "@domphy/core"
function myPatch(open: ValueOrState): PartialElement {
return {
ariaExpanded: typeof open === "object" && open._isState
? (l) => (open as ReadableState).get(l)
: open,
}
}
```
In practice most patch props accept `ValueOrState` so callers can pass `true` / `false` or a `toState(false)` interchangeably.
### api/text-node.md
# TextNode
Represents a text or inline HTML node in the Domphy tree. `TextNode` is created automatically when children contain strings or numbers. You usually do not instantiate it directly.
```ts
const node: DomphyElement = {
div: "Hello World" // -> TextNode internally
}
const node2: DomphyElement = {
div: 42 // -> TextNode internally
}
const node3: DomphyElement = {
div: "Bold" // -> TextNode with inline HTML
}
```
## Properties
| Property | Type | Description |
|---|---|---|
| `type` | `string` | Always `"TextNode"` |
| `parent` | `ElementNode` | Parent node |
| `text` | `string` | Current text content |
| `domText` | `ChildNode` | Mounted DOM node |
## Inline HTML
`TextNode` accepts a single-root HTML string. Multiple root elements are not supported.
```ts
"Hello" // valid
"" // valid
"Hello World" // invalid: multiple roots
```
Single-root HTML is required so DOM operations like `move()` and `swap()` can keep node identity stable.
## Empty string
An empty string `""` is stored as a zero-width space (`\u200B`) so the DOM node still exists.
```ts
{ div: "" } // renders as
```
## `generateHTML()`
Returns the text content as an HTML string. Used for SSR.
```ts
node.generateHTML() // "Hello World" or "" for empty string
```
### api/utilities.md
# Utilities
Top-level helper functions exported by `@domphy/core`.
```ts
import { toState, merge, hashString } from "@domphy/core"
```
Use `Utilities` here rather than `Functions`: these are reusable helper APIs, not the main object model like `ElementNode`, `ElementList`, or `State`.
## `toState(value, name?)`
Creates a `State` from a raw value. If the input is already a `State` or `ReadableState`, returns it as-is.
```ts
const a = toState(0) // State
const b = toState(a) // same State, no wrapping
const c = toState(0, "count") // State with debug name "count"
```
| Parameter | Type | Description |
|---|---|---|
| `value` | `T \| State \| ReadableState` | Raw value, existing `State`, or `ReadableState` |
| `name` | `string` (optional) | Debug name for the state, used in devtools and error messages |
Returns `State`.
Common use case: normalize patch props so callers can pass either a plain value or a reactive state.
```ts
const openState = toState(props.open ?? false)
```
---
## `merge(source, target)`
Deep-merges `target` into `source` using Domphy's composition rules.
```ts
const base = { class: "card", style: { padding: "1rem" } }
merge(base, { class: "active", style: { color: "red" } })
// base is now:
// { class: "card active", style: { padding: "1rem", color: "red" } }
```
| Parameter | Type | Description |
|---|---|---|
| `source` | `Record` | Object to mutate |
| `target` | `Record` | Values to merge into `source` |
Returns the same `source` object after merge.
Key merge behaviors:
- Plain objects are merged deeply.
- `class`, `transform`, `rel` and similar fields are space-joined.
- `animation`, `transition`, `boxShadow` and similar fields are comma-joined.
- Event handlers like `onClick` are chained.
- Hooks like `_onMount` are chained.
- Most other keys are overwritten by `target`.
Use `merge()` when composing patches or mutating a raw element in `_onSchedule`.
---
## `hashString(str?)`
Generates a deterministic string hash. The result always starts with a lowercase letter, so it is safe to use as a CSS identifier.
```ts
hashString("hello") // e.g. "b4a2f1c3"
hashString("hello") // same input, same output
```
| Parameter | Type | Default | Description |
|---|---|---|---|
| `str` | `string` | `""` | Input string to hash |
Returns a `string`.
Primary use case: generate a stable animation name from keyframes.
```ts
const keyframes = { to: { transform: "rotate(360deg)" } }
const animationName = hashString(JSON.stringify(keyframes))
const style = {
animation: `${animationName} 0.7s linear infinite`,
[`@keyframes ${animationName}`]: keyframes,
}
```
Do not use `hashString()` to generate ids for Domphy nodes. `ElementNode` already exposes `node.nodeId`, which is the runtime-scoped unique id used by the framework.
Notes:
- Deterministic: identical input always produces identical output.
- CSS-safe: output always starts with a letter.
- Not cryptographic: use it for IDs and CSS names, not security.
---
## `configure(options)`
Set global runtime options. Call once before mounting your app.
```ts
import { configure } from "@domphy/core"
configure({ cspNonce: "abc123" })
```
| Option | Type | Description |
|---|---|---|
| `cspNonce` | `string` | Nonce stamped on every `
${node.generateHTML()}