On this page
Import
import { List } from '@envsemble/envui';
Usage
List.Root is a ul of List.Item rows, each with its label, a description below it, one short fact, content at its end, such as a Button, and children after the description, such as a Badge. start holds content before the label, such as an Avatar, an icon or a Checkbox, in a slot at least an icon wide, so a narrower mark, such as a Spinner, keeps its label in line with its siblings'. Name the list with aria-label where its heading doesn't.
import { Button, List } from '@envsemble/envui';
export default function SettingsRows() {
return (
<List.Root aria-label="Workspace settings">
<List.Item
label="Notifications"
description="E-mail and in-app alerts"
end={<Button variant="outline">Manage</Button>}
/>
<List.Item
label="Members"
description="12 people · 3 pending invitations"
end={<Button variant="outline">Manage</Button>}
/>
<List.Item label="Plan" description="Team, renews on 1 December" />
</List.Root>
);
}Rows that open something
With interactive, a row reacts to hover, keyboard focus inside it and presses. Mark its one link or button data-row-target, which stretches it over the row; give a link draggable={false}, so a press that moves a little doesn't drag it. The row's other controls take position: relative to stay above it. While the target has keyboard focus, its ring traces the row's edge, the area a press follows.
<List.Item
interactive
label={
<Link href="/plans/roadmap" underline={false} data-row-target draggable={false}>
Roadmap
</Link>
}
description="Updated today by Sam Lee"
end={
<IconButton
variant="text"
icon={<IconMoreHoriz />}
aria-label="More actions for Roadmap"
style={{ position: 'relative' }}
/>
}
/>
The current and checked rows
current marks the current row, such as the one whose details show beside the list, with the selected fill and aria-current="true" on its data-row-target, or on the row without one. A row whose start checkbox is checked takes the selected fill too; name each checkbox after its row.
<List.Item interactive current={id === shown} label="Roadmap" />
Put in order
onReorder on List.Root gives each row a handle, moved by dragging it, by pressing it or by keyboard, and calls it with the row's index and the index it is dropped at. The application moves the item in its own state; nothing moves until it does. The rows are the list's children, directly or in arrays and fragments: List.Item elements, or elements of a row component that passes the props it doesn't use on to its List.Item. textValue names a row whose label isn't plain text, for its handle and the announcements.
<List.Root
aria-label="Agenda"
onReorder={(from, to) =>
setItems((list) => {
const next = [...list];
const [item] = next.splice(from, 1);
next.splice(to, 0, item!);
return next;
})
}
>
{items.map(({ id, title, length }) => (
<List.Item key={id} label={title} description={length} />
))}
</List.Root>
Density and panes
density is xs, sm, md, the default, or lg, and changes only the rows' block padding. Put a list directly in <Surface background="muted" padding={3}>, a muted Surface that holds only lists, and its rows take the raised fill.
<Surface background="muted" padding={3}>
<List.Root aria-label="Notifications" density="sm">
…
</List.Root>
</Surface>
Composition
The parts are the list, env-list, each row, env-list-item, with its handle, env-list-item-handle, its start, env-list-item-start, its content, env-list-item-content, which holds the label and the description, and its action at the end. Rows render on the server; only the handle of a reorderable row needs the browser.
Accessibility
You provide
- A name for the list where its heading doesn't give one.
- One link or button marked
data-row-targetin an interactive row, withdraggable={false}on a link. - Names for a start checkbox and for actions, which say which row they act on.
textValueon a reorderable row whose label isn't plain text, and the move inonReorder.
envUI provides
- A native
uloflirows, announced with their count. aria-current="true"on the current row's target.- Handles named Reorder and the row's label, described by how to move without a pointer.
- A polite announcement of each step of a move, with the position and the count.
Props
List.Root
A `ul` of connected rows with shared outer corners. In a muted Surface that holds only lists, such as `<Surface background="muted" padding={3}>`, the rows take the raised fill, so they stand out from it. With onReorder, people move the rows by their handles.
| Prop | Type | Description |
|---|---|---|
densityDefault 'md' | 'xs''sm''md''lg' | How tightly the rows pack: xs, sm, md or lg changes only their block padding, never the text. |
onReorder | ( | Makes the rows reorderable: each row gets a drag handle at its start, moved by dragging it, by pressing it or by keyboard, and the list calls this with the row's index and the index it is dropped at. The application moves the item in its own state; nothing moves until it does. The rows are the list's children, directly or in arrays and fragments: List.Item elements, or elements of a row component that passes the props it doesn't use on to its List.Item, such as `function SlideRow({ slide, ...props }) { return <List.Item {...props} label={slide} />; }`. |
skeletonDefault false | boolean | Shows it and its content as a busy skeleton while that loads; turning it on or off renders the content anew. |
Also accepts the native props of <ul>, such as ref, className and aria-* attributes.
List.Item
One `li` row with a label, a description and content at its end; the current row, content at its start, and in a reorderable list a drag handle. It renders on the server: only the handle of a reorderable row needs the browser.
| Prop | Type | Description |
|---|---|---|
labelrequired | ReactNode | The row's main text, such as an item's name; a link marked `data-row-target` in an interactive row. Undefined, such as over a placeholder item, a skeleton shows a bar in its place. |
description | ReactNode | A short fact in muted text below the label, such as who changed the item and when. Given as undefined, such as over a placeholder item, a skeleton shows a bar in its place. |
end | ReactNode | Content at the end of the row, such as a Button or a Switch; it moves below the text when it doesn't fit. |
children | ReactNode | More content after the label and the description, such as a Badge. |
interactiveDefault false | boolean | The row opens something through a link or button inside: it reacts to hover, keyboard focus inside it and presses. Mark that link or button `data-row-target` to stretch it over the row. |
currentDefault false | boolean | The current row, such as the one whose details show beside a list: the selected fill, and `aria-current="true"` on its `data-row-target` element in the label, description or children, or on the row without one. |
start | ReactNode | Content before the label, such as an Avatar, an icon or a Checkbox for choosing several rows; a row whose checkbox is checked takes the selected fill. |
textValueDefault the label's text | string | The row's label as plain text, for its drag handle's name and the reorder announcements where `label` isn't a string. |
skeletonDefault false | boolean | Shows it and its content as a busy skeleton while that loads; turning it on or off renders the content anew. |
Also accepts the native props of <li>, such as ref, className and aria-* attributes.
Part names
Each part's class and state attributes are public and versioned, for an application's CSS to style.
env-list- The list.
env-list-announcer- The announcements of a reordered item.
env-list-item- One item.
env-list-item-content- An item's content.
env-list-item-handle- The handle that reorders an item.
env-list-item-start- The start of an item, for the handle or a checkbox.
data-densitylg·md·sm·xsdata-env-skeleton- Any value, or present
data-interactive- Present on env-list-item
data-label- Any value, or present on env-list-item-handle
data-movingkeyboard·pointeron env-list-itemdata-reorderable- Present
data-reordering- Present
data-selected- Present on env-list-item
data-start- Present on env-list-item
Color properties
Each sets a color of one instance, through style or CSS; its hover, focus and press follow it. They are public and versioned.
--env-list-item-container- The row's fill; the state layers lie over it.
--env-list-item-content- The text, and the color of the state layers.
--env-list-item-selected- The fill of the current row and of a row whose checkbox is checked.
--env-list-item-selected-content- The text of a selected row.
Strings
EnvUIProvider's messages overrides each, and a component's own label prop wins over it.
messages.list.reorder({ label }) => stringA row's drag handle, such as Reorder Intro slide.messages.list.instructions"Press Space or Enter, or click, to pick the row up. Then use the arrow keys to move it, and press Space or Enter, or click, to drop it. Escape cancels."Describes each drag handle: how to move its row without a pointer.messages.list.lifted({ label, position, count }) => stringAnnounced when a row is picked up by keyboard or a press of its handle; the handle's description already says how to move it.messages.list.liftedByPointer({ label, position, count }) => stringAnnounced when the pointer starts dragging a row.messages.list.moved({ label, position, count }) => stringAnnounced as a picked-up row moves.messages.list.atStart({ label, position, count }) => stringAnnounced when a row can't move further up: it is first.messages.list.atEnd({ label, position, count }) => stringAnnounced when a row can't move further down: it is last.messages.list.dropped({ label, position, count }) => stringAnnounced when a row is dropped in its new place.messages.list.cancelled({ label, position, count }) => stringAnnounced when a move is cancelled and the row goes back.
Changelog
- 1.0.0
List.Item takes
currentfor the current row, marked in the server's HTML,startfor content before the label, such as an avatar or a checkbox, andtextValue; List.Root takesonReorder, which moves rows by a drag handle, by keyboard, by a press and by pointer, with announcements.GroupedRowsisList.RootandRowisList.Item, with theenv-listandenv-list-itemclasses; the page islist. List.Item'stitleislabel, itsdetailisdescriptionand itsactionisend, as on CommandPalette's items and Button. List.Root'scontrastis gone: put the list directly in<Surface background="muted" padding={3}>, where its rows take the raised fill.- 1.0.0-prerelease.6
A pressed row's link or button,
data-row-target, lays no pressed fill of its own: the row shows the press.Colors under the pointer and while pressed change with
--env-motion-ease-enter, rather than easing in slowly, so hover and press show from the first frame.In an interactive row the focus ring of the
data-row-targetlink or button traces the row, not the label alone.The start slot is at least an icon wide, with its content centred, so a Spinner among icons keeps its label in line.
- 1.0.0-prerelease.5
List.Item's
selectediscurrent, as it marks the current row witharia-current;selectedmeans chosen elsewhere.