Skip to content
ArcadiaDesign

TSX

The one row anatomy: sidebar navigation, chats and projects, settings navigation, palette results, the files in a changes card and tray pickers. A 20×20 leading slot, the label, an optional description, meta at the end, and actions revealed on hover that take the meta's place in the same slot. A hover fill comes in at once and trails off over --duration-fast when the pointer leaves; the current row's pill and a keyboard highlight change in one frame, both ways. A pressable product tile takes the kit's press and sets down the same way. An icon in the leading slot sits beside the label: framed and stroked by its text, centred in the slot. The current row is marked by its fill and ink, never by the glyph.

Examples

Palette rows

emphasis puts the label at text-1 beside a description.

highlighted is the keyboard highlight a list drives from elsewhere, such as a palette's search field. A description that says why a row can't act now ("Unavailable while running") carries state: descriptionYields={false} keeps its words while the label steps down, and it steps down itself only beside the label's shortest form.

A label beside a description

Neither loses a letter.

The description gives way first: to its shorter forms (whole words, or a path's last whole folders, asideForms), then whole. Then the label ends on a whole word, or on a name boundary for a path or a single token (textForms, every label's default). The meta keeps its seat, and on hover the row's tooltip says both whole. Narrow the room to see the folder step down and go, then the name step down.

A file row

The rule above, for a file: forms hands the label its shorter forms, here nameForms, which cuts a file name at /, -, _, ., a space or a camelCase hump, so it ends on a part of the name, never inside one.

A title holds the full path, and the meta keeps its seat.

A meta that gives way

A row ranks its label over its meta.

A label that fits keeps its time; one that would be cut takes the time's room, and one longer still ends on a whole word (whole sentences, then clauses, then words, sentenceForms), its last word dimmed by the fade, never cut through. Hover or focus a long one: the time comes back and the label steps down to the words that fit beside it. The row marks its label data-fit="primary" and its meta data-fit="meta", which the layout check holds; metaYields={false} keeps the meta's seat.

Inline rename

Double-click to rename: editing turns the row into a static container for a ListRowEdit. Enter or blur commits; Esc cancels.

You are here

The active row is a flat fill-3 pill with its label in text-1: no lift and no shadow.

sidebar.css

Hover is fill-5 and a keyboard highlight fill-4, so a hovered row never reads as selected. In a list with a Glide, the glide draws hover and highlight and the row draws only this.

People

size="person" is a row for someone you talk to: a 36px avatar in leading, the name over one sub line (the last thing said, or what they are doing now; 52px, and 40px with no line, the name centred on the avatar), aside at the end of the name's line and meta in a seat at the row's end.

A name that would be cut takes the aside's room until hover or focus; the current row, or one holding a dot or a mark (a wait's reset, which never yields), keeps it. subTone="attention" says they wait for you. Pressed, the row sets down a little.

Text in a box of your own

The label, a description and a string meta sit on their capitals, so their middles meet the row's.

sidebar.css

Text you draw in a box of your own, such as a time in a fixed-width column, takes the trim the row hands down, --caps-trim and --caps-pad, or it sits a device pixel under the label beside it.

Loading

A Skeleton in the label keeps the row's height while its data arrives.

Size and state

rootStyle takes where the row sits and these knobs, each a token of its kind. The states read their own values (the current row is text-1 over --row-fill-active, a disabled one text-4), so a colour you set never drops a state.

--row-h
Ref<Control | Measure>Default var(--control-md)
Minimum height.
--row-px
Ref<Space>Default var(--space-1)
Inline padding on the icon side: the leading slot's edge (its glyph's ink sits 2px further in).
--row-px-text
Ref<Space> | DerivedDefault calc(var(--row-px) + (var(--control-xs) - var(--icon-md)) / 2)
Inline padding on a text side: the icon side plus the slot's inset (R3).
--row-indent
Ref<Space> | "0px"Default 0px
A nested row's extra start padding; the fill starts after it.
--row-gap
Ref<Space>Default var(--space-1-5)
Gap between leading, label, description and meta.
--row-radius
Ref<Radius> | DerivedDefault var(--radius-row)
Corner radius of the fill.
--row-fill
PaintDefault transparent
Fill at rest.
--row-fill-hover
PaintDefault var(--fill-5)
Fill under the pointer (and while the row's actions have a menu open).
--row-fill-highlight
PaintDefault var(--fill-4)
Fill of the keyboard highlight a list drives from elsewhere.
--row-fill-active
PaintDefault var(--fill-3)
Fill of the current row.
--row-shadow
Ref<Shadow> | "none"Default none
Shadow at rest.
--row-shadow-active
Ref<Shadow> | "none"Default none
Shadow of the current row.
--row-fg
PaintDefault var(--text-2)
Label colour (the current row is text-1, a disabled one text-4).
--row-icon
PaintDefault var(--icon-2)
Leading glyph colour (the current row is icon-1, a disabled one icon-4).
--row-description
PaintDefault var(--text-3)
Description colour.
--row-meta
PaintDefault var(--text-3)
Meta colour.
--row-cursor
"pointer" | "default"Default pointer
default on options the keyboard leads (a command palette).
--glide-fill
PaintDefault var(--fill-4)
The Glide's fill on this row under the pointer.
--glide-fill-kbd
PaintDefault var(--fill-3)
The Glide's fill on this row under the keyboard.

Without actions the row is one control: a <button>, or your element through render, such as a link. With actions it becomes a container holding the control and the action buttons side by side, so no button nests inside another. Set aria-current yourself on the row for the current page. A label or description that fades is still read whole.

Actions show while the row has keyboard focus, so Tab reaches them right after it; Shift+Tab from the row below skips them while they are hidden. Keep every action in the row's menu too, so a touch screen, where nothing hovers, can reach it.

EnterSpace
Activates the row
Enter
Commits a rename
Esc
Cancels a rename

ListRow

size
"sm" | "md" | "lg" | "person"Default "md"
Heights 24 / 28 / 30 (the sidebar row); person, 52px (40px with no sub).
leading
IconName | ReactNode
20×20 leading slot: an icon name, drawn beside the label, or a node (StatusDot, PineSweep). A person row's is its 36×36 avatar.
reserveLeading
boolean
Keep the empty leading slot's width, so the label lines up with its siblings.
label
ReactNode
The label (or pass it as children). Text ends on a whole word; a Skeleton here keeps the row height.
labelId
string
forms
readonly (ReactNode | { cut: ReactNode })[]
The label's shorter forms (default textForms; a file name nameForms).
description
ReactNode
Text-3 after the label: it steps down, then goes, before the label loses a word; the hover says both whole.
descriptionForms
readonly (ReactNode | { cut: ReactNode })[]Default asideForms
Its shorter forms.
descriptionYields
boolean
false: it carries state ("Unavailable while running") and keeps its seat while the label steps down.
apart
number
Beside a state description: its forms that tell it from its list's rows (apartForms).
full
false
false: the row's own Tooltip says the whole text.
sub
ReactNode
person rows: one line under the name, text-3: the last thing said, or what they are doing now.
subTone
"neutral" | "attention"Default "neutral"
person rows: attention draws sub in the warning ink (they wait for you).
aside
ReactNode
person rows: a time ending the name's line; it yields to the name as a meta does.
meta
ReactNode
End slot (a time, a shortcut Badge, diff stats), hidden while actions show; never in the row's name: a shortcut (Kbd, KbdGroup, a Badge, "⌘N") becomes its aria-keyshortcuts, anything else its description.
metaOnHover
boolean
Quiet meta: hidden until hover or keyboard focus (a nav row's ⌘N badge).
metaYields
boolean
The meta gives way to a label or description it would shorten until hover, focus or highlighted (default; false keeps its seat). Not on a person row.
mark
ReactNode
A mark that never yields (a highlight) after the label, at the row's end after a cut one; a person row's ends its name's line.
actions
ReactNode
Hover-revealed end actions (IconButton size="xs" tone="muted") in meta's slot; a destructive one goes in the row's menu. In a tree or listbox: aria-hidden, keyboard commands on the row.
actionsOpen
boolean
Keep actions and the hover fill while one's menu is open.
active
booleanDefault false
Current item (route, open chat): fill + text-1. Set aria-current yourself where it applies.
highlighted
booleanDefault false
Keyboard highlight in a list driven from elsewhere (palette, tray with focus in a search box).
tone
"neutral" | "muted"Default "neutral"
muted: text-3 rows ("More (4)", "No chats yet"). neutral: text-2.
emphasis
boolean
Label at text-1 (tray and palette rows with a description).
editing
booleanDefault false
Inline rename: the row stops being a button so a ListRowEdit can sit in the label slot.
interactive
booleanDefault true
False renders a static row (a div, no hover fill): read-only lists, placeholders.
rootStyle
ListRowStyle
Typed StyleX override: where the row sits and its --row-* knobs.
className
string
A product's hook class, retiring: new code uses rootStyle (ui.lint kit-classname counts the rest).
disabled
booleanDefault false
Text-4, no hover fill, not clickable.
render
ReactElement | (props, state) => ReactElement
Replace the rendered element, e.g. render={<a href="/settings" />}.

Rest props go to <button> props and render.

onCommit
(value: string) => void
Enter or blur: the trimmed-or-not value is the caller's call.
onCancel
() => void
Escape: revert.
selectOnMount
booleanDefault true
Focus and select the whole value on mount.

Rest props go to <input> props.