# One time code

A one-time code typed into a row of boxes, one character each: the code an email or authenticator app gives. It is one text field underneath, so a screen reader hears one field, the phone's autofill and a paste fill every box at once, and Backspace works as in any field. Characters the code can't hold are dropped as they're typed or pasted; letters are upper-cased, and digits typed in Arabic-Indic or Persian script are kept as 0 to 9. The boxes run left to right in a right-to-left page too, as the code was written, and the highlighted box follows the caret.

Name it with a `<label>` around it or `Field`. After a refused code, set `attend` with focus moved to it, as for an `Input`.

- Registry name: `one-time-code` (`@candor/one-time-code`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/one-time-code

## Install

```bash
npx candor-ui add one-time-code
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/one-time-code`.
It brings in: motion, tokens, utils.

## Example

```tsx
"use client"

import * as React from "react"
import { Field } from "@/components/candor/ui/field"
import { OneTimeCode } from "@/components/candor/ui/one-time-code"

// One field underneath: autofill and paste fill every box. Nothing is sent when the last box fills unless you send it.
export default function Example() {
  const [code, setCode] = React.useState("")
  const [checked, setChecked] = React.useState<string | null>(null)
  return (
    <div className="flex flex-col gap-3">
      <Field label="Code from your authenticator app" hint="Six digits.">
        <OneTimeCode value={code} onValueChange={setCode} onComplete={setChecked} />
      </Field>
      <p className="text-sm text-muted-foreground" aria-live="polite">
        {checked ? `Checking ${checked}…` : ""}
      </p>
    </div>
  )
}
```

## <OneTimeCode>

Props:

- `length?: number | undefined` (default `6`): How many characters the code has. Defaults to 6.
- `kind?: OneTimeCodeKind | undefined` (default `"numeric"`): Digits only (the default), or digits and letters.
- `value?: string | undefined`: The code typed so far, when you control it.
- `defaultValue?: string | undefined`: The code at first, when you don't control it.
- `onValueChange?: ((code: string) => void) | undefined`: Called with the code as it changes, already cleaned of characters it can't hold.
- `onComplete?: ((code: string) => void) | undefined`: Called once the last box is filled, with the whole code. Submit from here if you like; nothing is sent for you.
- `attend?: boolean | undefined`: Plays one attention ring on the boxes as it turns true, while the code is `aria-invalid`. Clear it from `onAnimationEnd` or when the person types.

Also takes the props of `Omit<React.ComponentProps<"input">, "value" | "defaultValue" | "type" | "maxLength" | "onChange">`.

## Types

### OneTimeCodeKind

Which characters a code can hold.

```ts
"numeric" | "alphanumeric"
```

## Motion

- **invalid** (attention): One ring grows and fades around the boxes when `attend` turns on while the code is `aria-invalid`, once. It never plays while the person types; the boxes shake only if the brand draws it as the Pro `jolt` (a locked control's shake is `refuse`). A code that mounts with `attend` set shows nothing until it is set again.

Set with `--cd-motion-one-time-code-<moment>-*` in your brand file; the families are in the design reference.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
