Skip to content
Containment

List

MD3 lists — one to three lines per row, and rows you can swipe aside for their actions.

Walkthrough

1. Lines

One, two or three lines — a name, a name and a note, a note that wraps. The row height follows, so a list stays even.

Amina Yusuf Amina Yusuf Deploy is green again. 3m
Karwan Ali Karwan Ali I've moved the Basra delivery to Thursday because the road is closed.
Hevi Salih Hevi Salih
<script lang="ts">	import { Avatar, List, ListItem } from 'omaris';</script>​<div class="w-full max-w-md">	<List variant="outlined" dividers label="Messages">		<ListItem headline="Amina Yusuf" supportingText="Deploy is green again." lines={2}>			{#snippet leading()}<Avatar name="Amina Yusuf" size="sm" colorize />{/snippet}			{#snippet trailing()}<span class="text-body-sm text-muted-foreground">3m</span>{/snippet}		</ListItem>		<ListItem			headline="Karwan Ali"			supportingText="I've moved the Basra delivery to Thursday because the road is closed."			lines={3}		>			{#snippet leading()}<Avatar name="Karwan Ali" size="sm" colorize />{/snippet}		</ListItem>		<ListItem headline="Hevi Salih">			{#snippet leading()}<Avatar name="Hevi Salih" size="sm" colorize />{/snippet}		</ListItem>	</List></div>

2. Interactive

interactive makes rows pressable, with a ripple and a selected state — a picker where the choice stays visible, like the current warehouse.

<script lang="ts">	import { List, ListItem } from 'omaris';​	let chosen = $state('erbil');</script>​<div class="w-full max-w-md">	<List variant="surface" label="Warehouses">		<ListItem			interactive			selected={chosen === 'erbil'}			headline="Erbil"			supportingText="2 open orders"			lines={2}			onclick={() => (chosen = 'erbil')}		/>		<ListItem			interactive			selected={chosen === 'baghdad'}			headline="Baghdad"			supportingText="11 open orders"			lines={2}			onclick={() => (chosen = 'baghdad')}		/>		<ListItem			interactive			selected={chosen === 'basra'}			headline="Basra"			supportingText="No open orders"			lines={2}			onclick={() => (chosen = 'basra')}		/>	</List></div>

3. Swipe actions

Drag a row sideways — a finger, a mouse, or two fingers on a trackpad — to reveal its actions; let go and it springs to rest at the speed it had. Carry it clear across and the first action fires. dismiss collapses the row on the way out, so deleting does not leave a gap that jumps closed.

<script lang="ts">	import { List, ListItem, SwipeItem, toast } from 'omaris';​	let rows = $state([		{ id: 1, from: 'Amina Yusuf', subject: 'Deploy is green again' },		{ id: 2, from: 'Karwan Ali', subject: 'Basra delivery moved to Thursday' }	]);</script>​<div class="w-full max-w-md">	<List variant="outlined" dividers label="Inbox">		{#each rows as row (row.id)}			<SwipeItem				fullSwipe				startActions={[{ label: 'Pin', tone: 'warning', onaction: () => toast('Pinned') }]}				endActions={[					{						label: 'Delete',						tone: 'destructive',						dismiss: true,						onaction: () => (rows = rows.filter((r) => r.id !== row.id))					}				]}			>				<ListItem headline={row.from} supportingText={row.subject} lines={2} />			</SwipeItem>		{/each}	</List>​	{#if !rows.length}		<p class="p-4 text-body-sm text-muted-foreground">Everything swiped away.</p>	{/if}</div>

4. Reorder

Reorder takes the container props List takes, so a list becomes reorderable by swapping the tag and binding the array. Drag a row by its grip, or tab to it and press Space, then the arrow keys. Nothing moves in the DOM while you drag — the rows are measured once and slide by exactly the space the lifted one left, so rows of different heights land where they look like they will.

Press Space to pick a row up, the arrow keys to move it, Space to drop it and Escape to put it back.

Overture · The Long Way Round · Harbour Lights · Closing Time
<script lang="ts">	import { ListItem, Reorder, ReorderHandle, Text } from 'omaris';​	let tracks = $state([		{ id: 'a', name: 'Overture', length: '4:12' },		{ id: 'b', name: 'The Long Way Round', length: '3:48' },		{ id: 'c', name: 'Harbour Lights', length: '5:02' },		{ id: 'd', name: 'Closing Time', length: '2:57' }	]);</script>​<div class="flex w-full max-w-sm flex-col gap-3">	<Reorder bind:items={tracks} variant="outlined" dividers listLabel="Playlist">		{#snippet children(track, ctx)}			<ListItem role="presentation" headline={track.name} supportingText={track.length}>				{#snippet leading()}<ReorderHandle />{/snippet}				{#snippet trailing()}					<Text variant="label-sm" tone="muted" as="span">{ctx.index + 1}</Text>				{/snippet}			</ListItem>		{/snippet}	</Reorder>​	<Text variant="label-sm" tone="muted">		{tracks.map((track) => track.name).join(' · ')}	</Text></div>