List
Stacks rows of the same shape vertically. Use it for navigation, settings, search results: anything that repeats.
import { List, ListItem } from 'neba';
<List>
<ListItem startIcon={<GlobeIcon />} description="Deployed 4 minutes ago" onClick={open} selected>
production
</ListItem>
<ListItem startIcon={<GlobeIcon />} description="Deployed 2 hours ago" onClick={open}>
staging
</ListItem>
</List>;Props
List
| Prop | Type | Default | Description |
|---|---|---|---|
| variantshared | 'solid' | 'outline' | 'text' | 'outline' | Weight of the surface. The sheet is never dyed, as on Box. Reach for text inside a Card: the card is already a sheet |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The rows' type scale and padding. An axis of the list, not of any one row |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. Arbitrary colour values are not accepted |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| elevationshared | 0 | 1 | 2 | 3 | 0 | Drop shadow depth. 0 means no shadow at all |
| dividers | boolean | false | Separates the rows with a hairline instead of space. It changes more than it sounds like: the rules have to reach both edges, so the list gives up its inner padding and the rows give up their corners |
| render | useRender.RenderProp | — | Renders something other than a ul: render={<ol />} when the order is the point |
| children | ReactNode | — | The ListItems |
size and density are set on List only. They reach every ListItem through a context, so there is no need to repeat them per row.
ListItem
| Prop | Type | Default | Description |
|---|---|---|---|
| startIcon | ReactNode | — | Content before the label: an icon, an avatar, a status dot |
| endIcon | ReactNode | — | Content after the label, inside the pressable area |
| description | ReactNode | — | A second line under the label |
| action | ReactNode | — | A control pinned to the end of the row. Deliberately outside the pressable area: a button inside a button is markup the browser rewrites on parse |
| onClick | (event) => void | — | Passing it is what turns the row into a real button |
| href | string | — | Passing it is what turns the row into a real link |
| selected | boolean | false | The chosen row: the open page, the current filter. A link gets aria-current="page" and a button aria-current="true" |
| disabled | boolean | false | Unavailable. Drops the colour family for neutral grey |
| children | ReactNode | — | The label |
Examples
dividers
dividers draws a rule between rows. The rules have to reach both edges of the sheet, so the list's inner padding and the rows' rounded corners go with them: rows become ruled lines rather than floating tiles.
variant
Use variant="text" inside a Card. The card is already a sheet, so the borders do not double up.
onClick · href · action
onClick or href makes the whole row a <button> or an <a> respectively. action is a separate control slot outside that pressable area: for a row that navigates when pressed but also carries a switch of its own.
Accessibility
Listsetsrole="list"explicitly, because Tailwind's reset removes the bullets from<ul>and Safari drops the list semantics with them.- A
ListItemshell is always an<li>, with a<button>or an<a>inside it depending ononClickandhref. selectedputsaria-current="page"on a link andaria-current="true"on a button. Notaria-pressed: a selected row is not a toggle.