Codes and secrets
Show a short code on one device, type it on another, lay out a long secret, and copy that tells the truth.
import { CodeInput, CodeDisplay, KeyBlock and 1 more } from "@arcadia/ui"CodeDisplay shows a short code on the device that asks; CodeInput takes it on the device that answers. The groups are spaced by a gap, never a character, so a copied code pastes cleanly. One real field sits under the boxes: paste, autofill of one-time codes and undo behave as in any input, and onComplete fires on the last character.
Secrets
KeyBlock sets a long secret on one line, with nothing drawn around it.
7K2M
actions are icon buttons at the end of its first line; the screen's primary action stays in the screen's footer. onCopied hears a copy the person makes themselves (Cmd+C, the Edit menu's Copy, the context menu) when the selection holds the whole key.
7K2M
Where one line does not fit, the key folds after perLine groups, never inside one. Each dash leads its group, so a fold carries it to the next line's start, where the key's edge hides it: no line ends on a dash that reads as a hyphen, and a selection still copies every dash.
Copy
CopyButton writes through copyText and shows the check only when the text really landed.
A product with a native clipboard registers it once with setClipboardBridge; everywhere else the browser's clipboard runs, then a hidden-field copy. clearAfterMs asks a native bridge to clear a secret it still holds.
CodeInput is one labelled text field (autocomplete="one-time-code"), so screen readers and password managers see a normal input; the boxes are decorative. invalid sets aria-invalid and the shake honours reduced motion. CodeDisplay and KeyBlock are named groups. CopyButton announces "Copied" or "Couldn't copy" in a polite live region.
CodeInput
length- numberDefault 6
- Number of boxes.
kind- "numeric" | "alphanumeric"Default "numeric"
numerictakes digits only;alphanumerictakes letters and digits, upper-cased.value- string
- Controlled value (characters only, no separators).
defaultValue- stringDefault ""
- Initial value when uncontrolled.
onValueChange- (value: string) => void
- Every change, with the characters typed so far.
onComplete- (value: string) => void
- Called once the last box is filled (typed, pasted or autofilled).
invalid- booleanDefault false
- Draws the boxes in danger and, unless reduced motion is on, shakes them once each time it turns true.
disabled- booleanDefault false
autoFocus- booleanDefault false
group- numberDefault 3
- Group split: a wider gap after every
groupboxes. 0 for none. labelrequired- string
- The accessible name ("Code from MacBook Air").
name- string
- Form field name.
rootStyle- RootStyle
- Typed StyleX override: layout only.
coderequired- string
- The code, without separators ("482913").
group- numberDefault 3
- Characters per visual group (a gap, never a character, separates them).
size- "md" | "lg"Default "lg"
lgfor the one code a screen is about;mdinside a row or a sheet.label- stringDefault "Code"
- What the code is, for assistive tech ("Code"): the group's name, never drawn.
rootStyle- RootStyle
- Typed StyleX override: layout only.
valuerequired- string
- The secret as it is written, groups joined by
-("ABCD-EFGH-JKLM"). perLine- numberDefault 4
- Groups per line once the key no longer fits on one. Default 4: a 7-group key folds into 4 and 3.
labelrequired- string
- What the secret is, for assistive tech ("Recovery Key").
actions- ReactNode
- Quiet actions at the key's end: icon buttons (Copy, Print), never the screen's primary action.
onCopied- () => void
- A copy (Cmd+C, Edit > Copy, the context menu) whose selection holds the whole key.
rootStyle- RootStyle
- Typed StyleX override: layout only.
textrequired- string | (() => string)
- The text written to the clipboard (or a function that produces it at click time).
label- stringDefault "Copy"
- The label at rest.
copiedLabel- stringDefault "Copied"
- The label for COPIED_HOLD_MS after a copy that landed.
failedLabel- stringDefault "Couldn't copy"
- The label after a copy the clipboard refused.
iconOnly- booleanDefault false
- Draw only the glyph (an IconButton named by
label). clipboard- ClipboardWriteOptions
- Clipboard options:
clearAfterMsfor a secret. onCopied- (ok: boolean) => void
- Called after every copy with whether it landed.
variant- "primary" | "secondary" | "outline" | "ghost" | "text" | "danger"Default "secondary"
primaryis the monochrome ink CTA, for the one action that moves the flow forward (send).size- "xs" | "sm" | "md" | "lg"Default "sm"
- Heights 20 / 24 / 28 / 32.
shape- "rounded" | "pill"
tone- "neutral" | "plan"
planis Plan mode's distance blue: a blue CTA onprimary, blue text elsewhere. There is no amber tone: the sodium light is for literal lights (a running edge, a needs-you dot), never a control's fill.iconEnd- IconName
hint- ShortcutSpec | ReactElement
- Trailing key hint, drawn quietly:
"⇧Tab","⏎",["⌘","K"],"mod+k", or aKbdGroup. Visual only (aria-hidden, so the name stays "Allow", not "Allow ⏎"); the keys are exposed asaria-keyshortcutson the button. Binding the key is yours. progress- number
- Determinate progress, 0..1: a ring fills in the icon's slot (a download, an upload) while the label says the number. The button stays pressable;
loadingwins when both are set. pressed- boolean
- Toggle state: sets
aria-pressedanddata-pressed. Omit for a plain action button. labelRow- boolean
- The label is a row of parts (an avatar beside words), each on its capitals.
rootStyle- ButtonStyle
- Typed StyleX override: where the button sits (margins, self-alignment, flex and grid placement, size bounds) and its
--button-*knobs. render- RenderProp<{ disabled: boolean }>
- Replace the rendered element, e.g.
render={<a href="/settings" />}, or a function of props and state. disabled- boolean
- Native disabled: no pointer events, no tooltip, skipped by Tab.
focusableWhenDisabled- boolean
- Keep a disabled control focusable and hoverable (
aria-disabled), so a tooltip can say why. nativeButton- boolean
- Button semantics, inferred from what renders: a
<button>is native, an<a>stays a link, anything else getsrole="button"and key handling.falseforces button semantics on whatever renders (a link acting as a button). className- string
- A product's hook class for its own layout rules. Being retired: new code places a button with
rootStyle; the call sites left are counted by ui.lint (kit-classname) and only go down.
Rest props go to <button> props.