One semantic value
Labels, form submission, selection, and editing remain attached to a single native input.
Progressive enhancement for one-time codes
A11y OTP Field keeps the real form value in one text input, then adds decorative visual slots without taking over keyboard editing, paste, or one-time-code autofill.
Enter six digits or paste your code.
Ready for paste and one time code autofill
Live demo
Enter six digits, or paste 123-456. Use the same selection, arrow, Home, End, Backspace, Delete, and clipboard commands as any text field.
Why this approach
The component enhances author-provided markup instead of replacing a text field with a row of separate controls.
Labels, form submission, selection, and editing remain attached to a single native input.
Generated slots are marked aria-hidden="true" and never become extra focus stops.
Keyboard commands and paste remain available, while numeric and alphanumeric values can be normalized.
The labeled text field remains usable without JavaScript, and auto-submit stays disabled unless enabled explicitly.
Example gallery
Each page isolates one configuration or state so you can inspect the markup, interaction, and fallback without unrelated UI.
Setup
The package is prepared for npm but is not public yet. Until it is published, clone the repository, install its development dependencies, and build the distributable files locally.
git clone https://github.com/vmitsaras/A11y-OTP-Field.git
cd A11y-OTP-Field
npm install
npm run build:dist
python3 -m http.server 4173
import { createA11yOtpField } from "a11y-otp-field";
import "a11y-otp-field/styles.css";
const root = document.querySelector("[data-a11y-otp]");
if (root instanceof HTMLElement) {
createA11yOtpField(root, { length: 6 });
}
The plugin generates the visual slots during enhancement. Do not add one input for each character.
<div class="a11y-otp" data-a11y-otp data-otp-length="6">
<label class="a11y-otp__label" for="verification-code">
Verification code
</label>
<p class="a11y-otp__hint" id="verification-code-hint">
Enter the six-digit code.
</p>
<div class="a11y-otp__control" data-a11y-otp-control>
<input
class="a11y-otp__input"
data-a11y-otp-input
id="verification-code"
name="verificationCode"
type="text"
inputmode="numeric"
autocomplete="one-time-code"
maxlength="6"
aria-describedby="verification-code-hint"
/>
</div>
<p class="a11y-otp__error" data-a11y-otp-error></p>
<p class="a11y-otp__status" data-a11y-otp-status aria-live="polite"></p>
</div>
Configuration
Options can come from JavaScript or matching data-otp-* attributes. Defaults favor predictable manual submission.
| Option | Default | Purpose |
|---|---|---|
length | 6 | Sets the accepted code length, up to 64 characters. |
type | numeric | Accepts numeric, alphanumeric, or custom-normalized values. |
groups | null | Groups slots visually without splitting the form value. |
privacyMode | visible | Controls visible, masked, or reveal-last presentation. |
validateOn | submit | Runs validation on submit, blur, or only when requested. |
autoSubmit | false | Opts into delayed form submission after completion. |
messages | Built in | Overrides validation and status text. |
getValue(), setValue(), clear(), focus controls, state setters, validate(), and destroy().
The root dispatches bubbling init, input, change, complete, incomplete, paste, error, clear, and destroy events under the a11y-otp-field: namespace.
Accessibility behavior
The component relies on native text-input behavior and keeps visual presentation out of the accessibility tree.
type="text" input owns the value.inputmode and autocomplete="one-time-code" are preserved or added.aria-hidden="true".aria-invalid, and descriptions.Styling
Override public properties on .a11y-otp. Variables prefixed with --_ are internal and are not customization hooks.
.checkout-code {
--a11y-otp-slot-size: 3rem;
--a11y-otp-slot-gap: 0.625rem;
--a11y-otp-focus-color: #075fe4;
--a11y-otp-error-color: #a61b29;
--a11y-otp-radius: 0.75rem;
}
--a11y-otp-slot-size--a11y-otp-slot-gap--a11y-otp-group-gap--a11y-otp-border-color--a11y-otp-focus-color--a11y-otp-error-color--a11y-otp-radiusImplementation responsibilities
Inspect the implementation
Review the package API, tests, demo states, and fallback behavior before integrating a verification service.