DOM annotations
The four data-uidex* attributes and the rules for applying them.
Four attributes. All use kebab-case ids. DOM attributes are the sole surface for multi-per-file kinds (elements, regions, widget roots).
| Attribute | Purpose | Example |
|---|---|---|
data-uidex="<id>" | Register an element | <button data-uidex="submit-btn"> |
data-uidex-region="<id>" | Register a region, or override a landmark's id | <section data-uidex-region="toolbar"> |
data-uidex-widget="<id>" | Mark a composite widget's DOM root (must be mirrored by export const uidex = { widget: "<id>" } in the component module) | <div data-uidex-widget="video-player"> |
data-uidex-primitive="<name>" | Register a primitive, or override a convention name | <button data-uidex-primitive="button" {...props}> |
Only these four data-uidex* attributes are recognised. Other data-uidex-*
attributes are ignored (no warning — this lets teams use adjacent attributes for
their own tooling without collision).
data-uidex — every interactive element
<button data-uidex="save-btn">Save</button>
<input data-uidex="email-field" />
<a data-uidex="nav-home" href="/">Home</a>uidex scan --audit flags unannotated <button>, <a>, <input>, <select>,
<textarea> as INFO diagnostics. Rename carefully — ids are referenced by flow
tests.
data-uidex-region — structural regions
HTML5 landmarks (<header>, <nav>, <main>, <aside>, <footer>,
role="region") auto-register as regions. Only add data-uidex-region on
non-semantic <div> wrappers you want in the registry:
<div data-uidex-region="settings-form">
<button data-uidex="save-btn">Save</button>
</div>data-uidex-widget — composite behavioural units
Place on the root of a self-contained UI unit with internal state (video player,
date picker, rich text editor). The DOM attribute is the runtime boundary;
acceptance criteria live on the export const uidex at the component
definition.
import type { Uidex } from "@/uidex.gen"
export const uidex = {
widget: "video-player",
acceptance: [
"Plays/pauses on Space",
"Scrubber updates as video plays",
"M toggles mute",
],
} as const satisfies Uidex.Widget
export function VideoPlayer() {
return (
<div data-uidex-widget="video-player">
<button data-uidex="play">Play</button>
<input data-uidex="scrubber" type="range" />
</div>
)
}Widget id duplication rule. The id must appear in both places — on
data-uidex-widget="..." at the DOM root (runtime boundary) and on
export const uidex = { widget: "..." } (scan-time metadata). The scanner
cross-validates: a mismatch or one-sided presence is an error.
Child data-uidex elements are automatically composed onto the widget (via DOM
nesting at scan time).
data-uidex-primitive — reusable component definitions
Any .tsx under src/components/ui/** is auto-promoted to a primitive (name =
kebab of filename). Add data-uidex-primitive="name" only to opt-in a reusable
component that lives outside the convention directory.
// src/components/ui/button.tsx — auto-promoted as 'button', no attribute needed
export function Button({ children, ...props }) {
return <button {...props}>{children}</button>
}