

# PinInput

One-time code entry for email and SMS verification flows.

## Examples

### Default

```tsx
import { PinInput } from '@var-ui/react';

<PinInput label="Verification code" length={4} defaultValue="1234" autoFocus />
```

```astro
---
import { pinInput } from '@var-ui/core';

const p = pinInput();
const length = 4;
const value = '1234';
---

<div class:list={[p.root]}>
  <label class:list={[p.label]} id="pin-input-label">Verification code</label>
  <div class:list={[p.group]} role="group" aria-labelledby="pin-input-label">
    {Array.from({ length }).map((_, index) => (
      <input
        class:list={[p.cell]}
        type="text"
        inputmode="numeric"
        pattern="[0-9]*"
        maxlength="1"
        autocomplete={index === 0 ? 'one-time-code' : 'off'}
        aria-label={`Digit ${index + 1} of ${length}`}
        value={value[index] ?? ''}
        autofocus={index === length - 1}
      />
    ))}
  </div>
</div>
```

```html
<div class="var-ui-pin-input"><label class="var-ui-pin-input__label" id="pin-input-demo-label">Verification code</label><div class="var-ui-pin-input__group" role="group" aria-labelledby="pin-input-demo-label"><input class="var-ui-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="one-time-code" aria-label="Digit 1 of 4" value="1" /><input class="var-ui-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="off" aria-label="Digit 2 of 4" value="2" /><input class="var-ui-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="off" aria-label="Digit 3 of 4" value="3" /><input class="var-ui-pin-input__cell" type="text" inputmode="numeric" pattern="[0-9]*" maxlength="1" autocomplete="off" aria-label="Digit 4 of 4" value="4" autofocus /></div></div>
```

## Props

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| aria-label | string \| undefined | no |  |  |
| autoFocus | boolean \| undefined | no |  |  |
| className | string \| undefined | no |  |  |
| defaultValue | string \| undefined | no |  | Initial value for uncontrolled usage. |
| description | string \| undefined | no |  | Helper text shown below the control when there is no error. |
| errorMessage | string \| undefined | no |  | Validation message; when set, the field is shown in an error state. |
| isDisabled | boolean \| undefined | no |  |  |
| isRequired | boolean \| undefined | no |  |  |
| label | string \| undefined | no |  | Visible label rendered above the control. |
| length | number \| undefined | no | 4 | Number of digit cells. |
| onChange | ((value: string) => void) \| undefined | no |  | Called when any cell changes. |
| onComplete | ((value: string) => void) \| undefined | no |  | Called once when every cell is filled. |
| size | ControlSize \| undefined | no |  |  |
| type | PinInputType \| undefined | no | numeric | Accepted characters. |
| value | string \| undefined | no |  | Controlled value — one character per cell, no separators. |

## Accessibility

Uses a `role="group"` wrapper with one text input per digit. Each cell has an `aria-label` for its position, and the first cell sets `autocomplete="one-time-code"` so mobile browsers can suggest codes from SMS.
