Text and fields
Code Field
Enter a one-time code, one character per cell.
import { CodeField } from "macbetter/code-field"
import { Field, FieldDescription, FieldLabel } from "macbetter/field"
export default function Example() {
return (
<Field name="code">
<FieldLabel>Verification code</FieldLabel>
<CodeField />
<FieldDescription>Enter the code sent to your iPhone.</FieldDescription>
</Field>
)
}When to use it
Use a code field for a short code sent by text or email: a sign-in code, a two-factor code, a device pairing code. Each character gets its own cell, but it's one input, so a pasted or autofilled code fills every cell at once and a phone can offer the code from a message.
Say where the code was sent in the description. When the code is wrong or expired, mark the Field invalid and say what to do next.
Usage
import { CodeField } from "macbetter/code-field"length sets how many cells it draws (six by default). onValueCommit runs once every cell is filled, the moment to verify the code.
Groups, letters and errors
groupSize splits the cells into runs with a dash between, like 3 for "123–456". pattern accepts "digits" (the default), "letters", "alphanumeric" or a regular expression, and picks the matching touch keyboard.
import { CodeField } from "macbetter/code-field"
import { Field, FieldError, FieldLabel } from "macbetter/field"
export default function Example() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 20, maxWidth: "100%" }}>
<Field name="recovery">
<FieldLabel>Recovery key</FieldLabel>
<CodeField length={8} groupSize={4} pattern="alphanumeric" defaultValue="7HQ2VX" />
</Field>
<Field name="expired" invalid>
<FieldLabel>Sign-in code</FieldLabel>
<CodeField length={6} groupSize={3} defaultValue="482913" />
<FieldError match>That code has expired. Request a new one.</FieldError>
</Field>
</div>
)
}To lay the cells out yourself, pass CodeFieldGroup, CodeFieldSlot and CodeFieldSeparator as children.
Platform behavior
- macOS. Cells look like small text fields: 34 by 42 points, bordered, with 22-point digits. The active cell takes an accent edge and the focus halo.
- iPadOS and iOS. Gray filled cells, 46 by 58 points with 12-point corners and 28-point semibold digits, as in the system's verification screens. The active cell takes a 2-point accent edge.
- Every platform. When the row is wider than its container, as with a long code on a phone, the cells narrow together and keep their shape. The empty active cell shows an accent caret that blinks like the system's, and holds still under Reduce Motion. A typed digit settles into its cell with a small spring.
Accessibility
- It's one native input with
autocomplete="one-time-code", so autofill, paste and undo work, and screen readers read it as a single text field. - The cells are hidden from assistive technology; the Field label names the input.
- Arrow keys move between characters, and clicking a cell selects it.
API
CodeField
| Prop | Type | Default | Description |
|---|---|---|---|
length | number | 6 | How many characters, one cell each. |
groupSize | number | — | Splits the cells into runs of this many with a dash between. |
size | "md" | "lg" | "md" | Cell size. |
pattern | "digits" | "letters" | "alphanumeric" | RegExp | "digits" | Which characters it accepts. |
value | string | — | The code, for a controlled field. |
onValueChange | (value, details) => void | — | Runs on typing, paste, undo or a controller. |
onValueCommit | (value, details) => void | — | Runs when every cell is filled. |
name | string | — | The name the code submits under. |
CodeFieldGroup, CodeFieldSlot (with its index) and CodeFieldSeparator take the props of a div.
Data attributes
| Attribute | On | Values |
|---|---|---|
data-size | CodeField | md, lg |
data-active | Slot | The cell typing goes to, while focused |
data-fake-caret | Slot | The active cell while it's empty |
data-filled | Root, slot | Present when it holds a character |
data-complete | Root | Present while every cell is filled |
CSS variables
| Variable | macOS | iOS | What it sets |
|---|---|---|---|
--mb-code-field-cell-width | 34px | 46px | Cell width |
--mb-code-field-cell-ratio | 34 / 42 | 46 / 58 | Cell width to height; cells keep it as they narrow to fit |
--mb-code-field-cell-radius | 8px | 12px | Cell corners |
--mb-code-field-cell-bg | Field white | Tertiary fill | Cell background |
--mb-code-field-font | 22px medium | 28px semibold | Digit type |