{
  "example": "import { InputOTP, InputOTPGroup, InputOTPSlot } from \"@godxjp/ui/data-entry\";\n\n<InputOTP maxLength={6}>\n  <InputOTPGroup>\n    <InputOTPSlot index={0} /><InputOTPSlot index={1} /><InputOTPSlot index={2} />\n    <InputOTPSlot index={3} /><InputOTPSlot index={4} /><InputOTPSlot index={5} />\n  </InputOTPGroup>\n</InputOTP>",
  "group": "data-entry",
  "importPath": "@godxjp/ui/data-entry",
  "name": "InputOTP",
  "props": [
    {
      "description": "Initial uncontrolled code.",
      "name": "defaultValue",
      "type": "string"
    },
    {
      "description": "Canonical value callback, compatible with FormFieldControl.",
      "name": "onValueChange",
      "type": "(value: string) => void"
    },
    {
      "description": "Mask filled visual slots without changing the submitted code.",
      "name": "mask",
      "type": "boolean | string"
    },
    {
      "description": "Normalize typed and pasted codes.",
      "name": "formatter",
      "type": "(value: string) => string"
    },
    {
      "description": "Shared control height tier.",
      "name": "size",
      "type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
    },
    {
      "description": "Validation appearance.",
      "name": "status",
      "type": "\"error\" | \"warning\""
    },
    {
      "description": "Shared field chrome.",
      "name": "variant",
      "type": "\"outlined\" | \"filled\" | \"borderless\" | \"underlined\""
    },
    {
      "description": "Refuse edits including paste.",
      "name": "readOnly",
      "type": "boolean"
    },
    {
      "description": "Number of slots (e.g. 6).",
      "name": "maxLength",
      "required": true,
      "type": "number"
    },
    {
      "description": "Controlled value.",
      "name": "value",
      "type": "string"
    },
    {
      "description": "Value callback (this is a true text input — onChange is the DOM-style value handler here).",
      "name": "onChange",
      "type": "(value: string) => void"
    },
    {
      "description": "Allowed-char regex (e.g. digits only).",
      "name": "pattern",
      "type": "string"
    },
    {
      "defaultValue": "\"start\"",
      "description": "Main-axis alignment of the whole code row (groups + separators) inside the field. `center` is the canonical auth challenge. Before this existed, every consumer wrapped the OTP in their own flex-centring div — do not. A service that wants all code fields centred sets `--otp-container-align` once instead.",
      "name": "align",
      "type": "\"start\" | \"center\" | \"end\""
    },
    {
      "description": "Fires ONCE the last slot fills, whether the user typed it or pasted the whole code. This is the auto-submit hook: a 2FA challenge with a visible submit button is a step nobody wants, and the alternative — watching `value.length === maxLength` in an effect — re-fires on every re-render. Keep the submit button anyway for the paste-then-correct case.",
      "name": "onComplete",
      "type": "(value: string) => void"
    },
    {
      "description": "Rewrites CLIPBOARD text before it reaches the field. Distinct from `formatter`, which normalises every value: this one only sees a paste, which is where the junk arrives — `\"123 456\"`, `\"code: 123456\"`, a copied SMS line. Note the order the field applies them: `pattern` is matched against the RAW keystroke first, so a pattern must accept what a user actually types, not only what these two produce.",
      "name": "pasteTransformer",
      "type": "(pasted: string) => string"
    },
    {
      "description": "Class on the ROW container `input-otp` renders (the slots' flex parent), which `className` cannot reach — `className` lands on the hidden input, because that is the real field. Prefer `align` and the `--otp-*` tokens; this is the vendored escape hatch underneath them.",
      "name": "containerClassName",
      "type": "string"
    },
    {
      "description": "`input-otp`'s answer to the 1Password / LastPass badge that browsers float over a code field and that covers the last slot. `increase-width` (its default) reserves room so the badge sits beside the row; `none` turns the accommodation off, which is what a row already centred by `align` usually wants. Pure layout — it changes no value and no keyboard behaviour.",
      "name": "pushPasswordManagerStrategy",
      "type": "\"increase-width\" | \"none\""
    },
    {
      "description": "The `<noscript>` stylesheet `input-otp` injects so the slots are still visible with JS disabled. Pass `null` to suppress it — the one real reason being a CSP that forbids inline styles and that `nonce` cannot satisfy. Leave it alone otherwise.",
      "name": "noScriptCSSFallback",
      "type": "string | null"
    },
    {
      "description": "CSP nonce stamped on the stylesheet `input-otp` injects. Required only under a `style-src 'nonce-…'` policy, where the field otherwise renders unstyled and the console reports a blocked inline style. Pass the same nonce the document was served with.",
      "name": "nonce",
      "type": "string"
    },
    {
      "description": "`input-otp`'s headless mode: you draw the entire row from the slot state instead of composing InputOTPGroup / InputOTPSlot. It is mutually exclusive with `children` — the vendor types the two as a union and this component keeps that union. Reaching for it means giving up the slot styling, the group outline and the separator this package owns, so it is the last resort, not a customisation point.",
      "name": "render",
      "type": "(props: InputOTPRenderProps) => React.ReactNode"
    }
  ],
  "related": [
    "Input (a normal single text field)",
    "PasswordInput (masked secret field)"
  ],
  "rules": [
    3,
    6
  ],
  "storyPath": "data-entry/input-otp.tsx",
  "subParts": [
    "InputOTPGroup",
    "InputOTPSeparator",
    "InputOTPSlot"
  ],
  "tagline": "One-time-code / 2FA input (input-otp) — N single-character slots that behave as one field. Compose InputOTP > InputOTPGroup > InputOTPSlot.",
  "usage": [
    "DO set `maxLength` to the code length and render that many InputOTPSlot with sequential `index`.",
    "DO centre a challenge with `align=\"center\"` on InputOTP — never with a wrapper `<div className=\"flex justify-center\">`. The container is owned by input-otp, so a wrapper is the only thing a consumer CAN reach, which is exactly why the prop exists.",
    "DO wrap slots in InputOTPGroup; use InputOTPSeparator between groups (e.g. 3 + 3).",
    "For device codes, set `appearance='grouped'` on each InputOTPGroup to render one outline per group while preserving the single hidden input, paste, caret, keyboard and screen-reader behavior.",
    "DON'T build N separate Inputs — this is ONE field with paste, arrow-key, and caret handling built in.",
    "DO widen the slots with `--otp-slot-size` when a challenge row must fill a wide auth panel — it defaults to the live `--control-height` tier, so re-scoping `--control-height` on the card instead would also resize the submit button and every other input in it. Set a NAMED tier (`var(--control-height-lg)`), never an ad-hoc calc offset.",
    "DO use `--otp-slot-inline-size` / `--otp-slot-block-size` when the code field is NOT square — a device-grant slot is taller than it is wide. They win over the `--otp-slot-size` shorthand and fall back to it, so setting neither keeps the square control tier. You rarely set them by hand inside `the auth CenteredShell preset=\"device-authorization\"`: that preset already owns its code-field measure.",
    "DO drive the sign-in MFA challenge from FormField: `error` wires aria-invalid + aria-errormessage + a role=alert message onto the single field, and the slot borders turn destructive. State is never colour-only."
  ],
  "useCases": [
    "2FA / OTP verification code",
    "Email / SMS confirmation code",
    "PIN entry",
    "Invite / redemption code"
  ]
}
