HTML component implementation
Read the relevant core contract, nearest HTML element and tests, definition entry, and UI design record before choosing a pattern. Use packages/html/src/ui/play-button/ for a simple control and packages/html/src/ui/slider/ for a compound control. Read packages/element/README.md when changing reactive properties, lifecycle, or controllers.
Workflow
- Classify the request as a reusable primitive or app/skin composition before defining its contract. Then define the state owner, semantics, APIs, optional parts, styling contract, and opt-outs.
- Put runtime-neutral state and actions in
packages/core/src/core/ui/; put reusable DOM interaction inpackages/core/src/dom/ui/; keep the element an adapter. - Match the nearest
UIElementorMediaButtonElementpattern. Send properties down and semantic events up; use controllers or context only across a real ownership boundary. - Let
ReactiveElementown rendered DOM updates: derive changes from reactive properties and controller state, then apply them through its update lifecycle. Avoid out-of-band mutations so scheduling,updateComplete, tests, and integrations remain authoritative. - Add markup for semantics, focus, content, or parts consumers must place, omit, or replace. Leave purely visual structure to skins, CSS, or pseudo-elements. If the API starts coordinating independent behavior, extract another element or controller instead of adding modes and options.
- Expose discrete state through mapped
data-*attributes and dynamic layout values through stable CSS custom properties. Define each token's owner, units, and fallback. Treat descendant selectors,:has(), and other complex selectors introduced by composition as a likely sign that the primitive boundary or styling hooks need improvement. - Keep the element class free of registration side effects. Register it with
safeDefineunderpackages/html/src/define/ui/, updateHTMLElementTagNameMap, and preserve narrow import andsideEffectsboundaries. - Clean up subscriptions, observers, listeners, locks, and async work across disconnect and destroy. Add focused core and HTML tests; verify interaction in a browser when semantics, focus, or pointer behavior changes.
Check packages/html/package.json and packages/html/vite.config.ts when adding an entrypoint or registration module. Run the narrow package tests and build; build changed exported types before repository typechecking.
Example
Input: “Create the HTML chapter-skip control.”
Output: A scoped core contract, thin custom element, explicit definition entry, headless styling hooks, cleanup, and focused tests.


