SwInput
SwInput is a text input: a thin wrapper over the native <input> whose value is the unified baseline box, not extra features — it shares the box model of the SwSelect trigger (--control-height / --control-font-size / --input border / --radius), so SwInput / SwSelect / SwSegmented align to the same height and font size by default when mixed in one row, and appliers no longer hand-roll input box styles.
Undeclared native attributes (placeholder / maxlength / autocomplete / name / readonly …) and native event listeners automatically land on the inner <input> (inheritAttrs: false + v-bind="$attrs") — the root is a .sw-input wrapper (carrying the box styles and trailing icon buttons); passthrough behavior is unchanged for callers.
iOS focus-zoom counter-measure: iOS Safari auto-zooms the page when a text input with font-size < 16px gets focused. On touch devices (
hover: none+pointer: coarse) SwInput bumps its default font size back to1remwhile the height stays 32px; an explicit--sw-input-font-sizeoverride applies on both ends.
Basic usage
v-model + native attribute fallthrough
<script setup lang="ts">
const keyword = ref('')
</script>
<template>
<SwInput v-model="keyword" placeholder="Search keyword" maxlength="50" />
</template>
With SwFormField (error state)
Error copy is not SwInput's job — pair with SwFormField for inline messages; SwInput's error only handles the destructive border + aria-invalid:
SwFormField simple mode + error border / disabled
<SwFormField label="Email" required :error="emailError">
<SwInput v-model="email" type="email" :error="!!emailError" />
</SwFormField>
vee-validate mode injects via v-bind="field":
<SwFormField name="email" label="Email" v-slot="{ field, error }">
<SwInput v-bind="field" type="email" :error="!!error" />
</SwFormField>
Clear button & password visibility
clearable shows a trailing clear button when the value is non-empty (clears + refocuses the input + emits clear); revealable only takes effect with type="password" and toggles plain/cipher text (silently ignored for other types). Enabling either reserves trailing padding permanently (no text jumping), with built-in touch hit area (sw-hit-area) and localized aria-labels:
clearable + revealable password toggle
<SwInput v-model="keyword" clearable placeholder="Search" @clear="onCleared" />
<SwInput v-model="password" type="password" clearable revealable />
Aligned form-control row
SwInput + SwSelect + SwSegmented aligned by default
Props
| Name | Type | Required | Default | Notes |
|---|---|---|---|---|
modelValue (v-model) | string | number | '' | Controlled value; emits string on input (type="number" also emits string — convert in app code) | |
type | 'text' | 'password' | 'search' | 'email' | 'tel' | 'url' | 'number' | 'text' | Native type | |
disabled | boolean | false | Disabled state | |
error | boolean | false | Error state (destructive border + aria-invalid), a boolean (matching Vuetify); pair error copy with SwFormField — whose error is an error-message string, so wire them as :error="!!field.error" | |
clearable | boolean | false | Trailing clear button: shown when non-empty and not disabled; click = clear + emit update:modelValue('') + emit clear + refocus | |
revealable | boolean | false | Password visibility toggle, only with type="password" (silently ignored otherwise + dev warn); toggles internal display only, type prop unchanged |
All other native attributes fall through — no component declaration needed.
Events
| Name | Payload | Notes |
|---|---|---|
update:modelValue | value: string | Fired on input |
clear | — | Emitted when the clear button is clicked (after update:modelValue('')) |
Native events (focus / blur / change …) can be listened to directly via fallthrough.
Styling Tokens
| Token | Default | Applies to |
|---|---|---|
--sw-input-min-height | var(--control-height) | min-height |
--sw-input-padding | 0.25rem 0.75rem | padding |
--sw-input-border | var(--input) | border color |
--sw-input-radius | var(--radius) | border radius |
--sw-input-bg | var(--background) | background |
--sw-input-color | var(--foreground) | text color |
--sw-input-font-size | var(--control-font-size) (touch devices: 1rem) | font size |
--sw-input-error-border | var(--destructive) | error border color |
--sw-input-placeholder-color | var(--muted-foreground) | Placeholder text color |
Default height / font size come from the form-control semantic tokens
--control-height(32px) /--control-font-size(14px) — override these two tokens to scale every control site-wide (component-level tokens still take precedence).
Caveats
- Native DOM / imperative focus: the root is a wrapper (
$elis no longer the<input>) — use the exposedfocus()/blur()/select():inputRef.value?.focus(). - Error copy is external:
erroronly styles the border; render the message via SwFormField'serrorprop / vee-validate. - Not for multi-line text: this wraps a single-line
<input>only; use a native<textarea>for now (SwTextareais shape-reserved, pending a real consumer). - Aligning submit buttons in a form: SwButton stays unstyled (does not consume
--control-height); give form buttonsheight: var(--control-height)in app code.