Input
A text input component with focus and validation states.
Default
Types
Disabled
Sizes
Leading icon
Pass any node (we use Phosphor Icons) as
leadingIcon to render an icon inside the field. The input reserves
enough left padding to clear the glyph, which scales in em with the
field's font-size.
Prefix and suffix
prefix and suffix draw fixed text inside the field, in the same well as the
value, in the secondary text colour. They are not part of the value or the
selection, and the value never runs under them at any length. Clicking the
affix focuses the input. The text is wired to the input with
aria-describedby, so a screen reader reads it as a description of the field,
not as what was typed.
The field keeps its one edge, drawn from the shared --control-edge-* tokens
around the affix and the value together; there is no border between them. With
an affix, className lands on the wrapper that draws the well, and the native
HTML prefix attribute (not useful on an <input>) is not forwarded. Tune the
gap and ink with --input-affix-gap and --input-affix-color.
Invalid
Password managers
By default, Input opts out of password-manager autofill (1Password, Bitwarden,
LastPass) so contact, marketing, and settings forms don't render autofill icon
overlays. Browsers ignore autocomplete="off" on individual inputs, so Visor
emits the four per-manager data-* attributes (data-1p-ignore,
data-bwignore, data-lpignore, data-form-type="other") that each manager
respects.
On login, signup, or credential fields where you want the manager to offer
autofill, opt back in with passwordManagers="allow":
Inheriting from Form
For forms with multiple credential fields, set passwordManagers once on the
parent <Form> and every descendant Input inherits the value — no need to
repeat the prop on each field. The field-level prop still wins when it's set,
so honeypots or other ignore-only fields can override the form-level setting.
<Form schema={loginSchema} action={loginAction} passwordManagers="allow">
{({ fields }) => (
<>
<Input {...getInputProps(fields.email, { type: "email" })} />
<Input {...getInputProps(fields.password, { type: "password" })} />
</>
)}
</Form>With Field
Installation
npx visor add inputThis copies two files into your project:
components/ui/input/input.tsx— the componentcomponents/ui/input/input.module.css— the styles
Usage
import { Input } from '@/components/ui/input/input';
<Input type="email" placeholder="[email protected]" />API Reference
InputProps
| Prop | Type | Default | Description |
|---|---|---|---|
type | string | 'text' | HTML input type (text, email, password, number, file, etc.). |
size | 'sm' | 'md' | 'lg' | 'md' | Controls height, padding, font-size, and border-radius. |
leadingIcon | React.ReactNode | — | Optional leading glyph rendered inside the field (e.g. a Phosphor icon). The input reserves enough left padding to clear it. |
passwordManagers | 'ignore' | 'allow' | 'ignore' | Whether 1Password / Bitwarden / LastPass should offer to autofill this field. Defaults to "ignore"; set to "allow" on credential fields, or set once at the parent <Form passwordManagers="allow">. Field-level prop wins over Form context. |
placeholder | string | — | Placeholder text shown when the input is empty. |
disabled | boolean | false | Disables the input. |
className | string | — | Additional CSS class names to merge onto the element. |
...props | React.InputHTMLAttributes<HTMLInputElement> | — | All standard HTML input attributes are forwarded. |
The component also accepts all standard <input> HTML attributes.
Accessibility
- Renders a native
<input>— always pair with a<Label>using matchinghtmlFor/id - Use
aria-invalid="true"to signal a validation error; pair with a<FieldError>oraria-describedbypointing to the error message disabledsetsaria-disabledand prevents interaction;readonlyallows focus but prevents editing- Use the
Fieldcomponent to automatically compose label, description, and error for a fully accessible form field
Label Centering
A native <input> is not trimmed to the capital-height band (it would clip descenders and accents), so its value sits on the font's own line box: capitals land within 0.5px of centre at every size (Chromium shifts a little padding from the bottom to the top at md and lg, with the box size unchanged). Shared spec: docs/label-centering.md.