NovAPR Technologies

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.

Simulated network

Applies to every field on this page. Break the connection, or make the server slow, and watch the states change.

  • 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

How the Input behaves in each state
StateWhenSurfaceSays
IdleEmpty, or just editedPlain obsidianThe helper text, if any
PendingThe check takes longer than 200 msLiquid floods from the caretChecking…
SlowLonger than 1.8x this user's usual waitLiquid stalls and heavesStill checking. The connection is slow.
QueuedOfflineLiquid pools at the foot, motionlessCan't check this offline. We'll check when you're back online.
Confirmed, then settledThe check passesSets into obsidian, with an ember flashLooks good
FailedThe check failsLiquid drains and scattersThe validator's own message
DisabledNot available yetMatte obsidianAlways the reason

Accessibility

  • A real <label for>. The message is linked with aria-describedby.
  • aria-invalid follows failure and conflict. aria-busy follows 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.