InputOTP
An OTP field is a row of boxes for a code that arrived by text or email. The boxes are a picture: underneath there is one hidden input holding the whole code, which is what lets paste, undo and a password manager all work the way they do in any other field.
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "ropav";max-length decides how long the code is, and each InputOTPSlot takes the index it displays. InputOTPGroup splits the boxes into runs and InputOTPSeparator goes between them.
<InputOTP aria-label="Verification code" default-value="04" :max-length="4">
<InputOTPGroup>
<InputOTPSlot :index="0" />
<InputOTPSlot :index="1" />
<InputOTPSlot :index="2" />
<InputOTPSlot :index="3" />
</InputOTPGroup>
</InputOTP>input-otp-groups.vue
<script setup lang="ts">
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Label } from "ropav";
</script>
<template>
<div class="stack">
<Label>Verification code</Label>
<InputOTP aria-label="Verification code" default-value="042" :max-length="6">
<InputOTPGroup>
<InputOTPSlot :index="0" />
<InputOTPSlot :index="1" />
<InputOTPSlot :index="2" />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot :index="3" />
<InputOTPSlot :index="4" />
<InputOTPSlot :index="5" />
</InputOTPGroup>
</InputOTP>
</div>
</template>
<style scoped>
.stack {
display: flex;
flex-direction: column;
gap: calc(var(--rp-spacing) * 2);
}
</style>What the code may contain
pattern is a regular expression tested against the whole value, not one character. Three are exported ready-made — REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS and REGEXP_ONLY_DIGITS_AND_CHARS — and input-mode decides which keyboard a phone offers.
auto-complete defaults to one-time-code, which is what lets a phone offer the code straight from the message. Leave it alone unless the code is not one.
paste-transformer rewrites pasted text before it is accepted, for stripping the spaces or the prefix that come with a copied code.
Accessibility
- There is one control, not six, so a screen reader announces one field and the whole value — which is the right unit. Six separate inputs would announce six unlabelled boxes.
- Name it. The boxes carry no text of their own, so
aria-labelor aLabelis the only thing that says what code is being asked for. push-password-manager-strategydecides how the field gets out of the way of a password manager's badge. The default widens the control rather than letting the badge cover a box.- The separator is decoration and announces nothing — the code is read as one string regardless of how the boxes are grouped.
API
InputOTP
| Prop | Type | Default | Description |
|---|---|---|---|
ariaDescribedby | string | — | Ids of the elements that describe the field. |
ariaLabel | string | — | Accessible name, for a field with no visible label. |
ariaLabelledby | string | — | Ids of the elements that name the field. |
autoComplete | string | "one-time-code" | What the browser should offer to fill in. |
defaultValue | string | — | The code the field starts with when nothing is controlling it. |
form | string | — | id of the form to submit with, for a field rendered outside it. |
id | string | — | Lands on the control, which is the field as far as assistive technology is concerned. |
inputClass | string | — | Class for the hidden control. Rarely useful: it is invisible by design. |
inputMode | string | "numeric" | Keyboard a touch device should offer. |
isDisabled | boolean | — | Whether the field is disabled — no typing, no submission. |
isInvalid | boolean | — | Whether the code fails validation. Published to a nested FieldError. |
maxLength* | number | — | How many characters the code is. Decides how many slots there are. |
name | string | — | Name submitted with the form. |
pasteTransformer | ((pasted: string) => string) | — | Rewrites pasted text before it is accepted — to strip spaces or a prefix, say. |
pattern | string | RegExp | — | Characters the code may contain, as a regular expression over the whole value. |
placeholder | string | — | Characters shown in the empty slots before anything is typed. |
pushPasswordManagerStrategy | "increase-width" | "none" | "increase-width" | Whether the control shrinks to clear a password manager's badge. |
textAlign | "center" | "left" | "right" | "left" | Where the text sits inside the hidden control. |
validationDetails | ValidationDetails | — | Which constraint failed, for a caller that wants to branch on the reason. |
validationErrors | string[] | — | Messages a nested FieldError should show. |
value | string | — | The code. Set it to take the field over. |
variant | "primary" | "secondary" | "primary" | Visual variant. |
Slots: default.
| Event | Payload |
|---|---|
change | string |
complete | string |
update:value | string |
InputOTPGroup
Slots: default.
InputOTPSeparator
InputOTPSeparator takes nothing but class.
InputOTPSlot
| Prop | Type | Default | Description |
|---|---|---|---|
index* | number | — | Which character of the code this box shows. |