Skip to content
ArcadiaDesign
Questions
Should variance use the population or the sample formula?
Describe a change, @ to add files

TSX

Docked to the composer for questions, approvals, queued messages and the +, / and @ pickers. It sits on --bg-elevated with radius 20 and continuous corners (16 round), a soft shadow and the float edge drawn inside; it slides 4px from the composer and never zooms. Give it a name (aria-label, or aria-labelledby on its title) and a role that fits. TrayTitle takes the props of the <span> it renders.

Tray, Dialog or Confirm Dialog? A Tray asks during work and leaves the page usable. Use a Dialog for a task with its own controls, and a Confirm Dialog only to stop something that can't be undone.

The footer keeps its actions at the end. Actions that don't fit one row wrap under it, still at the end, so in a narrow window the last one (the primary) takes a row of its own and none is pushed past the tray's edge. Its start slot takes a count, a pager or a text button such as Skip: the button's label starts on the tray's text column, under the title, not on its padding.

A notice is a tray of its header alone: a glyph at the text's height, one line, the remedy at its end. Alone, the header is centred in its 40px and its end is a control side, as a footer's.

A tray that asks something docks to a hailing composer: the horizon holds its light while the product waits on you, and the warm answer button reads as part of that light.

Examples

Multiple choice

kind="multiple" options are checkboxes; the footer's start slot holds a count or a pager.

Which checks should run?
Describe a change, @ to add files

A confirmation

A neutral question about the page itself, in a tray instead of a dialog, so the work behind it stays in view. It asks nothing of the agent's turn, so the horizon stays dark.

Discard 3 unsaved changes?
The edits to settings.css and two other files are lost.
Describe a change, @ to add files

Picker

The + menu: ListRows in a tray below the composer, labels only; a disabled row says why. Pickers are static, so they open instantly.

Describe a change, @ to add files

A notice

Its header alone: what the message box holds can't go as it is, and the one move that fixes it.

Large model isn't available: its provider is signed out.
Describe a change, @ to add files

Enter and exit

Pass open to let the tray keep itself mounted for its exit fade. Only event-driven trays animate.

A 120ms slide in, a 90ms fade out
Reduced motion keeps the fades and drops the 4px travel.

rootStyle takes where the tray sits and the knobs below, each a token of its kind.

--tray-bg
PaintDefault var(--bg-elevated)
Surface fill.
--tray-radius
Ref<Radius>Default var(--radius-frame-tray)
Corner radius.
--tray-pad
Ref<Space>Default var(--space-1-5)
Inset around a body of rows (6px). Their corners follow this inset.
--tray-max-h
Ref<Measure>Default min(420px, 50vh)
Tallest height (420, at most half the viewport); the body scrolls.
--tray-px
Ref<Space> | DerivedDefault calc(var(--tray-pad) + var(--space-1) + (var(--control-xs) - var(--icon-md)) / 2)
Inline padding of the header, the footer's start and a text body: the rows' first ink (the pad plus a row's text side, 12), so a heading sits on the text it labels (R3).

--tray-pad sets the row inset. Both TrayOption and ListRow corners follow the tray radius minus that inset.

TrayOption supplies role="radio" or "checkbox" with aria-checked; the surrounding body owns the group role, as above (radiogroup, group). A tray is not modal: focus stays where it was when one arrives, so announce a new question with a live region or move focus to it on purpose.

The keys belong to the flow around the tray. A question flow should handle these:

1 to 9
Pick an option by its number
↓↑
Move between options
Space
Toggle a checkbox option
Enter
Submit the answer

Tray

placement
"above" | "below"Default "above"
Which side of the composer it docks to. above rises from below; below drops from above.
motion
"enter" | "static"Default "enter"
enter: 120ms rise + fade on mount, 90ms fade on close. Only event-driven trays animate (a question or approval arriving, a message queued). static: appears and leaves instantly: hydrated trays and the +, / and @ pickers, which open at once.
open
boolean
Omit to mount and unmount the tray yourself (no exit motion). Pass it to let the tray keep itself mounted for its exit fade (data-exiting, inert) and unmount afterwards.
rootStyle
RootStyle<TrayKnobs>
Typed StyleX override: where the tray sits (layout) and its --tray-* knobs.

Rest props go to <div> props and render.

No props of its own. Rest props go to <div> props and render.

layout
"rows" | "text"Default "rows"
rows: ListRows / TrayOptions inset 6px. text: prose aligned with the header text.
glide
booleanDefault false
One highlight that glides between the rows (a picker's +, / or @ list); see Glide.

Rest props go to <div> props and render.

divided
booleanDefault false
Hairline above the footer.
start
ReactNode
Left-aligned group (a pager, a hint, a text button on the text column); the children stay at the end.

Rest props go to <div> props and render.

kind
"single" | "multiple"Default "single"
single: a lettered/numbered badge, role="radio". multiple: a checkbox, role="checkbox".
badge
ReactNode
Badge content for single ("1", "A", or an Icon for "Other").
selected
booleanDefault false
label
ReactNode
The option text (or pass it as children).
description
ReactNode
Quieter text after the label (text-3).
disabled
booleanDefault false
Text-4, not selectable.

Rest props go to <button> props and render.