PasswordInput

Password field with a show/hide toggle, plus a live requirements list that gives check and × feedback as the user types.

Spec · from metadata

When to use

  • Set-password, change-password, and reset-password forms
  • Sign-in password fields — use `PasswordInput` alone, without the rules list
  • Any field where the user types a secret they may want to check before submitting

When not to use

  • One-time codes and MFA challenges — use `InputOTP`
  • Non-secret values that merely look sensitive — a plain `Input` is clearer
  • Displaying a stored password. Never render one; offer a reset instead

Anti-patterns

Avoid<Input type="password" />
{error && <p className="text-destructive">Password too short</p>}
Prefer<PasswordInput />
<PasswordRulesList value={value} rules={RULES} />

The convention states rules as positive assertions with live feedback, and the rules list doubles as the error state. Telling someone what went wrong after they submit is slower than showing them what is required as they type.

Avoid<PasswordRulesList value={value} rules={HARDCODED_LIBRARY_RULES} />
Prefer<PasswordRulesList value={value} rules={ourPolicyRules} />

Password policy belongs to the product and its server. The component renders whatever policy it is handed; it does not define one.

Accessibility

  • Required ARIAaria-labelaria-pressed
  • Min touch target48px
  • Screen readerThe toggle is a real button with an aria-label that swaps between show and hide, and aria-pressed reflecting the current state. Each rule row carries visually hidden 'met' or 'not met' text so the check and × are not conveyed by icon alone. The optional strength bar is a progressbar with aria-valuetext naming the level.
  • ContrastUnmet rules use text-muted-foreground; met rules use text-foreground with a success-colored check. State is never conveyed by color alone — the icon shape and the hidden text both carry it.

Token bindings

TokenCategoryUsage
muted-foregroundcolorToggle icon, unmet rule text and ×
successcolorMet-rule check icon
primarycolorStrength bar fill
mutedcolorStrength bar track
text-smtypographyRule row scale
Recent changes
  • new

    PasswordInput and PasswordRulesList compounds

    2026-08-12

This is the password-rules convention as components: rules stated as positive assertions, live feedback, an eye toggle on the field, and the rules list doubling as the error state.

Import

import { PasswordInput, PasswordRulesList } from "@timelycare/helix-ui"

Props

interface PasswordInputProps extends Omit<React.ComponentProps<"input">, "type"> {
  showPasswordLabel?: string   // default "Show password"
  hidePasswordLabel?: string   // default "Hide password"
  hideToggle?: boolean         // the convention is to keep the toggle
}

interface PasswordRule {
  id: string
  label: React.ReactNode       // positive assertion, already translated
  test: (value: string) => boolean
}

interface PasswordRulesListProps {
  value: string
  rules: PasswordRule[]
  title?: React.ReactNode
  showStrength?: boolean       // default false
  strengthLabels?: [string, string, string, string]
  strengthLabel?: string
  className?: string
}

Usage

Sign-in

The toggle is the whole point here; no rules list on a field the user is recalling rather than choosing.

<Label htmlFor="password">Password</Label>
<PasswordInput id="password" autoComplete="current-password" />

Set or change a password

const RULES = [
  { id: "length", label: "Must have 10 characters", test: (v) => v.length >= 10 },
  { id: "upper",  label: "Must contain 1 capital letter", test: (v) => /[A-Z]/.test(v) },
  { id: "number", label: "Must contain 1 number", test: (v) => /\d/.test(v) },
]

<Label htmlFor="new-password">New password</Label>
<PasswordInput
  id="new-password"
  autoComplete="new-password"
  value={value}
  onChange={(e) => setValue(e.target.value)}
/>
<PasswordRulesList value={value} rules={RULES} />

Submit stays disabled until every rule passes — see the wizard footer's disabled-until-valid rule.


The rules come from the product

rules is caller-supplied on purpose. Password policy is owned by each product and enforced by its server; the component renders whatever policy it is handed.

A list that tells someone their password is acceptable while the API rejects it is worse than showing no list at all, so the list must mirror what is actually enforced.


Structure

ElementTailwind
Field wrapperrelative
InputInput + pr-10 to clear the toggle
Toggleabsolute inset-y-0 right-0 px-3, size-4 icon
Rule rowflex items-center gap-2 text-sm
Met ruletext-foreground + CheckIcon text-success
Unmet ruletext-muted-foreground + XIcon
Strength track / fillbg-muted / bg-primary

Strength bar

Off by default. When enabled with showStrength, it is a summary of how many rules pass, rendered as a progressbar.

It is deliberately not an entropy estimate, and it is secondary to the list: the rules tell someone what to do, a bar only tells them how they are doing.

<PasswordRulesList value={value} rules={RULES} showStrength />

Accessibility

  • The toggle is a real <button type="button"> with an aria-label that swaps between show and hide, and aria-pressed reflecting state.
  • Toggling visibility does not move focus or reset the caret.
  • Each rule row carries visually hidden "met" or "not met" text, so state is never conveyed by icon or color alone.
  • The strength bar is a progressbar with aria-valuetext naming the level, not just a number.
  • Pass every string already translated; the library does no i18n and the defaults are English.
  • Do not disable paste. It breaks password managers, which makes people choose worse passwords.

Gotchas

ProblemSolution
Rules pass but the server rejects the passwordThe list does not mirror the enforced policy; fix the rules array
Rules read like errorsState them as positive assertions ("Must have 10 characters"), not failures
A separate error message under the listThe list is the error state; remove the duplicate
Icon overlaps long valuesKeep the pr-10 on the input if you pass a custom className
Browser offers the wrong autofillSet autoComplete="current-password" for sign-in, "new-password" for set/change

Related

  • Related Components: Input, Label, Field, InputOTP (one-time codes), Form
  • Patterns: form patterns — password rules display, and the disabled-until-valid footer rule