forms · primitive

Inline validation

Live validation patterns — masked inputs, character counters, async availability checks. These compose plain UInput + a Vue ref + a small handler. No new component to ship; the patterns are the shipped primitive.

live counter

Character remaining

238 characters remaining

masked input

Phone number

Type digits; mask formats automatically.
LET-NUM format, e.g. GM-123456.

Bound: phone=(empty) · grantId=(empty)

async availability

"This email is taken"

Try "taken@example.org" to see the conflict state.

notes

Why no Tux wrapper

Each of these is ten lines of Vue. Wrapping them in a single component would obscure intent and lock the host app into one masking convention. The patterns here transfer cleanly to any consumer (Landscape admin, tti-ai-studio settings, sponsor-portal forms) — copy the handler, point it at the right ref.