Form

Code

Code captures a short, fixed-length verification code.

Components · Form

A code field captures a short, fixed-length value — a one-time password, a PIN, a signing confirmation code. Namirial doesn't give it dedicated tokens; it's a composition of single-character input cells laid out in a row, so it inherits sizes, focus, and validation for free.

Overview

The Code Field component is designed for entering short verification codes, where each character is entered into its own input cell. This layout improves readability, makes input errors easier to spot, and supports common authentication and verification flows.

When to use

Use the Code Field component when:

  • Users need to enter a one-time password (OTP) received via SMS, email, or an authenticator app.
  • Users need to enter or confirm a short PIN or verification code.
  • A workflow requires a short confirmation or signing code as part of a secure process.

Anatomy

Each cell is a full .nm-input instance — container and field — with maxlength="1" and centered text, laid out with a simple flex row. There is no .nm-code class in the theme; the row is just display: flex with a gap.

Tokens and values that drive each part

PartRoleSource
CellEvery visual property--nm-input-* — identical to a size-matched input (see Input)
Cell widthFixed so digits don't reflow32px, same at every size
CellBorder radius, both sizes8px
RowGap between cells8px
TextCentered in the celltext-align: center on .nm-input__field

Sizes

Code only supports two sizes — sm and lg — pick lg for signing flows where legibility at a glance matters most.

sm
in-page confirmations
lg
default for OTP, signing surfaces

States

Each cell carries its own focus ring, so the currently-active cell is unambiguous. Validation is applied to the whole row at once, not per cell.

Focus state

First digit entered, second cell focused and awaiting input

Error — all four cells recolor together with nm-input--error, with a status icon alongside the row

Error states

All code cells recolor together with nm-input--error.

Incorrect code. Please try again.

Error message - placed directly beneath the row, exactly like a standard input fields.

Code expires in 4:55
Incorrect code. Please try again.

Help text + error — If persistent help text (e.g. a countdown) is shown, keep it in its neutral color and display the error on a second line below it. This keeps the countdown visible during validation.

Success state

Success — shown once the whole code validates.

Code verified.

Behavior

A code field needs a small amount of JavaScript beyond CSS: typing a digit should auto-advance focus to the next cell, Backspace on an empty cell should move focus back, and pasting a full code should split it across every cell.

const cells = Array.from(row.querySelectorAll('.nm-input__field'));

cells.forEach((cell, i) => {
  cell.addEventListener('input', () => {
    cell.value = cell.value.replace(/\D/g, '').slice(0, 1);
    if (cell.value && cells[i + 1]) cells[i + 1].focus();
  });
  cell.addEventListener('keydown', (e) => {
    if (e.key === 'Backspace' && !cell.value && cells[i - 1]) cells[i - 1].focus();
  });
  cell.addEventListener('paste', (e) => {
    const digits = e.clipboardData.getData('text').replace(/\D/g, '').split('');
    if (digits.length < 2) return;
    e.preventDefault();
    digits.slice(0, cells.length).forEach((d, j) => { cells[j].value = d; });
    cells[Math.min(digits.length, cells.length) - 1].focus();
  });
});

Validate and submit only once every cell is filled — don't ask the user to press a separate "Verify" button for a 4–6 digit code if you can submit automatically on completion.

Light & dark theme

Light data-theme="light"
Dark data-theme="dark"

Do & don't

✓Do
Support pasting the entire code into any cell — split it automatically across the row.
✕Don't
Don't block paste or autofill — users copy codes from SMS, email, and password managers.
✓Do
Auto-advance focus to the next cell after each valid character is typed.
✕Don't
Don't move focus unpredictably or trap keyboard users — Tab and Shift+Tab must still work.
✓Do
Show errors only once the code is complete, or on submit.
✕Don't
Don't validate while the user is still typing partial input.
✓Do
Code expires in 4:55
Incorrect code.
Keep help text — like a resend countdown — visible, and show the error beneath it.
✕Don't
Incorrect code.
Don't replace helpful information — like the countdown — with the error message.

Accessibility

Semantics

  • Use <input inputmode="numeric" pattern="[0-9]*" maxlength="1"> per cell so mobile keyboards default to a numeric pad.
  • Wrap the row in a <fieldset> with a <legend> describing what the code is for ("Verification code sent to your phone").

Keyboard

  • Auto-advance and auto-retreat (described in Behavior) must not trap focus — Tab and Shift+Tab should still move a keyboard user through cells normally if they choose not to type sequentially.
  • Paste-splitting must not break the browser's native paste handling for users who paste one cell at a time.

Labeling

  • Announce validation results in an aria-live="polite" region near the field — don't rely on color or shake animation alone.
  • Give the fieldset an aria-describedby pointing at any help or countdown text ("Code expires in 4:59").

Playground

Type digits, paste a full code, or use Backspace to move back.

<div class="code-field"> <div class="nm-input nm-input--lg"> <div class="nm-input__container"> <input class="nm-input__field" maxlength="1"> </div> </div> <!-- × 4 cells --> </div>

Implementation

<link rel="stylesheet" href="namirial-theme.css">

<fieldset>
  <legend>Verification code</legend>
  <div class="code-field">
    <div class="nm-input nm-input--lg">
      <div class="nm-input__container">
        <input class="nm-input__field" inputmode="numeric" pattern="[0-9]*" maxlength="1" placeholder="-">
      </div>
    </div>
    <!-- repeat per digit -->
  </div>
</fieldset>

CSS classes

ClassValuesDefault
nm-input--{size}sm · lg per cell — the only two sizes code supportslg
nm-input--{intent}error · success, applied to every cell (or the last, for success)—
code-fieldFlex row with an 8px gap and fixed cell width—