OTP <mv-otp>

Saisie de code à usage unique : un vrai champ masqué gère la frappe, le collage et le remplissage automatique par SMS, tandis que des cases animées affichent le code avec un curseur clignotant, un séparateur et une secousse en cas d’erreur.

CatégorieFormulaires
TypeWeb Component (<mv-otp>)
Statutstable
Keywordsotp, code, pin, 2fa, verification, one-time-code, form-associated

When to use

  • A one-time code from SMS or email must be entered, with paste and autofill support
  • A two-factor or login verification step needs a clear per-character layout with error feedback
  • A short numeric PIN must be entered masked

Avoid when

  • The value is a password or free-length secret rather than a fixed-length code → use Input instead
  • The code is long or has variable length, like a license key; a plain text field handles it better

Installation

node scripts/add.mjs otp --out ./src/marvelous

Agent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["otp"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, components/otp/otp.js, components/otp/otp.css.

Utilisation

Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :

<div style="display:grid;gap:1.75rem;justify-items:center;text-align:center">
  <form id="otp-demo-verify" style="display:grid;gap:.6rem;justify-items:center">
    <label for="otp-demo-code" style="font-size:.875rem;font-weight:600">Verification code</label>
    <mv-otp id="otp-demo-code" name="code" length="6" separator required aria-describedby="otp-demo-hint"></mv-otp>
    <p id="otp-demo-hint" style="margin:0;font-size:.8125rem;color:var(--mv-fg-muted)" aria-live="polite">Sent by text to (•••) •••-••42. Try <strong>424242</strong>.</p>
  </form>

  <div style="display:flex;gap:2.5rem;flex-wrap:wrap;justify-content:center">
    <div style="display:grid;gap:.5rem;justify-items:center">
      <label for="otp-demo-ref" style="font-size:.8125rem;font-weight:500;color:var(--mv-fg-muted)">Referral code</label>
      <mv-otp id="otp-demo-ref" length="6" pattern="alphanumeric" case="upper" separator="3" placeholder="-" data-size="sm"></mv-otp>
    </div>
    <div style="display:grid;gap:.5rem;justify-items:center">
      <label for="otp-demo-pin" style="font-size:.8125rem;font-weight:500;color:var(--mv-fg-muted)">PIN</label>
      <mv-otp id="otp-demo-pin" length="4" mask value="2718" data-size="sm"></mv-otp>
    </div>
  </div>
</div>
<script type="module">
  const otp = document.getElementById("otp-demo-code");
  const hint = document.getElementById("otp-demo-hint");
  const initial = hint.innerHTML;
  otp?.addEventListener("mv-complete", (e) => {
    if (e.detail.value === "424242") {
      otp.valid = true;
      hint.textContent = "Code confirmed, redirecting…";
      hint.style.color = "var(--mv-success)";
    } else {
      otp.invalid = true;
      hint.textContent = "Incorrect code, try again.";
      hint.style.color = "var(--mv-danger)";
    }
  });
  otp?.addEventListener("mv-change", (e) => {
    if (!e.detail.complete && hint.style.color) { hint.innerHTML = initial; hint.style.color = ""; }
  });
</script>

API

Attributes

NameTypeDefaultDescription
lengthnumber6Number of slots (1-12).
patternnumeric | alphanumeric | alpha | <single-character regex>numericAccepted characters; “numeric” opens the number pad on mobile.
caseupper | lowerForces the case of typed characters.
separatorboolean | "3" | "2,4"Visual separator: in the middle if empty, otherwise after the given positions.
maskbooleanMasks the characters (•), for a PIN.
placeholderstringCharacter(s) shown in empty slots (e.g. “-” or “000000”).
valuestringInitial value (and form reset value).
namestringField name in the form.
requiredbooleanInvalid until every slot is filled.
invalidbooleanError state (red border + shake when set); cleared on the next input.
validbooleanSuccess state (green border and tint); cleared on the next input.
disabledbooleanDisables input.
labelstringAccessible name when no <label for> is associated.
data-sizesm | lgSlot size.

Properties

NameTypeDescription
valuestringCurrent code (filtered and truncated).
completebooleanRead-only: every slot is filled.
form / validity / validationMessage-Native form API (ElementInternals).

Methods

NameDescription
focus()Focuses the real input.
clear()Clears the code and the valid/invalid states.
shake()Plays the shake animation.
checkValidity() / reportValidity()Native validation.

Events

NameDescription
mv-changedetail: { value, complete } on every change.
mv-completedetail: { value } when the last slot is filled.

CSS classes

NameDescription
mv-otp-slot / -char / -caret / -separatorGenerated parts (data-active, data-filled).

CSS variables

NameDefaultDescription
--mv-otp-slot-width2.75remSlot width.
--mv-otp-slot-height3remSlot height.
--mv-otp-gapvar(--mv-space-2)Space between slots.
--mv-otp-radiusvar(--mv-radius-md)Slot radius.

Accessibility

A single real <input> (autocomplete="one-time-code", numeric inputmode) holds focus and the accessible name (from <label for>, label or aria-label); the visual slots are aria-hidden. Arrow keys, Home/End, Backspace and Delete move between slots; pasting spreads the code. aria-invalid reflects the error state; form-associated element with native validation.

Cette page a été traduite par IA. Signaler un problème de traduction