Skip to content
EspañolEnglish
All components (204)

Selectable list

Choice · SelectableList

What it is and when to use it

A list of options in plain view, not dropped down, to choose one or check several. Each option can carry a description, an icon and a detail on the right (an amount, a date, a counter), and the list can be divided into sections.

When to use it

  • When the options have to be in front of you to compare them: an invoice template, a saved address.
  • selection="multiple" to check several rows of a short list and do something with them.
  • maxHeight when the list is long: the scroll goes inside the list, not on the page.
  • loading while the options arrive: the list is marked busy and a row appears with loadingText ("Loading…"); any that were already there stay.

When not to

  • A long list inside a form: Select, or SearchField if you need to search.
  • Distributing things between two columns: TransferList, which already uses this list internally.
  • Data with columns that are sorted and filtered: Table.

Keyboard: Tab enters the list with the focus on the chosen option (or on the first one). The arrow keys move, skipping disabled ones, Home and End go to the ends and typing jumps to the option that starts with those letters - accents are not needed: "e" finds "Éxito" -. Enter or Space choose; with multiple, they check and uncheck without releasing the others, and with Shift they check the whole span from the last one pressed. Disabled options cannot be chosen.

Accessibility: it is a listbox named like the label, and each option is an option with its chosen state. The description is read as the option's description; the icon is decorative. Without options, it shows the text of empty instead of leaving a blank gap.

States: each row darkens on hover and a little more when pressed; the chosen one has the soft accent background and the check mark is drawn when it is chosen (and erased, faster, when it is released). The keyboard focus is a 2 px ring inside the row, which the list clips when scrolling. Disabled rows are gray and have the not-allowed cursor. Rows that appear (on mount, on filtering) enter with a short fade.

Sizes: sm, md (the default) and lg: rows with a minimum height of 30, 36 or 44 px, with the font of the fields of the same size. With a finger no row goes below 44 px.

Fine-tuning: CSS variables on .amset-list-sel (VirtualList uses the same ones, except the row height, which there is its rowHeight):

  • Measurements: --amset-list-sel-height-row, --amset-list-sel-padding (on the sides), --amset-list-sel-padding-block (top and bottom), --amset-list-sel-slot (between rows), --amset-list-sel-radius (the box) and --amset-list-sel-radius-row.
  • Font and icons: --amset-list-sel-font, --amset-list-sel-description (description, detail and empty state), --amset-list-sel-icon and --amset-list-sel-brand (the check mark).
  • Colors: --amset-list-sel-background, --amset-list-sel-border, --amset-list-sel-hover, --amset-list-sel-selected (background of the chosen one), --amset-list-sel-ink-selected and --amset-list-sel-color-brand.

The chosen mark always takes up space, so the rows do not jump when choosing.

What it looks like

Basic invoiceNo logo12 uses
Full invoiceWith logo and legal footer48 uses
Simplified invoiceFor counter receipts103 uses
2024 modelCan no longer be used
Order 1042Lucía Martín · awaiting shipment189,00 €
Order 1043Javier Ortega · shipped420,00 €
Order 1044Carmen Ruiz · payment pending59,90 €
Order 1045Aitor Etxeberria · shipped1.249,00 €
Order 1046Nerea Sánchez · delivered89,00 €

<SelectableList
  label="Invoice template"
  options={[
    { key: 'basica', label: 'Basic invoice', description: 'No logo', detail: '12 uses' },
    { key: 'completa', label: 'Full invoice', description: 'With logo and legal footer' },
  ]}
  initialValue="completa"
/>
<SelectableList label="Orders to print" selection="multiple" options={pedidos} maxHeight="14rem" />

Props

Import it with import { SelectableList } from '@amset/ui'. Required props are marked.

PropTypeWhat it does
labelrequiredstringWhich list this is. Required: without a name it is just "list".
optionsrequiredreadonly ListOption[] | readonly ListSection[]The options as a flat list, or all of them grouped in sections (with their title).
classNamestringMerged with the library classes; it does not replace them.
emptyReactNodeText when there is nothing. A blank gap looks broken.
initialValuestring | readonly string[]—
loadingbooleanThe options are arriving. The list is marked busy and a `loadingText` row is shown; those that were already there stay (searching for more, refreshing).
loadingTextstringWhat the loading row says. "Loading…" by default.
maxHeightstringMaximum height before scrolling inside the list.
onChange((key: string | null) => void) | ((keys: readonly string[]) => void)—
renderOptionOptionRender<ListOption>Paints the inside of each option: an avatar, two lines, a price. What the screen reader READS is still `label` (and `description`); this is only what is seen.
selection"multiple" | "single"—
size"sm" | "md" | "lg"Row height and font size: `sm` (30 px), `md` (36 px, the default) or `lg` (44 px). With a finger no row goes below 44 px.
valuestring | readonly string[] | null—

It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.

In the test bench

Every state and variant, to try one at a time, on its page at ui.amset.tech: