Skip to content
ArcadiaDesign
Username
ada

TSX

The home of every form that is not open on a page. The page shows a row with its current value and one button; the button opens this. Good design removes what it can while keeping what it does: three password fields at rest become one button, and the fields appear when someone asks to change it.

onSubmit resolves to close the dialog (the caller toasts in three words or fewer) or rejects. A FormError naming a field puts its message under that field, invalid and focused with its text selected; any other rejection is one line above the footer. A FormField's rule shows only after the field is left once or a submit is tried, never while the first characters are typed.

Examples

In steps

FormStep children are steps of one form, so a password manager sees every field.

Continue runs the step's check; the next step slides in and Cancel reads Back. A FormError with a step goes back to it. Try any current password but correct horse.

Focus goes to the first field on open, and back to the button that opened it on close. Tab runs the fields, then Cancel or Back, then the primary. An error is announced, and the field it names is marked invalid. While a submit is in flight, Escape and a press outside wait for it. Under 480px the footer's buttons take a line each, the primary first; under reduced motion a step changes with a fade.

Enter
Continues, or submits on the last step
Esc
Closes and discards what was typed
Tab
Fields, then Cancel or Back, then the primary

FormDialog

openrequired
boolean
onOpenChangerequired
(open: boolean) => void
titlerequired
ReactNode
description
ReactNode
The consequence of the action, when there is one. A FormStep's own description replaces it on that step.
size
"md" | "lg"Default "md"
lg 480px.
submitLabelrequired
string
The primary's label on the last step ("Change password").
tone
"neutral" | "danger"Default "neutral"
onSubmitrequired
() => Promise<void> | void
Resolves on success (the dialog closes; the caller toasts in three words or fewer). Rejects with a FormError to show its message, on a field and step when it names them; any other rejection is the form's message.
canSubmit
booleanDefault true
The primary is enabled when true.
name
string
The form's name, rendered as data-form on the dialog: what a test or a product rule finds it by.
container
PortalContainer
Where a surface's portal mounts. Default: document.body. Pass a themed subtree to keep its data-theme, or a transformed stage to contain position: fixed overlays (previews).
childrenrequired
ReactNode
Fields (FormField + Input), or FormStep children for a form in steps.

namerequired
string
The name of the input inside: a FormError names it, and its rules wait for it to be left once.
rule
string | null
The message while the value breaks a rule ("At least 12 characters"), else null: shown after the field's first blur or the first submit, never while the first characters are typed.
label
ReactNode
hint
string
Why or how, for a field its label cannot explain: a Hint after the label, and the control's description. At most 160 characters.
description
ReactNode
Help text under the label (text-3, 12/16).
invalid
boolean
disabled
boolean
Disables the control and dims the label.
rootStyle
RootStyle
Typed StyleX override: where the field sits.
className
string
A product's hook class for its own layout rules. Being retired: new code places a field with rootStyle; the call sites left are counted by ui.lint (kit-classname) and only go down.

Rest props go to <div> props.

nextLabel
string
The primary's label on this step (default "Continue"; the last step's is the dialog's submitLabel).
canContinue
booleanDefault true
The primary is enabled when true.
onContinue
() => void | Promise<void>
A check before the next step: throw a FormError to stay with a message. Nothing is sent by default.
description
ReactNode
The dialog's description while this step shows (the consequence of the last step belongs on it).