# PasswordField Guides

## Patterns

### Sign-In Password Row

When to use:

- users enter an existing account password

Composition notes:

- set `autoComplete='current-password'`
- keep the toggle visible and close to the input

Trade-offs:

- familiar pattern
- reveal affordance adds one extra action in the row

```tsx
import {
  Label,
  PasswordField,
  PasswordFieldHiddenIcon,
  PasswordFieldInput,
  PasswordFieldShownIcon,
  PasswordFieldToggleIconButton,
} from '@vega-ui/react';
import { useId } from 'react';

export default function SignInPassword() {
  const id = useId();

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-3)', width: '100%' }}>
      <Label htmlFor={id}>Password</Label>
      <PasswordField>
        <PasswordFieldInput id={id} name='password' autoComplete='current-password' />
        <PasswordFieldToggleIconButton aria-label='Show password'>
          <PasswordFieldShownIcon />
          <PasswordFieldHiddenIcon />
        </PasswordFieldToggleIconButton>
      </PasswordField>
    </div>
  );
}
```

### New Password Creation

When to use:

- users create or reset a password

Composition notes:

- set `autoComplete='new-password'`
- pair the field with helper copy or strength guidance

Trade-offs:

- improves clarity during onboarding or reset flows
- can encourage overreliance on visual strength hints

```tsx
import {
  Icon,
  Label,
  PasswordField,
  PasswordFieldHiddenIcon,
  PasswordFieldInput,
  PasswordFieldShownIcon,
  PasswordFieldToggleIconButton,
  Text,
} from '@vega-ui/react';
import { CheckIcon, CircleIcon } from '@vega-ui/icons';
import { useId, useState } from 'react';

const RULES = [
  { label: 'At least 12 characters', test: (value: string) => value.length >= 12 },
  { label: 'Upper and lower case letters', test: (value: string) => /[a-z]/.test(value) && /[A-Z]/.test(value) },
  { label: 'At least one number', test: (value: string) => /\d/.test(value) },
  { label: 'At least one symbol', test: (value: string) => /[^A-Za-z0-9]/.test(value) },
];

export default function NewPasswordChecklist() {
  const id = useId();
  const rulesId = `${id}-rules`;
  const [password, setPassword] = useState('');
  const passed = RULES.filter((rule) => rule.test(password)).length;

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-6)', width: '100%' }}>
      <div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
        <Label htmlFor={id}>New password</Label>
        <PasswordField>
          <PasswordFieldInput
            id={id}
            name='newPassword'
            autoComplete='new-password'
            value={password}
            aria-describedby={rulesId}
            onChange={(event) => setPassword(event.currentTarget.value)}
          />
          <PasswordFieldToggleIconButton aria-label='Show password'>
            <PasswordFieldShownIcon />
            <PasswordFieldHiddenIcon />
          </PasswordFieldToggleIconButton>
        </PasswordField>
      </div>
      <ul id={rulesId} style={{ display: 'grid', gap: 'var(--spacing-3)', margin: 0, padding: 0, listStyle: 'none' }}>
        {RULES.map((rule) => {
          const ok = rule.test(password);
          return (
            <li
              key={rule.label}
              style={{
                display: 'flex',
                alignItems: 'center',
                gap: 'var(--spacing-4)',
                color: ok ? 'var(--label-primary)' : 'var(--label-tertiary)',
              }}
            >
              <Icon size='xs' aria-hidden style={{ flexShrink: 0 }}>
                {ok ? <CheckIcon strokeWidth={2.5} /> : <CircleIcon />}
              </Icon>
              <Text size={2} style={{ color: 'inherit' }}>{rule.label}</Text>
            </li>
          );
        })}
      </ul>
      <Text size={2} fontWeight={500} role='status' style={{ fontVariantNumeric: 'tabular-nums' }}>
        {passed} of {RULES.length} requirements met
      </Text>
    </div>
  );
}
```

### Custom Visibility Iconography

When to use:

- product language calls for custom security iconography

Composition notes:

- keep the accessible button label generic and clear
- swap icons inside the shipped toggle part

Trade-offs:

- fits product language
- easy to over-style if the icons dominate the field

```tsx
import {
  Label,
  PasswordField,
  PasswordFieldHiddenIcon,
  PasswordFieldInput,
  PasswordFieldShownIcon,
  PasswordFieldToggleIconButton,
} from '@vega-ui/react';
import { LockIcon, LockOpenIcon } from '@vega-ui/icons';
import { useId } from 'react';

export default function CustomVisibilityIcons() {
  const id = useId();

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-3)', width: '100%' }}>
      <Label htmlFor={id}>Vault passphrase</Label>
      <PasswordField>
        <PasswordFieldInput id={id} autoComplete='current-password' />
        <PasswordFieldToggleIconButton aria-label='Show passphrase'>
          <PasswordFieldShownIcon><LockIcon /></PasswordFieldShownIcon>
          <PasswordFieldHiddenIcon><LockOpenIcon /></PasswordFieldHiddenIcon>
        </PasswordFieldToggleIconButton>
      </PasswordField>
    </div>
  );
}
```

## Comparison

### Quick Decision Rule

Use `PasswordField` when users must enter a secret and may need to reveal it temporarily. Use `TextField` for generic text input.

### `PasswordField` vs `TextField`

Use `PasswordField` when:

- the value is a masked secret with reveal behavior

Use `TextField` when:

- the field is ordinary text input

### `PasswordField` vs `PinField`

Use `PasswordField` when:

- the secret is free-form

Use `PinField` when:

- the secret is a short fixed-length code

### Choose This Component When

- the value should be masked by default
- a reveal/hide affordance improves input review
- password-manager and browser secret-entry behavior matter

### Do Not Choose This Component When

- the field is not a secret
- the value is a fixed-length OTP or PIN

## Troubleshooting

### The Browser Does Not Offer To Save The Password

#### Symptom

The browser does not offer to save the password, or a shown password appears in the autofill suggestions of other fields.

#### Likely Cause

The password was shown when the form was submitted, so the input had `type='text'` and the browser treated it as a plain text field.

#### How To Verify

- submit the form with the password shown and with it hidden, and compare

#### Fix

Control `shown` and set it to `false` in the `onSubmit` handler of the form, before the browser collects the values.

### Autofill Behaves Differently Across Browsers

#### Symptom

Autofill works in one browser but not another.

#### Likely Cause

Password managers and native autofill interpret password fields differently, especially for new-password vs current-password flows.

#### How To Verify

- test the real browser flow with the intended `autoComplete` value

#### Fix

Use the correct autocomplete token and test in real target browsers.

### Strength Hints Do Not Match Validation

#### Symptom

The UI claims the password is strong, but backend validation still rejects it.

#### Likely Cause

Visual strength scoring and actual password policy are different systems.

#### How To Verify

- compare frontend strength hints with backend policy rules

#### Fix

Document policy separately and treat strength hints as advisory, not authoritative.
