Input
An enhanced base input built on the native <input> element.
Basic Usage
Basic usage
Accessible Name (label)
label (accessible name)
label serves as the accessible name (aria-label) source for the input, announced by screen readers. When label is not set, it falls back to placeholder → built-in text "Input"; once set (e.g. "Login email"), it overrides the fallback chain.
Native Form & Label Association
form-associated: label for / FormData / reset
The component is a form-associated custom element (formAssociated: true): <label for> works natively (clicking the label focuses the real input inside the shadow root, and screen readers announce the label text); inside a native <form> the value is collected via standard FormData (submitted only when name is set), form.reset() restores the initial value, and a parent fieldset[disabled] disables it. Works in parallel with oas-form's collectFields mechanism.
Reading/writing the current value is done via the public value property (matching native input.value): el.value reads the parser/mask-aware submitted-value semantics (same as oas-input/oas-change/FormData); el.value = x writes the controlled value attribute and immediately syncs the inner control, dispatching no events. Controlled assignment is immediate: even a same-value assignment force-writes the inner control; frameworks like Vue re-write an element's :value binding unconditionally on every patch, so under a controlled binding sync external state via events (oas-input/oas-change) to avoid an unrelated re-render wiping uncommitted input.
Types
type
type is passed through to the native input type, supporting text / password / number / email, etc.
Password Visibility Toggle
show-password
With show-password and type="password", an eye button is rendered on the right side of the input; clicking toggles between plaintext and masked text. The button has an aria-label (locale text), aria-pressed, and a focus ring when focused.
Clearable
clearable
When there is content and clearable is set, a clear button is shown; clicking clears the value, refocuses, and dispatches oas-clear.
Loading
loading (input stays editable)
With loading set, a spinner shows at the end of the field and aria-busy="true" is mirrored: the input is not disabled (users can keep typing — useful for remote validation / suggestion requests). Together with clearable, the spinner takes precedence and the clear button yields; exiting loading restores it.
Disabled & Readonly
disabled / readonly
Addons
addon-before / addon-after
addon-before / addon-after render external addon text blocks (e.g. units, domains) outside the input, going through dedicated ::part(prepend) / ::part(append) parts; addons are greyed out when disabled.
Icons
prefix-icon / suffix-icon
prefix-icon / suffix-icon accept icon names (from @oas-ui/icons built-in icon set) and inline-render decorative SVG icons.
Inline Affixes & Clear Together
prefix / suffix + clearable
prefix-text / suffix-text are inline text inside the input and can coexist with clearable, icons, and addons without conflicts. prefix / suffix are compatibility aliases (writable through both the attribute and property channels, including React / Vue bindings) — the canonical names are prefix-text / suffix-text.
Custom Clear Icon (clear-icon slot)
clear-icon slot
The clear button of clearable renders a built-in close icon by default; distribute a custom icon via slot="clear-icon" to personalize it (the slot natively replaces the built-in fallback when it has content; the icon size is defined by the distributed content).
Character Count
show-count + maxlength
show-count displays a character counter at the bottom-right of the input: with maxlength it shows current/maxlength, without it just the current length; maxlength is also passed through to the native input to limit input length. When the limit is exceeded, the counter number turns danger-colored.
Hint Text (hint)
hint static hint (independent of validation errors)
hint renders a persistent static hint below the input (font size --oas-font-size-sm, secondary color) and is associated with the inner input via aria-describedby (screen readers announce it). It is independent of validation errors (status / aria-invalid): the hint stays visible when an error appears. Click the button to toggle status="error" and see the error border coexist with the hint text.
Enter Submit Event
oas-enter
Pressing Enter while typing (not an IME composition commit) dispatches oas-enter with detail: { value }.
Events
Input and clear events
Listen to oas-input (while typing) and oas-clear (on clear):
Complex Affixes (slot distribution)
slot=prefix / slot=suffix
Use the prefix-text / suffix-text attributes for simple text; for complex content (icons/buttons/badges etc.) distribute via the same-named slot="prefix" / slot="suffix" slots — distributed content natively replaces the attribute text. prefix / suffix are compatibility aliases (canonical: prefix-text / suffix-text), usable through both the attribute and property channels (React / Vue bindings included).
Sizes
size
size supports small / medium (default) / large, with heights aligned to the global size tokens (--oas-control-height-sm/md/lg, 24/32/40px). Font size, inline affixes, addons, and the clear/eye buttons scale together. When not set, it inherits the size injected by the nearest oas-config-provider (global form density).
Variants
variant
variant supports outlined (default) / filled (filled background, border appears on focus) / borderless (no border or background, for plain-text embedding scenarios).
Validation Status
status
status supports error / warning / success: the border and focus ring switch to the semantic color. error also syncs aria-invalid="true" onto the inner input (announced as invalid by screen readers); warning / success are visual-only without invalid semantics. The existing channel where form validation (oas-form) writes host-level aria-invalid still works and composes with status (error takes priority).
Native Attribute Passthrough
Native attribute passthrough
The following native attributes are mirrored onto the inner native input (and removed in sync when the host attribute is removed): name / autocomplete / autofocus / inputmode / minlength / min / max / step / required / spellcheck / enterkeyhint / pattern. Useful for native form semantics, autofill, and mobile keyboard hints (inputmode / enterkeyhint); min / max / step are for type="number" (natively ignored by other types). autofocus is forwarded by the component as a one-time focus after mount (native autofocus does not pierce the shadow DOM).
Focus & Commit Events
oas-focus / oas-blur / oas-change + select()
oas-focus/oas-blur: dispatched when the inner input gains/loses focus,detail: { value }carries the current value.oas-change: commit semantics — dispatched on blur or Enter only when the value changed since the last commit (not on every keystroke, unlikeoas-input).select()method: selects all content (delegates to the native input.select);blur()delegates likewise.
Complex Addons (slot distribution)
slot=prepend / slot=append (text / icons / buttons)
The addon-before / addon-after attributes accept text only; for complex content distribute via slot="prepend" / slot="append". Plain text and icons render on the gray addon surface (radius merging / border dedup work the same as with the addon attributes, and distributed content natively replaces the attribute fallback across the two coexisting channels). When a self-contained control (oas-button / oas-button-group / oas-compact) is distributed, the addon degrades into a flush container: it drops the text addon's gray fill, border and horizontal padding, aligns its height with the input, overlaps the shared edge by -1px into a single line, and forwards the radius merging protocol (--oas-button-group-radius) to the control so its outer corner joins the input frame into one continuous radius. Bordered compound controls such as selectors are still not recommended in addon slots: the host cannot safely strip their internal styles across the Shadow boundary (their dropdown shares color tokens with the trigger — making them transparent would also transparentize the dropdown). To compose a selector with an input, use the oas-compact container:
select + input composition (oas-compact)
Block Addons (block-start / block-end)
slot=block-start / slot=block-end (full-width block above/below the field)
slot="block-start" / slot="block-end" render a full-width block row above / below the field — a different dimension from prepend / append (which join horizontally at either side). Block rows occupy their own line and never participate in border joining; they can host hint text, action buttons, links, or any other content. The row container uses --oas-font-size-sm in secondary text color (grayed out when disabled), and is deep-customizable via ::part(block-start) / ::part(block-end). When combined with show-count (the outside counter also lives below the field), the counter automatically moves into the block-end row, aligned to the end edge — no overlap.
Count Position & Grapheme Counting
count-position + emoji grapheme counting
show-count counts by grapheme clusters (built-in Intl.Segmenter, falling back to code points when unavailable) — emoji and ZWJ sequences (like the family emoji) count as 1, avoiding the JS length behavior that counts one emoji as multiple. count-position="inside" moves the counter to the right inside the input (default outside, bottom-right). Note: native maxlength truncation still uses the UTF-16 code-unit metric — counting and truncation are separate concerns (see allow-over-max below for over-limit scenarios).
Allow Over Max
allow-over-max + oas-validate
By default maxlength is passed through to the native hard truncation; with allow-over-max, input beyond the limit is no longer truncated — the counter turns danger-red and oas-validate is dispatched whenever the over-limit state flips (detail.error is exceed-maximum, or null when back within the limit), so hosts can warn without interrupting typing.
Display Formatting (formatter / parser)
formatter / parser (thousand separator, property channel)
formatter / parser are JS property-channel only (el.formatter = fn) — Web Components attributes cannot carry functions, so there is no corresponding HTML attribute. Display = formatter(rawValue); detail.value of oas-input / oas-change carries the parsed raw value (parser(display)). Removing them (set to null) restores the raw display. While typing, the cursor is approximately preserved by shifting with the length delta of the format change (sufficient for card-number grouping and similar cases).
Input Mask (mask)
mask input mask (###-####) and mask-raw raw value
mask defines an input mask template: # digit, A letter, * alphanumeric; any other character is a literal (like the - in ###-####). Typing skips literals automatically, invalid characters are filtered, pasting accepts only the fitting positions, and backspace stops at literals (literals are managed by the template and can never be deleted through); the caret always stays at the correct position. By default events and the form submit value carry the display value (including literals); with mask-raw set they carry the de-formatted raw character sequence instead (FormData submits the same). mask is mutually exclusive with formatter/parser — when both are set, mask wins with a warning; maxlength is not passed through in mask mode (native truncation would break the mask). (The show-count counter follows the submission channel: the display value with literals by default, the raw sequence with mask-raw.)
Clear Button Visibility
show-clear-on
show-clear-on controls the clear-button visibility strategy: default always (shown whenever there is a value, unchanged behavior); focus shows only while focused; hover shows while hovering or focused (reduces visual noise in dense forms).
Auto Width
auto-width
With auto-width, the input width follows its content (mirror measurement: a hidden element sharing the input's font and paddings; empty value falls back to the placeholder width). auto-width-min / auto-width-max (px numbers) clamp the range — min defaults to 72px and max to 100%. Common in inline editing and tag-input scenarios.
Range Input (Composition)
Two inputs + separator (range input)
A range input is just two oas-input elements plus a separator layout (the component does not provide a pair API): each input commits its own oas-change, and the host combines them into a range. Wrap in an oas-compact container when you need tight radius-merged composition.
API
oas-input
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
addon-after | Addon text block after the input | — | — |
addon-before | Addon text block before the input | — | — |
allow-over-max | Over-limit without truncation: maxlength is no longer passed native (typing continues), count turns red and oas-validate fires | boolean | — |
auto-width | Auto width following content (mirror measurement; empty value falls back to placeholder width), usually with auto-width-min/max | boolean | — |
auto-width-max | Maximum width for auto-width, px or percentage (default 100%) | — | — |
auto-width-min | Minimum width in px for auto-width (default 72) | — | — |
autocomplete | Passes the autocomplete hint through to the inner native input (password managers / autofill) | — | — |
autofocus | Auto-focus the inner input after mount (native autofocus does not cross shadow DOM, so the component forwards it) | boolean | — |
clearable | Clearable | boolean | — |
count-position | Character count position: outside (default) / inside (right side within the field) | string | — |
disabled | Disabled | boolean | — |
enterkeyhint | Passes the mobile virtual keyboard enter-key appearance through (e.g. search/send/go) | — | — |
hint | Persistent static hint below the input (aria-describedby, independent of validation errors) | string | — |
inputmode | Passes the mobile virtual keyboard type through to the inner input | — | — |
label | Accessible name (aria-label source; falls back to placeholder → built-in "Input" when unset) | — | — |
loading | Loading state: trailing spinner with aria-busy (input stays editable); takes precedence over the clear button | boolean | — |
mask | Input mask template: # digit, A letter, * alphanumeric; other characters are literals (e.g. ###-####); typing skips literals, pasting accepts only fitting positions, backspace stops at literals; mutually exclusive with formatter/parser (mask wins) | string | — |
mask-raw | Events and form submit value carry the de-formatted raw character sequence; by default the display value is submitted | boolean | — |
max | Native max mirrored to the inner input | — | — |
maxlength | Maximum input length (passed through to native maxlength) | string | — |
min | Native min mirrored to the inner input (number type) | — | — |
minlength | Passes the native minimum length constraint through | — | — |
name | Native form field name (form-associated: the FormData submission key) | — | — |
pattern | Passes the native regex validation pattern through | — | — |
placeholder | Placeholder text | string | — |
prefix-icon | Icon name for the leading icon | string | — |
prefix-text | Inline text before the input value; prefix is a compatibility alias (usable via attribute and property channels) | string | — |
readonly | Readonly | boolean | — |
required | Required marker (native passthrough to the inner input; drives the valueMissing validation chain) | boolean | — |
show-clear-on | Clear button visibility: always (default) / hover / focus | string | — |
show-count | Show character count (bottom-right; danger when over limit) | boolean | — |
show-password | Password visibility toggle (renders an eye button when type="password") | boolean | — |
size | Size preset small / medium (default) / large: height and font scale | string | medium |
spellcheck | Passes the native spellcheck toggle through | — | — |
status | Validation status: error / warning / success; error mirrors aria-invalid on the inner input | string | — |
step | Native step mirrored to the inner input | — | — |
suffix-icon | Icon name for the trailing icon | string | — |
suffix-text | Inline text after the input value; suffix is a compatibility alias (usable via attribute and property channels) | string | — |
type | Native input type | string | text |
value | Value (controlled) | string | — |
variant | Variant: outlined (default) / filled / borderless | string | — |
Property (JS property; see each row for attribute reflection)
| Property | Description | Type | Default |
|---|---|---|---|
value | Current value: parser/mask-aware submitted-value semantics (el.value reads; el.value = x writes the controlled value and syncs the inner control, no event) | string | — |
Events
| Event | Description |
|---|---|
oas-blur | Fires on blur, detail: { value } |
oas-change | Commit semantics: fires on blur/Enter when the value differs from the last commit, detail: { value } |
oas-clear | Cleared by click, detail: { originalEvent } |
oas-enter | Enter pressed (not IME composition), detail: { value } |
oas-focus | Fires on focus, detail: { value } |
oas-input | While typing, detail: { value } |
oas-validate | Fires when the over-limit state flips (allow-over-max), detail: { error: "exceed-maximum" | null } |
Slots
| Name | Description |
|---|---|
append | Append addon area (may contain select/button or any content) |
block-end | Full-width block addon below the field (action/counter row, any content; the outside character counter moves into this row when both are present) |
block-start | Full-width block addon above the field (hint/note row, any content) |
clear-icon | Custom clear-button icon (replaces the built-in when present) |
prefix | Inline leading content (icons/buttons etc.; distributed content takes precedence over the prefix attribute text). For simple text use the prefix attribute |
prepend | Prepend addon area (may contain select/button or any content) |
suffix | Inline trailing content (icons/buttons etc.; distributed content takes precedence over the suffix attribute text). For simple text use the suffix attribute |
CSS Variables
| CSS Variable | Default |
|---|---|
--oas-button-group-radius | var(--oas-radius-md) |
--oas-input-auto-max | 100% |
--oas-input-auto-min | 72px |
--oas-input-measured | 100% |