Password Input
A password field with a show/hide eye toggle and an optional live requirements checklist.
- Status
- Since
0.1.0- Accessibility pattern
- native input with reveal toggle
Last updated
Install
Add Password Input from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/password-inputThe same command installs the registry items it composes: @vegastack/icon-button, @vegastack/input.
Usage
import { PasswordInput } from "@/components/ui/password-input";
<PasswordInput aria-label="Password" autoComplete="current-password" />;PasswordInput wraps Input with a trailing eye
toggle — an IconButton in the ghost recipe — that flips the field
between type="password" and type="text".
Visibility is local component state, so the field works uncontrolled out of the
box — every other native attribute (value/defaultValue, onChange, name,
required, disabled, autoComplete, …) passes straight through, and the ref
forwards to the underlying <input>.
Examples
States
The field reflects disabled (which cascades to the reveal toggle too) and
aria-invalid. The invalid state re-colors the border with the destructive
token — pair it with a visible error message for non-color-dependent feedback.
Revealing the value
The eye toggle flips the field between type="password" and type="text",
swapping the lucide Eye icon for EyeOff and setting aria-pressed="true"
while revealed. Visibility is local component state, so the example below is
interactive — click the eye to reveal. Override the toggle's accessible label
with toggleAriaLabel (shown here as "Show password").
Requirements
Pass a requirements array of { label, met } to render a live checklist below
the field — ideal for signup and password-reset flows. Each row shows a success
check when met and a muted cross when unmet, so the state never depends on color
alone. The input references the list with aria-describedby, each row includes
hidden Met: / Not met: state text, and a polite live summary announces how
many requirements are currently satisfied. Drive met from your own validation
and the list re-renders as the user types.
0 of 3 password requirements met
- Not met: At least 8 characters
- Not met: Contains a number
- Not met: Contains a special character
Once every rule is satisfied, each row turns to a success check
(text-success-text) — shown statically below with all met: true:
3 of 3 password requirements met
- Met: At least 8 characters
- Met: Contains a number
- Met: Contains a special character
Playground
Every PasswordInput prop, auto-generated from its TypeScript types.
API Reference
PasswordInput
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | ((state: InputState) => string | undefined) | — | Classes for the Base UI input element. Accepts Base UI's state-function
form, so styles can respond to field state such as focused or invalid. |
containerClassName | string | — | Classes for the wrapper used only when prefix or suffix is present. |
requirements | PasswordRequirement[] | — | Optional checklist of rules rendered below the field. Each entry shows a success check (met) or a muted cross (unmet); the styling never relies on color alone. Omit to render a bare password field. |
size | "lg" | "md" | "sm" | 'md' | Control height on the shared 28/32/40 scale (--size-sm/md/lg), matching
Button and Select. (The native numeric size attribute is intentionally
replaced by this variant prop.) |
toggleAriaLabel | string | "Toggle password visibility" | Accessible label for the show/hide toggle button. |
Data attributes and CSS variables on PasswordInput
| Attribute | Values |
|---|---|
data-met | "" |
data-slot | "password-input" | "password-input-requirements" |
Each entry in the requirements array is a PasswordRequirement:
PasswordRequirement
| Prop | Type | Default | Description |
|---|---|---|---|
label* | string | — | Human-readable description of the rule (e.g. "At least 8 characters"). |
met* | boolean | — | Whether the current value satisfies this rule. |
Accessibility
- Renders a native
<input>— always associate a visible<label>(wrap it or usehtmlFor/id); usearia-labelonly when a visible label is impossible. - The toggle is an
IconButton— which makes the missingaria-labela type error rather than a review note — witharia-pressedreflecting the reveal state. Override its accessible name viatoggleAriaLabel. - The toggle remains keyboard reachable; Tab moves from the field to the reveal button, and Enter / Space toggles visibility.
- On focus the field re-colors its border with the
ringtoken (focus:border-ring/(--alpha-tint-border), inherited fromInput) and the reveal toggle shows the centralized 2px:focus-visibleoutline — a visible focus treatment on both. - The eye swap has no motion. A visibility toggle is not an arrival and not a success; the glyph changes instantly.
- Requirement rows pair an icon with text, hidden met/unmet state text, and a live summary, so met/unmet is conveyed without relying on color alone.
| Contract | States tested |
|---|---|
| Behaviour | default, disabled, pressed, success |
| Accessibility | described, disabled, focus-visible, labeled, live, pressed, semantic-html |
| Visual | default, hover, focus, disabled, success |