Skip to content
Nana UI

Forms & Inputs

OTP

A one-time-code field: a row of single-character boxes that behave as one input, with typing, pasting and deletion handled across them.

View .md Download

A one-time-code field. The boxes look separate but behave as one input: typing advances, Backspace retreats, and a pasted code fills from the box it landed on. It is form-associated, so it submits under its name like any native control.

npm install @nana-tec/ui-components
import "@nana-tec/ui-components/otp";

Give it a length and a name. The value is the characters entered so far, so a partially typed code reads back as a short string.

alphanumeric widens what the boxes accept and switches the on-screen keyboard away from the numeric one. Anything outside the accepted set is dropped rather than rejected, so pasting "12 34-56" still fills six digit boxes.

A long run of boxes is hard to carry from a message, so six splits as 3-3 and eight as 4-4 on its own. group-size sets the split yourself, and separator changes the mark. The mark is hidden from assistive tech, so the code is still announced as characters rather than punctuation.

masked renders the characters as dots, for a code that should not be read over a shoulder.

required fails constraint validation until every box is filled, so a native form will not submit a half-entered code.

KeyResult
A characterFills the box and moves to the next
BackspaceClears the box, or clears the previous one and moves back if already empty
← / →Moves between boxes
Home / EndJumps to the first or last box
PasteFills from the current box onward, ignoring separators

Properties

PropertyAttributeTypeDefaultDescription
alphanumericalphanumericbooleanfalseAccept letters as well as digits.
disableddisabledbooleanfalse
groupSizegroup-sizenumber0How many boxes sit between separators. 0 picks a split that suits the length — six reads as 3-3, which is far easier to carry from a message than one run of six.
labellabelstring'One-time code'Labels the group for assistive tech.
lengthlengthnumber6How many characters the code has.
maskedmaskedbooleanfalseRender the characters as dots.
namenamestring''Submitted under this name.
requiredrequiredbooleanfalse
separatorseparatorstring'-'The mark drawn between groups.
valuevaluestring''The code entered so far.

Events

EventDescription
nana-inputOn every change; detail: { value: string }
nana-completeOnce every box is filled; detail: { value: string }

CSS parts

PartDescription
::part(base)The row of boxes
::part(slot)One character box
::part(separator)The mark drawn between groups

CSS custom properties

PropertyDescription
--nana-otp-gapSpace between the boxes
--nana-otp-group-gapSpace around a separator
--nana-otp-separator-colorColour of the separator
--nana-otp-sizeWidth and height of one box
--nana-otp-bgBox background
--nana-otp-colorCharacter colour
--nana-otp-font-sizeCharacter size
--nana-otp-radiusBox corner radius
--nana-otp-border-colorBox border colour
EventTypeDescription
nana-inputCustomEventThe code changed; detail.value is the code so far.
nana-completeCustomEventEvery box is filled; detail.value is the whole code.

The host is a group labelled by label, and each box names its own position (“One-time code, character 3 of 6”) so a screen reader announces where focus landed. The first box carries autocomplete="one-time-code", which lets a platform offer a code it received by SMS.

  • Input — a single free-text field
  • Checkbox — another form-associated control