<!-- Generated by tools/docs.php from docs/src/list-group.html - edit the source, not this file. -->

# List groups

A column of items in one frame - plain rows, or links and buttons to choose from - with the current one and the unavailable ones marked by the same attributes the browser reads.

## Examples

`list-group` on a list, `list-group-item` on each item.

```html
<ul class="list-group">
	<li class="list-group-item">North Bay Marine Park</li>
	<li class="list-group-item">Turtle Cove and Pearl Cove</li>
	<li class="list-group-item">The Five Islands by kayak</li>
</ul>
```

### Links and buttons

`list-group-item-action` makes an item a choice. The current one is `aria-current` - `"page"` for a link to the page shown, `"true"` otherwise; one that cannot be chosen is a `disabled` button, or a link without `href` and with `aria-disabled="true"`.

```html
<nav class="list-group" aria-label="Sites">
	<a class="list-group-item list-group-item-action" href="#list-group-action" aria-current="page">All sites</a>
	<a class="list-group-item list-group-item-action" href="#list-group-action">lagoon.example</a>
	<a class="list-group-item list-group-item-action" href="#list-group-action">northbay.example</a>
	<a class="list-group-item list-group-item-action" aria-disabled="true">archive.example</a>
</nav>
<div class="list-group" role="group" aria-label="Import from">
	<button class="list-group-item list-group-item-action" type="button">A CSV file</button>
	<button class="list-group-item list-group-item-action" type="button">Another site</button>
	<button class="list-group-item list-group-item-action" type="button" disabled>The old catalog</button>
</div>
```

### Flush, numbered, with counts

`list-group-flush` drops the frame, for a list inside a card: its rows' words line up with the card's. A flush list of `list-group-item-action` rows keeps the rows' room, so a hover's tint and the current row's bar stand off the words - as in [a card that scrolls](card.md#card-scrolling). `list-group-numbered` on an `<ol>` numbers the items.

```html
<div class="card">
	<div class="card-body">
		<ol class="list-group list-group-flush list-group-numbered">
			<li class="list-group-item hstack gap-8"><span class="flex-fill">North Bay Marine Park</span><span class="badge">28</span></li>
			<li class="list-group-item hstack gap-8"><span class="flex-fill">Turtle Cove</span><span class="badge">11</span></li>
			<li class="list-group-item hstack gap-8"><span class="flex-fill">The Five Islands</span><span class="badge badge-subtle-warning">3</span></li>
		</ol>
	</div>
</div>
```

### Headings

`list-group-header` on an item that is not one: a small heading over the items that follow it, in capitals, on the page's own background.

```html
<ul class="list-group">
	<li class="list-group-header list-group-item">Today</li>
	<li class="list-group-item">North Bay Marine Park</li>
	<li class="list-group-item">Night market and street food</li>
	<li class="list-group-header list-group-item">Tomorrow</li>
	<li class="list-group-item">Turtle Cove and Pearl Cove</li>
</ul>
```

### Side by side

`list-group-horizontal` sets the items in a row; with a width prefix, from that width of the region on: `md:list-group-horizontal`.

```html
<ul class="list-group md:list-group-horizontal">
	<li class="list-group-item">Transfer</li>
	<li class="list-group-item">Boat</li>
	<li class="list-group-item">Lunch</li>
	<li class="list-group-item">Park fee</li>
</ul>
```

### With checks

```html
<div class="list-group" role="group" aria-label="Show on">
	<label class="list-group-item hstack gap-8"><input class="check" type="checkbox" checked>lagoon.example</label>
	<label class="list-group-item hstack gap-8"><input class="check" type="checkbox" checked>northbay.example</label>
	<label class="list-group-item hstack gap-8"><input class="check" type="checkbox">turtlecove.example</label>
</div>
```

### Filtered by a search

A search field with `data-filter` naming a region hides, as the person types, the region's items whose words lack one of the typed words - case and accents aside: list items, items with a `data-value`, [media tiles](media.md), [tree items](tree.md) and table rows; an item's `data-search` stands in for its text. A tree keeps the ancestors of a match and opens their groups. An element with `data-filter-empty` in the region shows when nothing matches. The count is said once the typing pauses, in the page's count text `found`; `Esc` in the field clears it. A hidden item carries `data-filtered`.

```html
<input class="form-control" type="search" data-filter="list-group-search-list" aria-label="Find an excursion" placeholder="Find an excursion" autocomplete="off">
<ul class="list-group" id="list-group-search-list">
	<li class="list-group-item">North Bay Marine Park</li>
	<li class="list-group-item">Turtle Cove and Pearl Cove</li>
	<li class="list-group-item">The Five Islands by kayak</li>
	<li class="list-group-item" data-search="elephant sanctuary chang">Elephant sanctuary</li>
	<li class="list-group-item text-muted" data-filter-empty hidden>No excursion has these words.</li>
</ul>
```

### Reordering

A list with `data-reorder` naming a hidden field moves its items, each with a `data-value`, by a `drag-handle`: a button opening the list's menu of moves, whose items carry `data-move` - `up`, `down`, `first`, `last` - or the pointer dragging the handle: a lifted copy follows the pointer, the item's place - an empty slot - goes where it will land, and the other items glide aside; near the edge of the box the list scrolls in, it scrolls, and `Escape` takes the item back. A move by the menu glides too; with reduced motion nothing glides. Each move ends with the focus back on the handle, and the page's text `moved` says the new place. Then `redz:move` fires on the item: a page that saves cancels it and answers with a promise - refused, the move is undone and the page's words are said; left alone, hidden fields beside the list hold the new order for the form.

```html
<ul class="list-group" id="list-group-moves-list" aria-label="Stops of the tour" data-reorder="stops[]">
	<li class="list-group-item hstack gap-8" data-value="pier"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: The pier"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>The pier</li>
	<li class="list-group-item hstack gap-8" data-value="lagoon"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: The emerald lake"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>The emerald lake</li>
	<li class="list-group-item hstack gap-8" data-value="beach"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Lunch on the beach"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>Lunch on the beach</li>
</ul>
<div class="menu" id="list-group-moves-menu" popover role="menu" aria-label="Move">
	<button class="menu-item" type="button" role="menuitem" data-move="up"><i class="bi bi-arrow-up" aria-hidden="true"></i>Move up</button>
	<button class="menu-item" type="button" role="menuitem" data-move="down"><i class="bi bi-arrow-down" aria-hidden="true"></i>Move down</button>
	<button class="menu-item" type="button" role="menuitem" data-move="first">Move to the top</button>
	<button class="menu-item" type="button" role="menuitem" data-move="last">Move to the bottom</button>
</div>
```

A table's rows move the same: `data-reorder` on the `tbody`, a `data-value` on each row and its `drag-handle` in a cell. The hidden fields and the polite status go after the table.

```html
<table class="table" aria-label="Lines of the estimate">
	<thead><tr><th scope="col"><span class="visually-hidden">Move</span></th><th scope="col">Item</th><th scope="col" class="text-end">Amount</th></tr></thead>
	<tbody data-reorder="lines[]">
		<tr data-value="cement"><td><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Cement"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button></td><td>Cement</td><td class="text-end tabular-nums">12,400</td></tr>
		<tr data-value="steel"><td><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Steel bars"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button></td><td>Steel bars</td><td class="text-end tabular-nums">38,900</td></tr>
		<tr data-value="labor"><td><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Labor"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button></td><td>Labor</td><td class="text-end tabular-nums">21,000</td></tr>
	</tbody>
</table>
```

In a grid of more than one column, or a row, the pointer drags in two dimensions: the item under it takes the dragged one before or after it, by the half of it the pointer is in. The menu's moves go earlier and later in the order - give them those words.

```html
<div class="grid cols-2 md:cols-3 gap-12" aria-label="Dashboard cards" role="list" data-reorder="cards[]">
	<div class="card" role="listitem" data-value="sales"><div class="card-body hstack gap-8"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-grid-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Sales"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>Sales</div></div>
	<div class="card" role="listitem" data-value="bookings"><div class="card-body hstack gap-8"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-grid-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Bookings"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>Bookings</div></div>
	<div class="card span-full md:span-1" role="listitem" data-value="tasks"><div class="card-body hstack gap-8"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-grid-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Tasks"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>Tasks</div></div>
	<div class="card" role="listitem" data-value="weather"><div class="card-body hstack gap-8"><button class="btn btn-sm btn-icon btn-text drag-handle" type="button" commandfor="list-group-moves-grid-menu" command="toggle-popover" aria-haspopup="menu" aria-label="Move: Weather"><i class="bi bi-grip-vertical" aria-hidden="true"></i></button>Weather</div></div>
</div>
<div class="menu" id="list-group-moves-grid-menu" popover role="menu" aria-label="Move">
	<button class="menu-item" type="button" role="menuitem" data-move="up"><i class="bi bi-arrow-left flip-rtl" aria-hidden="true"></i>Move earlier</button>
	<button class="menu-item" type="button" role="menuitem" data-move="down"><i class="bi bi-arrow-right flip-rtl" aria-hidden="true"></i>Move later</button>
	<button class="menu-item" type="button" role="menuitem" data-move="first">Move to the start</button>
	<button class="menu-item" type="button" role="menuitem" data-move="last">Move to the end</button>
</div>
```

```js
document.addEventListener('redz:move', (event) => {
	event.preventDefault();
	const {item, parent, position} = event.detail;
	event.detail.respondWith(fetch('/stops/' + item.dataset.value + '/place', {method: 'POST', body: new URLSearchParams({parent: parent?.dataset.value ?? '', position})})
		.then((answer) => answer.ok || Promise.reject('The stop could not be moved')));
});
```

## States

## Keyboard and accessibility

- Links and buttons keep their own keys: `Tab` from one to the next, `Enter` - and `Space` on a button - to choose. A list of links that moves between pages is a `<nav>` with its own name.
- The current item is `aria-current`, never a class: the screen reader says “current page”, the look follows the attribute.
- A link that may not be followed has no `href`: `aria-disabled` alone would leave it working.

## Tokens

From [`css/components/list-group.css`](https://github.com/redz-tech/REDZ.UI/blob/main/css/components/list-group.css). It declares `--redz-list-group-row`, `--redz-list-group-column` on its root, never on rows or items.

It reads the theme's `--redz-border`, `--redz-radius-12`, `--redz-bg`, `--redz-space-12`, `--redz-space-16`, `--redz-text`, `--redz-space-8`, `--redz-bg-page`, `--redz-text-muted`, `--redz-fs-12`, `--redz-bg-hover`, `--redz-primary-subtle`, `--redz-primary`, `--redz-text-faint`.

## From Bootstrap 5.3

| Bootstrap 5.3 | REDZ.UI | Change | Note |
| --- | --- | --- | --- |
| `list-group-item-danger` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-dark` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-info` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-light` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-primary` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-secondary` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-success` | - | review | no item tones: a badge or a dot in the item says it |
| `list-group-item-warning` | - | review | no item tones: a badge or a dot in the item says it |
