Input
A labelled, accessible text field with v-model. State is expressed through real attributes — the
native disabled, aria-invalid when there is an error, and an aria-describedby that points at
the rendered hint or error. The label is wired to the field with for/id (auto-generated via
useId when you don't pass one), and arbitrary native attributes (name, autocomplete,
inputmode, …) fall through to the underlying <input>.
This page is laid out by layer. The live demos are the @oriui/css layer — their code
defaults to HTML (the standalone classes, also your htmx / Astro / Svelte / plain-HTML usage),
with Vue one tab away; the Framework API documents the
@oriui/vue component. Input is pure CSS — there is no behaviour layer in between.
Classes
A field is a block wrapper with single-class token utilities — one class repoints one token; no
separate base class is needed. The ori-color_* accent drives the focus ring; the idle border is a
neutral, theme-aware blend. The Vue props in Framework API map 1:1 to these.
| Class | Type | Description |
|---|---|---|
ori-input | Block | Required base class (wrapper div). |
ori-input_* | Style | outline · fill |
ori-color_* | Color | primary · secondary · success · warn · danger · info · surface · background (focus ring accent) |
ori-input_* (size) | Size | xs · sm · md · lg · xl · xxl field height (size sugar on the wrapper) |
ori-size-radius_* | Radius | zero · xs · sm · md · lg · xl · rounded (field corners) |
ori-font-size_* | Font | xs · sm · md · lg · xl · xxl (label + field text scale) |
ori-input__label · ori-input__required · ori-input__field · ori-input__hint · ori-input__error | Part | label / required-asterisk / input / helper / error elements |
ori-input_fluid | Layout | full-width (stretches wrapper to 100 %) |
disabled · aria-invalid · aria-describedby | State | real attributes, not classes |
Variants
Two visual styles — outline (default, border visible at rest) and fill (tinted background, no
border).
<div class="ori-input ori-color_primary ori-font-size_md ori-input_outline">
<label class="ori-input__label" for="f1">Outline</label>
<input id="f1" placeholder="Type here" class="ori-input__field ori-size-radius_md" />
</div>
<!-- swap the variant: ori-input_outline → ori-input_fill -->
Colors
The ori-color_* class controls the focus ring (and the danger color inherits for error text).
<div class="ori-input ori-color_danger ori-font-size_md ori-input_outline">
<label class="ori-input__label" for="f2">danger</label>
<input id="f2" class="ori-input__field ori-size-radius_md" />
</div>
Sizes
xs → xxl. The size drives both the field height (ori-input_* size sugar on the wrapper) and
the text scale (ori-font-size_*).
<!-- size sugar on the wrapper (ori-input_xl); the field inherits the height via --ori-size-action -->
<div class="ori-input ori-color_primary ori-font-size_xl ori-input_outline ori-input_xl">
<label class="ori-input__label" for="f3">xl</label>
<input id="f3" class="ori-input__field ori-size-radius_md" />
</div>
<!-- a bare ori-input wrapper is already a valid md field — no size class needed for the default -->
Radius
From zero (square) through the default md to rounded (pill-shaped field).
<input class="ori-input__field ori-size-radius_zero" />
Label, hint & required
label renders a <label> linked to the field. hint renders a helper line below. required
adds the real HTML attribute and a visual asterisk (hidden from assistive tech).
We never share it.
<div class="ori-input ori-color_primary ori-font-size_md ori-input_outline">
<label class="ori-input__label" for="email">
Email<span class="ori-input__required" aria-hidden="true">*</span>
</label>
<input
id="email"
type="email"
required
placeholder="you@example.com"
aria-describedby="email-hint"
class="ori-input__field ori-size-radius_md"
/>
<p id="email-hint" class="ori-input__hint">We never share it.</p>
</div>
States
disabled uses the real attribute. error renders a role="alert" message and sets
aria-invalid. invalid flips aria-invalid without a message (for external validation).
Enter a valid email address.
<!-- disabled -->
<input class="ori-input__field …" disabled />
<!-- invalid without message -->
<input class="ori-input__field …" aria-invalid="true" />
<!-- error: message gets role=alert and is referenced by aria-describedby -->
<input class="ori-input__field …" aria-invalid="true" aria-describedby="email-error" />
<p id="email-error" class="ori-input__error" role="alert">Enter a valid email address.</p>
Fluid
fluid stretches the wrapper to the full width of its container.
<div class="ori-input ori-input_fluid …">…</div>
Input types
type maps directly to the native <input type=""> — use email, password, search, tel,
url, number, etc.
<input type="password" class="ori-input__field …" /> <input type="search" class="ori-input__field …" />
Common patterns
A real-world sign-in form — label, type, hint, required, and error compose freely.
Use a mix of letters, numbers and symbols.
<form style="display: flex; flex-direction: column; gap: 1rem; max-width: 24rem">
<div class="ori-input ori-color_primary ori-font-size_md ori-input_outline">
<label class="ori-input__label" for="si-email">
Email<span class="ori-input__required" aria-hidden="true">*</span>
</label>
<input
id="si-email"
type="email"
required
placeholder="you@example.com"
class="ori-input__field ori-size-radius_md"
/>
</div>
<div class="ori-input ori-color_primary ori-font-size_md ori-input_outline">
<label class="ori-input__label" for="si-pw">
Password<span class="ori-input__required" aria-hidden="true">*</span>
</label>
<input
id="si-pw"
type="password"
required
placeholder="Min 8 characters"
aria-describedby="si-pw-hint"
class="ori-input__field ori-size-radius_md"
/>
<p id="si-pw-hint" class="ori-input__hint">Use a mix of letters, numbers and symbols.</p>
</div>
</form>
Accessibility
The accessibility contract holds across every layer — the standalone classes and the Vue component render the same attributes and ARIA wiring.
labelis associated with the field viafor/id; the id is auto-generated (useId) when you don't pass one, so the association holds even without an explicitidprop.hintanderrorare wired througharia-describedby, referencing only the element that is actually rendered (errorsupersedeshint). Pass extra ids withdescribedbyto reference additional descriptions (e.g. a shared form note).errorsetsaria-invalid="true"and announces viarole="alert";invalidflipsaria-invalidon its own for external validation flows.- Uses the real
disabledattribute and nativerequired; the focus ring is always visible and switches to thedangercolor when invalid. - All native attributes (
name,autocomplete,inputmode,maxlength, …) fall through to the underlying<input>— OriInput stays out of the way.
| Key | Action |
|---|---|
Tab | Moves focus to / away from the field. |
Framework API
The props, events, and slots of the Vue component. The standalone CSS layer has no component API — its surface is the classes above. (Svelte bindings are planned.)
Props
| Prop | Type | Default | Description |
|---|---|---|---|
color | ThemeColor | 'primary' | Accent color for focus ring: primary · secondary · success · warn · danger · info · surface · background. |
describedby | string | — | Extra element id(s) appended to aria-describedby (e.g. a shared form note). |
disabled | boolean | false | Real disabled attribute; blocks interaction and dims the field. |
error | string | — | Error message rendered below the field (role="alert"); also sets aria-invalid="true". |
fluid | boolean | false | Full-width — stretches wrapper to 100 % of its container. |
hint | string | — | Helper text below the field; hidden while error is shown. |
id | string | — | Explicit id for the <input>; auto-generated via useId when omitted. |
invalid | boolean | false | Sets aria-invalid="true" without rendering an error message (for external validation). |
label | string | — | Visible <label> text, wired to the field via for/id. |
placeholder | string | — | Native placeholder text. |
radius | RadiusSize | 'md' | Corner radius of the field (zero · xs · sm · md · lg · xl · rounded). |
required | boolean | false | Native required attribute; also renders a visual asterisk (aria-hidden). |
size | ActionSize | 'md' | Field height + text scale (xs · sm · md · lg · xl · xxl). |
type | string | 'text' | Native input type (text, email, password, search, tel, url, number, …). |
variant | 'fill' | 'outline' | 'outline' | Visual style: outline (border) or fill (tinted background). |
Events & attributes
v-model binds to a string via defineModel<string>() — it reads the modelValue prop and emits
update:modelValue on input, so you use it like any controlled field:
<OriInput v-model="myValue" label="Name" />
OriInput sets inheritAttrs: false and spreads $attrs onto the underlying <input>, so native
attributes and listeners pass through directly to the field element — not the wrapper <div>:
- Attributes:
name,autocomplete,inputmode,maxlength,minlength,pattern,spellcheck,readonly, … - Listeners:
@input,@change,@focus,@blur,@keydown, …
Slots
OriInput exposes no named slots.
| Slot | Description |
|---|---|
| (none) | No slots are defined. |