Components · v0.1
Built from the state language.
Every component takes its states, copy, sounds and haptics from the spec, and works with no JavaScript and no WebGL. The Input comes first: a field that tells you honestly what it is doing.
Input
A field that validates in the open.
Type a meal name. Names are checked against a server, so a slow answer turns the field to liquid and a confirmed one sets into obsidian. Try Oats to see a failure.
- Type, then pause. Nothing is checked until you stop typing.
- Edit a confirmed field. It returns to plain obsidian at once, because the old confirmation no longer applies.
- Go offline, then type. The check is queued and resumes when you reconnect.
- Slow is relative. After a few slow answers, "still checking" waits longer: it adapts to the connection you actually have.
Using the Input
Real markup first. mountInput then adds the surface, the states and the announcements.
Markup
<div class="lava-field" id="fName">
<label for="mealName">Meal name</label>
<div class="lava-well">
<input id="mealName" name="mealName"
autocomplete="off" required>
</div>
<p class="lava-msg" role="status"></p>
</div>
Script
const name = mountInput(fName, {
ctx: stage.ctx, // or omit for the CSS-only tier
shader: true,
network: true, // offline queues the check
required: true,
validate: async (value) =>
(await isTaken(value))
? { ok: false,
message: "That name's already used. Try another." }
: { ok: true },
})
What the person sees and hears
| State | When | Surface | Says |
|---|---|---|---|
| Idle | Empty, or just edited | Plain obsidian | The helper text, if any |
| Pending | The check takes longer than 200 ms | Liquid floods from the caret | Checking… |
| Slow | Longer than 1.8x this user's usual wait | Liquid stalls and heaves | Still checking. The connection is slow. |
| Queued | Offline | Liquid pools at the foot, motionless | Can't check this offline. We'll check when you're back online. |
| Confirmed, then settled | The check passes | Sets into obsidian, with an ember flash | Looks good |
| Failed | The check fails | Liquid drains and scatters | The validator's own message |
| Disabled | Not available yet | Matte obsidian | Always the reason |
Accessibility
- A real
<label for>. The message is linked witharia-describedby. aria-invalidfollows failure and conflict.aria-busyfollows pending and slow.- Changes are announced in a
role="status"region: polite while working, assertive for failures. - Never color or motion alone: every state has words, and failures are also brighter and heavier.
- A visible focus ring (2px, high contrast) on every field, in both tiers.
- Without WebGL, on touch, or with reduced motion, the CSS-only tier runs the same states.