Writing a Custom Primitive Prototype
Understand the minimal shape of direct and authored-asHook entries inside a governed or candidate boundary.
A leaf Prototype represents a protocol subject with a clear boundary and an independent information-flow responsibility. Button and Toggle are two relatively clear current examples.
This guide explains authoring structure. Existing governed subjects proceed directly from their current P/T graph. For a genuinely new Base subject, research, a candidate graph, draft entities, implementation probes, and tests may proceed immediately; the one unresolved semantic decision is whether Proto UI admits that independent subject as a Base identity. Use Implementing a Governed Base Semantic Slice to carry either path through coherent delivery.
Start from entities and evidence
Section titled “Start from entities and evidence”Do not begin by copying source. For Button, read in this order:
- lifecycle, criteria, relations, and sources in
P-BASE-BUTTON; - cases and executable mappings in
T-BASE-BUTTON-0001; - packages/prototypes/base/src/button/button.proto.ts;
packages/prototypes/base/test/as-button.test.tsand applicable Adapter evidence; and- package exports, CLI, documentation, and demos.
P-BASE-BUTTON is currently draft. Source is implementation evidence, not authority above the applicable entity.
What base-button demonstrates
Section titled “What base-button demonstrates”Button has two official authoring entries:
- the
base-buttondirect Prototype; and - the
asButtonauthored asHook.
They share setupButton(def) instead of maintaining two versions of Button semantics. That arrangement realizes P-BASE-BUTTON-AUTHORING-ENTRIES; it is not a fixed template that every Prototype must copy.
The current implementation includes:
def.props.define()fordisabled;def.state.bool()for states such asdisabled,hovered, andpressed;asFocusable()forfocused,focusVisible, and the focus method;def.event.on()for pointer routes andpress.commit;def.expose.state(),def.expose.method(), anddef.expose.event()for outward surfaces; andasAccessible()for Button role, name, state, and action.
The former def.state.fromInteraction() example no longer describes the current Button implementation and must not be used as the example for this guide.
The boundary between def and run
Section titled “The boundary between def and run”def declares the setup-time plan: props, state, events, exposes, accessibility, rules, and lifecycle hooks. run appears inside runtime callbacks and provides access to current props, context, lifecycle, and outward effects.
For example:
def.event.on('press.commit', (run) => { if (disabled.get()) return; run.expose.emit('click');});The event route is registered during setup; the outward signal is emitted through run when the event occurs.
When to provide an authored asHook
Section titled “When to provide an authored asHook”Do not treat “exports a Prototype but no asHook” as a universal error. Ask:
- Does the applicable P catalog direct and authored-asHook forms as two entries of one protocol?
- Should both entries share the complete protocol surface and implementation?
- Does the hook serve only its owning protocol rather than becoming cross-Prototype substrate?
- Would the entry introduce ungoverned options, merge, or configure semantics?
D-PROTOTYPE-ENTITY-NAMING-0001 requires existing entries for one protocol to be cataloged in the same P entity; it does not require every direct Prototype to generate an asHook. D-AS-HOOK-CONFIGURABLE-AUTHORED-0001 also keeps ordinary configurable authored asHooks in governed future design space.
What completes a leaf slice
Section titled “What completes a leaf slice”A source file is only one part of delivery. A governed leaf or clearly marked candidate normally follows:
governed identity or candidate draft→ P criteria and relations→ T cases and executable tests→ implementation and public exports→ CLI facade generation→ bilingual docs and real public-package demo→ applicable WC / React / Vue evidenceThe three current Adapter previews verify one Web host profile; they do not automatically prove multi-host conformance.
When to pause
Section titled “When to pause”If implementation needs a new public prop/event/state, changes ownership, requires a raw host object, or exposes a contradiction between P/T and implementation, update the candidate or governed graph, tests, and evidence together instead of widening source alone. Continue the reversible work through fresh review. Escalate only when the evidence requires admission of a different new Base identity; keep that identity draft while the admission choice is unresolved.
- For a compound family, read Writing a Compound Prototype
- For a design-language projection, read Building a Styled Library on Top of Base
- Before opening a pull request, use the Prototype Author Checklist