{
  "name": "otp-field",
  "type": "registry:ui",
  "title": "OTP Field",
  "description": "Fixed-length segmented input for one-time passwords, verification codes and recovery codes.",
  "registryDependencies": [
    "@siteplane/base",
    "@siteplane/separator"
  ],
  "dependencies": [
    "@base-ui/react@1.5.0"
  ],
  "files": [
    {
      "path": "src/components/ui/otp-field.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { OTPFieldPreview as OTPFieldPrimitive } from \"@base-ui/react/otp-field\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { useResolvedControlSize } from \"@/components/ui/siteplane-provider\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { surfaceClassNames } from \"@/components/ui/surface\";\n\nexport function OTPField({\n  className,\n  size,\n  ...props\n}: React.ComponentProps<typeof OTPFieldPrimitive.Root> & {\n  size?: \"sm\" | \"default\" | \"lg\" | \"xl\";\n}): React.ReactElement {\n  const resolvedSize = useResolvedControlSize(size);\n\n  return (\n    <OTPFieldPrimitive.Root\n      className={cn(\n        \"flex items-center gap-2 has-disabled:opacity-64 has-disabled:**:data-[slot=otp-field-input]:shadow-none has-disabled:**:data-[slot=otp-field-input]:before:shadow-none!\",\n        className,\n      )}\n      data-size={resolvedSize}\n      data-slot=\"otp-field\"\n      {...props}\n    />\n  );\n}\n\nexport function OTPFieldInput({\n  className,\n  ...props\n}: React.ComponentProps<typeof OTPFieldPrimitive.Input>): React.ReactElement {\n  return (\n    <OTPFieldPrimitive.Input\n      className={cn(\n        surfaceClassNames.formControlCell,\n        \"in-[[data-slot=otp-field][data-size=sm]]:size-8 in-[[data-slot=otp-field][data-size=lg]]:size-10 in-[[data-slot=otp-field][data-size=xl]]:size-14 size-9 in-[[data-slot=otp-field][data-size=sm]]:text-sm in-[[data-slot=otp-field][data-size=lg]]:text-lg in-[[data-slot=otp-field][data-size=xl]]:text-lg in-[[data-slot=otp-field][data-size=sm]]:leading-8 in-[[data-slot=otp-field][data-size=lg]]:leading-10 in-[[data-slot=otp-field][data-size=xl]]:leading-6 leading-9 caret-transparent selection:bg-transparent selection:text-foreground sm:in-[[data-slot=otp-field][data-size=sm]]:size-7 sm:in-[[data-slot=otp-field][data-size=lg]]:size-9 sm:in-[[data-slot=otp-field][data-size=xl]]:size-14 sm:size-8 sm:in-[[data-slot=otp-field][data-size=lg]]:text-base sm:in-[[data-slot=otp-field][data-size=xl]]:text-lg sm:in-[[data-slot=otp-field][data-size=sm]]:leading-7 sm:in-[[data-slot=otp-field][data-size=lg]]:leading-9 sm:in-[[data-slot=otp-field][data-size=xl]]:leading-6 sm:leading-8\",\n        className,\n      )}\n      data-slot=\"otp-field-input\"\n      spellCheck={false}\n      {...props}\n    />\n  );\n}\n\nexport function OTPFieldSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof Separator>): React.ReactElement {\n  return (\n    <OTPFieldPrimitive.Separator\n      render={\n        <Separator\n          className={cn(\n            \"rounded-full bg-input data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:w-3\",\n            className,\n          )}\n          orientation=\"horizontal\"\n          {...props}\n        />\n      }\n    />\n  );\n}\n\nexport { OTPFieldPrimitive };\n"
    }
  ],
  "categories": [
    "form-controls"
  ],
  "docs": "# OTP Field\n\n> Siteplane UI release `0.1.1`.\n\n## Public Purpose\n\nUse OTP Field for short one-time passwords, verification codes and recovery codes that should be entered as a fixed sequence of slots.\n\n## Import\n\n`import { OTPField, OTPFieldInput, OTPFieldSeparator } from \"@/components/ui/otp-field\";`\n\n`import { Field, FieldDescription, FieldError, FieldLabel } from \"@/components/ui/field\";`\n\n## Public API\n\n- `OTPField` is the root and requires `length`; it also accepts `size=\"sm\" | \"default\" | \"lg\" | \"xl\"` and the Base UI OTP Field props.\n- `size=\"xl\"` renders each slot as a 56px square; the segmented value remains a\n  set of real inputs. Use the surrounding provider's `radius=\"sm\"` personality\n  for the global 8px main corner.\n- Render one `OTPFieldInput` for every character in DOM order. The number of slots must match `length`.\n- `OTPFieldSeparator` visually divides slot groups without changing the value length.\n- Use `validationType`, `normalizeValue`, `onValueInvalid`, `mask` and controlled or uncontrolled value props on the root.\n\n## Public Motion\n\n- Slots do not translate or resize while the value changes. Focus, invalid and disabled feedback use the native state styles.\n- Reduced motion needs no extra handling because OTP Field does not animate with scale or movement.\n\n## Public Accessibility\n\n- Pair the field with a visible `FieldLabel`; without one, give every slot a specific label such as `Character 2 of 6`.\n- Keep the slot count synchronized with `length` and explain rejected characters when custom validation is active.\n- Masking protects shoulder-surfing only; it is not a substitute for secure verification logic.\n\n## Public Agent Guidance\n\n- Use OTP Field only for short fixed-length codes, not free text.\n- Keep every slot as a real `OTPFieldInput` and use `length`, not the legacy `maxLength` contract.\n- Do not replace the native slot, focus, disabled, invalid or masking behavior.\n\n## Public Links\n\n- [Documentation and preview](https://ui.siteplane.io/docs/components/form-controls/otp-field)\n\n- [Registry source](https://ui.siteplane.io/r/otp-field.json)\n",
  "meta": {
    "siteplane:agentContractVersion": 1,
    "siteplane:docs": "https://ui.siteplane.io/docs/components/form-controls/otp-field",
    "siteplane:releaseVersion": "0.1.1",
    "siteplane:sourceOwned": true
  },
  "devDependencies": []
}
