/// Data Display
ListCell
Composable list row: leading slot, title/subtitle, trailing controls, optional selection and whole-row link.
import { ListCell } from "@protocore/pds";Basics
A leading slot (avatar / icon / thumbnail), a title + optional subtitle, and a trailing slot for controls or meta. Rows share a hairline divider and stack straight into a list.
Selectable
Set selectable to add a leading Checkbox. It's controllable via selected / defaultSelected / onSelectedChange, and a selected row tints with the accent-muted wash.
Whole-row link
Give the row an href and the title becomes a stretched anchor that covers the whole surface — one real link, keyboard- and screen-reader-correct. Trailing controls stay clickable above it. Without href, the row shows no pointer or hover affordance (§7.2).
When to use it
ListCell is the general-purpose row for settings lists, member directories, file browsers, and pickers — anywhere each item pairs an identity with controls or navigation. It composes with Persona, Avatar, Badge, and IconButton in its slots.
When the row is a flat, mono, column-aligned record (careers, changelogs, endpoint directories), use ListingRow. When you need sorting, a header, or pagination, graduate to DataTable.
Usage
Do
- Use `href` for navigating rows — it stretches one anchor over the surface.
- Keep trailing controls to a small, consistent set across the list.
- Pair `selectable` with a controlled `selected` for bulk-selection UIs.
- Compose a Persona into `leading`+`title`, or use the slots directly.
Don't
- Don't add a second link inside a row that already has an `href`.
- Don't attach an onClick to fake a link — use `href` for whole-row navigation.
- Don't overload the `trailing` slot; two controls is plenty.
- Don't rely on the selected tint alone — the Checkbox carries the state.
Accessibility
| Keys | Action |
|---|---|
| Tab | Moves focus to the row link (when href) and to the Checkbox / trailing controls. |
| Enter | Follows the row link (native anchor). |
| Space | Toggles the selection Checkbox when focused. |
- The whole-row link is a single stretched anchor named by the `title` — no nested clickables.
- The selection Checkbox is labelled by `selectLabel` (default "Select item").
- Trailing controls and the Checkbox sit above the stretched link so they remain independently operable.
- ListCell imposes no list role — provide `role="listitem"` / `role="option"` from the parent context.
ListCell props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | |
defaultSelected | boolean | false | Uncontrolled initial selected state. |
href | string | — | When set, the whole row becomes a single link: a stretched anchor covers the surface (keyboard- and screen-reader-correct), while `trailing`/Checkbox stay clickable above it. Without it the row renders no pointer or hover affordance. |
leading | ReactNode | — | Leading slot — an Avatar, Icon, thumbnail, or any mark. |
onSelectedChange | ((selected: boolean) => void) | — | Fires when the selection Checkbox toggles. |
selectable | boolean | false | Show a leading selection Checkbox. |
selected | boolean | — | Controlled selected state (pairs with `onSelectedChange`). |
selectLabel | string | Select item | Accessible label for the selection Checkbox. |
size | enum | md | Row density. |
style | CSSProperties | — | |
subtitle | ReactNode | — | Optional muted second line. |
title * | ReactNode | — | Primary line, rendered in ink. When `href` is set this becomes the row link. |
trailing | ReactNode | — | Trailing slot — a control, badge, menu trigger, or meta. Stays clickable above the row link. |