Lists
A list is a set of rows with text and, if needed, an icon, an avatar or a control. Rows can be read only, act on the page or navigate. What you pass decides which element is rendered.
Usage
- Browsers
- Chrome
- Safari
- FirefoxFirefox is the spiritual successor of Netscape Navigator.
- Compare benchmarksThis will open a new tab
<script lang="ts">
import { Divider, List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<List style="max-width:300px">
<ListItem>Browsers</ListItem>
<Divider />
<ListItem>Chrome</ListItem>
<ListItem>Safari</ListItem>
<ListItem supportingText="Firefox is the spiritual successor of Netscape Navigator."
>Firefox</ListItem
>
<ListItem href="https://browserbench.org" target="_blank"
>{#snippet end()}
<Icon>open_in_new</Icon>
{/snippet}
{#snippet supportingText()}
This will open a new tab
{/snippet}Compare benchmarks</ListItem
>
</List>
Icon item
- Account
- Setting
- Sign out
<script lang="ts">
import { Divider, List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<List style="max-width:300px">
<ListItem>
Account
{#snippet start()}
<Icon>person</Icon>
{/snippet}
</ListItem>
<Divider />
<ListItem>
Setting
{#snippet start()}
<Icon>settings</Icon>
{/snippet}
</ListItem>
<Divider />
<ListItem>
Sign out
{#snippet start()}
<Icon>logout</Icon>
{/snippet}
</ListItem>
</List>
Leading and trailing content
start adds a leading icon. avatar adds a 40px circle: initials as a
string, or a snippet with an image. end adds a trailing icon, or a short text such as a
count in a small label style.
- InboxLeading icon
- Ada LovelaceAvatar with initials100+
- Grace HopperAvatar with an image
<script lang="ts">
import { List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
const photo = `data:image/svg+xml,${encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><rect width="40" height="40" fill="#7d5260"/><circle cx="20" cy="16" r="7" fill="#ffd8e4"/><rect x="8" y="26" width="24" height="14" rx="7" fill="#ffd8e4"/></svg>',
)}`
</script>
<List style="max-width:340px">
<ListItem supportingText="Leading icon">
Inbox
{#snippet start()}<Icon>mail</Icon>{/snippet}
{#snippet end()}<Icon>favorite</Icon>{/snippet}
</ListItem>
<ListItem avatar="AL" supportingText="Avatar with initials" end="100+">Ada Lovelace</ListItem>
<ListItem supportingText="Avatar with an image">
Grace Hopper
{#snippet avatar()}<img alt="" src={photo} />{/snippet}
{#snippet end()}<Icon>verified</Icon>{/snippet}
</ListItem>
</List>
Lines
An item takes one line, or two with supportingText: 56px and 72px high. Supporting
text long enough to wrap makes it a three-line item of 88px.
- One line
- Two linesSupporting text
- Three linesSupporting text that is long enough to wrap onto a second line of its own
<script lang="ts">
import { List, ListItem } from 'noph-ui'
</script>
<List style="max-width:340px">
<ListItem>One line</ListItem>
<ListItem supportingText="Supporting text">Two lines</ListItem>
<ListItem
supportingText="Supporting text that is long enough to wrap onto a second line of its own"
>
Three lines
</ListItem>
</List>
Actions
An item with an onclick is a button and acts on the page. An item with an href is a link and navigates. The actions of a list share one tab stop. Tab moves to the first one, and the arrow keys move between them.
- Help
- Sign out
Last picked: nothing yet
<script lang="ts">
import { List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
let last = $state('nothing yet')
</script>
<div style="width:100%;max-width:340px">
<List aria-label="Account">
<ListItem onclick={() => (last = 'Profile')}>
Profile
{#snippet start()}<Icon>person</Icon>{/snippet}
</ListItem>
<ListItem onclick={() => (last = 'Settings')}>
Settings
{#snippet start()}<Icon>settings</Icon>{/snippet}
</ListItem>
<ListItem href="#actions">
Help
{#snippet start()}<Icon>info</Icon>{/snippet}
{#snippet end()}<Icon>open_in_new</Icon>{/snippet}
</ListItem>
<ListItem disabled>
Sign out
{#snippet start()}<Icon>logout</Icon>{/snippet}
</ListItem>
</List>
<p>Last picked: {last}</p>
</div>
Single select
selection="single" turns the list into a listbox and its items into options, so
assistive technology announces which one is selected. You manage the selection: set selected on the items and change it in their onclick. Show the selection
with more than color, for example a check.
<script lang="ts">
import { List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
const places = ['Home', 'Work', 'Coffee shop']
let place = $state('Work')
</script>
<List selection="single" aria-label="Pick a place" style="max-width:340px">
{#each places as option (option)}
<ListItem selected={place === option} onclick={() => (place = option)}>
{option}
{#snippet end()}
{#if place === option}<Icon>check</Icon>{/if}
{/snippet}
</ListItem>
{/each}
</List>
Multi select
selection="multiple" works the same way, but any number of items can be selected.
<script lang="ts">
import { List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
const topics = ['Mail', 'Calendar', 'Reminders', 'Updates']
let chosen = $state(['Mail'])
const toggle = (topic: string) =>
(chosen = chosen.includes(topic) ? chosen.filter((t) => t !== topic) : [...chosen, topic])
</script>
<List selection="multiple" aria-label="Notify me about" style="max-width:340px">
{#each topics as topic (topic)}
<ListItem selected={chosen.includes(topic)} onclick={() => toggle(topic)}>
{topic}
{#snippet start()}
<!-- The check keeps its place when hidden, so the labels stay in line. -->
<span style:visibility={chosen.includes(topic) ? 'visible' : 'hidden'}>
<Icon>check</Icon>
</span>
{/snippet}
</ListItem>
{/each}
</List>
Theming
Tokens
| Token | Default value |
|---|---|
--np-item-container-height | 3.5rem, or 4.5rem with supporting text |
--np-item-gap | 1rem |
--np-item-avatar-container-color | --np-color-primary-container |
--np-item-avatar-label-text-color | --np-color-on-primary-container |
--np-item-label-text-color | --np-color-on-surface |
--np-item-supporting-text-color | --np-color-on-surface-variant |
--np-item-leading-icon-color | --np-color-on-surface-variant |
--np-item-trailing-icon-color | --np-color-on-surface-variant |
--np-item-container-height sets the minimum height of an item. An item with taller content
grows past it.
Example
- Account
<script lang="ts">
import { List, ListItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<List style="max-width:300px">
<ListItem
--np-item-container-height="3rem"
--np-item-label-text-color="var(--np-color-tertiary)"
--np-item-leading-icon-color="var(--np-color-tertiary)"
>
Account
{#snippet start()}
<Icon>person</Icon>
{/snippet}
</ListItem>
</List>
Accessibility
List renders a plain <ul>, so anything you put inside becomes a
list item for assistive technology. Give an item an href when it navigates, an onclick when it acts on the current page, and neither when it is only read. This decides
the rendered element, its role and whether it can get focus.
The actions of a list share one tab stop. It is on the selected item, or on the first one if nothing is selected. ↓ and → move to the next action, ↑ and ← to the previous one, wrapping at the ends. Home and End jump to the first and last. Space and Enter activate an item, links included.
With selection, the list is a listbox and each item is an option with aria-selected. It adds aria-multiselectable for multiple. Give the list an aria-label that says what is being chosen.
A disabled item renders as a <div> with aria-disabled instead of a disabled
control. Screen readers can still read it, but it is not in the tab order.
API
List attributes
All other attributes go to the <ul> element, including class, style and event handlers.
| Attribute | Type | Default | Description |
|---|---|---|---|
element | HTMLUListElement | undefined | undefined | Bindable reference to the underlying <ul>. |
selection | 'single' | 'multiple' | undefined | undefined | Makes the list a listbox of options that announce their selected state. |
Item
ListItem is an Item wrapped in an <li>. When a row is
not in a <ul>, for example in search results, a sheet or a card, use Item instead. It takes the same attributes as ListItem below.
The item picks its element from what you pass: an href makes it an <a>. An onclick, a command or a popovertarget makes it a <button>. Without any of them it is a <div>. Links and buttons have a ripple. Set type to choose the
element yourself, for example type="text" for a row where only the controls inside it react
to clicks.
<script lang="ts">
import { Item } from 'noph-ui'
</script>
<Item href="/components/list">Lists</Item>ListItem attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
variant | 'text' | 'button' | 'link' | undefined | undefined | Deprecated, use type. Still pins the element: a <div>, a <button> or an <a>. |
selected | boolean | false | Whether the item is shown as selected. |
disabled | boolean | false | Renders the item as non-interactive with aria-disabled, even with an href or onclick. |
supportingText | string | Snippet | undefined | undefined | Second line below the label. Pass a snippet to render your own markup. An item with supporting text is taller. |
start | Snippet | undefined | undefined | Leading content, usually an icon or an avatar. |
avatar | string | Snippet | undefined | undefined | A 40px round avatar in place of start: initials, or an image in a snippet. |
end | string | Snippet | undefined | undefined | Trailing content, usually an icon, a switch or a checkbox. Pass a string for short trailing text such as a count. |
softFocus | boolean | false | Shows the hover state without hover. Use it to mark the active option in a list controlled by the arrow keys. |
lazy | boolean | false | Skips rendering the item while it is off screen, for very long lists. |
type | 'text' | 'link' | 'button' | 'submit' | 'reset' | null | undefined | Pins the element, like on Card. text renders a <div>, link an <a>, and the native button
types a <button> of that type. Use submit to have the item submit
the surrounding form. |