OTP Input

One native code input with visual character slots, including masked PIN entry

OTP Input

OTP and PIN entry use the same component. Like the DaisyUI OTP pattern, visual slots sit behind one native input. The browser owns text editing, selection, paste, deletion, validation, form submission and reset. There is one accessible label, one focus stop and one submitted value; no focus-moving script is needed.

Imports

Individual example prerequisites (including composition and demo actions):

@import "@duskmoon-dev/core/base.css";
@import "@duskmoon-dev/core/themes/sunshine";
@import "@duskmoon-dev/core/components/otp-input.css";
@import "@duskmoon-dev/core/components/form-group.css";
@import "@duskmoon-dev/core/components/button.css";

base.css supplies global focus/utility definitions and shared spacing/shape tokens; use your framework reset when appropriate. Substitute the Moonlight theme for dark forms.

Aggregate: @duskmoon-dev/core with Tailwind v4; no-Tailwind: @duskmoon-dev/core/standalone.css. Individual: @duskmoon-dev/core/components/otp-input.css plus a theme/reset and form-group.css for labels/help. Extensionless components/otp-input exports generated css and constructable styles, with no interaction runtime.

Single Native Input

Use a .otp-input label containing empty aria-hidden="true" spans, followed by one .otp-code input. Each span defines a visual slot; use one to eight spans and match minlength, maxlength and pattern to the code length. The spans are decorative: all characters, the caret and the selection belong to the real input.

Six-character code

Enter the complete six-digit code.

Error: enter exactly six digits.

Four-character code

Use type="text" for visible codes: it preserves leading zeros and supports native length/pattern constraints. inputmode="numeric" suggests a keyboard; the pattern validates the characters. For alphanumeric codes, use an appropriate pattern and text input mode. The span count changes presentation; it does not replace HTML constraints.

Joined Slots

Joined code

Sizes

Apply .otp-input-xs, .otp-input-sm, .otp-input-md (default), .otp-input-lg or .otp-input-xl to the wrapper.

OTP sizes

Colors

The default uses the on-surface color. Seven semantic variants change the wrapperโ€™s color, which the slot borders inherit.

OTP colors

Appearance

Ghost, filled and underline

Masked PIN

Use the same OTP component with type="password" to mask a PIN. It still submits one complete value, including leading zeros. Choose an autocomplete hint appropriate to your application; one-time-code is for codes delivered for a single use. Masking hides the displayed text and does not replace server-side security checks.

Masked PIN

States

Application error and disabled code

Error: the code has expired.

Set native disabled and aria-invalid on the input. Native success requires .validator.validator-success; a success color alone does not prove that a code was accepted. Adjacent .validator-error text reserves space while hidden so displaying feedback does not interrupt a Reset click. Follow the Validator contract for explicit error precedence.

API Reference

Class / markup Role
.otp-input Label wrapper for decorative slots and one input
span[aria-hidden="true"] One visual slot; use 1โ€“8 before the input
.otp-code The only editable input and submitted value
.otp-input-joined Joined slots
.otp-input-xs/sm/md/lg/xl Slot and character sizes
.otp-input-primary/secondary/tertiary/info/success/warning/error Semantic colors
.otp-input-ghost/filled/underline Appearance variants
.otp-error-message Application error text; associate with aria-describedby

The standalone .otp-code presentation remains available without a wrapper; .otp-code-4 and the .otp-code-{color} modifiers retain that compact presentation. Migrate old .otp-input-field or .pin-input-field groups to one .otp-code input. There is no separate PIN component or per-character input controller.

Verification Boundary

Automated checks cover text editing, clipboard paste, selection, native constraints, FormData, reset and styling. autocomplete="one-time-code" requests platform support: real SMS delivery, device autofill and mobile keyboard behavior require separate device tests. No device-level autofill verification is claimed.

Input ยท Field composition