Listview
Listview displays structured rows of content with optional icons, titles, subtitles, and trailing actions.
It is a pure CSS component — no JavaScript required.
Apply .listview to a <ul>, <ol>, or <div> and compose each row with the provided slot classes.
Listview is suited for navigation menus, settings panels, file browsers, contact lists, onboarding flows, and any interface that needs scannable rows with consistent structure.
Getting Started
Apply .listview to a list container and .list-item to each row.
Use the three slot classes — .list-start, .list-content, and .list-end — to compose the row layout.
<ul class="listview">
<li class="list-item">
<div class="list-start">
<span class="list-icon">★</span>
</div>
<div class="list-content">
<span class="list-title">Item Title</span>
<span class="list-subtitle">Supporting description text</span>
</div>
<div class="list-end">
<span>12:30</span>
</div>
</li>
</ul>
- Item TitleSupporting description text12:30
Structure
Listview is built on a flexible three-slot system. Each .list-item supports a left slot,
a center content slot, and a right slot — use only the slots you need.
| Element | Class / Attribute | Purpose |
|---|---|---|
| Container | listview | Root class. Apply to <ul>, <ol>, or <div>. |
| Row | list-item | Row element. Flex row with space-between alignment and bottom border. |
| Left slot | list-start | Fixed-width left slot for icons, avatars, or checkboxes. Never compresses. |
| Center slot | list-content | Takes all remaining space via flex: 1. Holds title and subtitle. |
| Primary text | list-title | Main label inside .list-content. 500 weight, single-line ellipsis. |
| Secondary text | list-subtitle | Supporting text inside .list-content. Muted color, 0.875rem. |
| Right slot | list-end | Fixed-width right slot for metadata, badges, or actions. |
| Icon helper | list-icon | Sizes icons to 1.25rem with line-height: 1. Use inside .list-start or .list-end. |
State Modifiers
State modifiers are applied to individual .list-item elements.
| Modifier | Applies to | Effect |
|---|---|---|
is-active | .list-item | Fills the row with --primary. Text color auto-calculated for contrast via oklch. |
is-disabled | .list-item | Applies opacity: 0.6 and pointer-events: none. Also works on <a> elements. |
is-active
Highlights the row as selected. The background fills with --primary, and text color is automatically
calculated for contrast via oklch. Subtitle, start, and end slots shift to 75% opacity of the same contrast color.
<ul class="listview is-bordered">
<li class="list-item">Inbox</li>
<li class="list-item is-active">Sent</li>
<li class="list-item">Drafts</li>
</ul>
- Inbox
- Sent
- Drafts
is-disabled
Makes the row non-interactive. Applies opacity: 0.6 and pointer-events: none.
Works on both <li> and <a> elements.
<ul class="listview is-bordered">
<li class="list-item">Available option</li>
<li class="list-item is-disabled">Unavailable option</li>
</ul>
- Available option
- Unavailable option
.is-disabledusespointer-events: none, which prevents mouse interaction but does not remove the element from the tab order. For full accessibility, also addaria-disabled="true"andtabindex="-1"to disabled items.
Style Modifiers
Style modifiers are applied to the .listview container.
| Modifier | Effect |
|---|---|
is-bordered | Adds a visible oklch(50% 0 0 / 0.12) border around the entire container. |
is-borderless | Removes the container border and every row's bottom divider. Useful inside cards or panels. |
is-hoverable | Enables subtle hover background and cursor: pointer. Disabled rows are excluded automatically. |
is-striped | Applies oklch(50% 0 0 / 0.04) tint to every even-numbered row. |
is-numbered | Prepends an incrementing CSS counter to each row. Renders in --primary at font-weight: 600. |
is-bordered
Adds a visible border around the entire container. By default the container has a transparent border.
<ul class="listview is-bordered">
<li class="list-item">First item</li>
<li class="list-item">Second item</li>
</ul>
- First item
- Second item
is-borderless
Removes all borders — both the container border and every row's bottom divider. Useful for lists embedded inside cards or panels.
<ul class="listview is-borderless">
<li class="list-item">No dividers</li>
<li class="list-item">No borders</li>
</ul>
- No dividers
- No borders
is-hoverable
Enables a subtle hover background (oklch(from var(--body-text) l c h / 0.05)) and sets cursor: pointer.
Rows with .is-disabled are automatically excluded. Active rows get a darker oklch shade on hover.
<ul class="listview is-bordered is-hoverable">
<li class="list-item">Hover me</li>
<li class="list-item is-active">Active row</li>
<li class="list-item is-disabled">Cannot hover</li>
</ul>
- Hover me
- Active row
- Cannot hover
is-striped
Applies a tint to every even-numbered row for a zebra-stripe pattern.
<ul class="listview is-striped is-bordered">
<li class="list-item">Row 1 — default</li>
<li class="list-item">Row 2 — tinted</li>
<li class="list-item">Row 3 — default</li>
<li class="list-item">Row 4 — tinted</li>
</ul>
- Row 1 — default
- Row 2 — tinted
- Row 3 — default
- Row 4 — tinted
is-numbered
Automatically prepends an incrementing counter to each row using CSS counter-reset and counter-increment.
No manual numbering is needed in markup. The counter renders in --primary at font-weight: 600.
<ul class="listview is-numbered is-bordered">
<li class="list-item">Create your account</li>
<li class="list-item">Verify your email</li>
<li class="list-item">Complete your profile</li>
</ul>
- Create your account
- Verify your email
- Complete your profile
Style modifiers can be combined freely.
is-hoverable is-bordered is-stripedis a valid combination.
Size Modifiers
Size modifiers are applied to individual .list-item elements — not the container —
allowing mixed-density rows within the same list.
| Class | Padding |
|---|---|
| default | 0.875rem 1rem |
is-medium | 1.125rem 1.25rem |
is-large | 1.375rem 1.5rem |
<ul class="listview is-bordered">
<li class="list-item">Default padding</li>
<li class="list-item is-medium">Medium padding</li>
<li class="list-item is-large">Large padding</li>
</ul>
- Default padding
- Medium padding
- Large padding
Using Links as Items
Apply .list-item directly to an <a> element for a fully clickable navigation row.
The entire row becomes the tap and click target — no wrapper div needed.
<ul class="listview is-bordered is-hoverable">
<a class="list-item" href="/profile">
<div class="list-start">
<span class="list-icon">👤</span>
</div>
<div class="list-content">
<span class="list-title">Profile</span>
<span class="list-subtitle">Manage your account</span>
</div>
<div class="list-end">›</div>
</a>
<a class="list-item" href="/settings">
<div class="list-start">
<span class="list-icon">⚙️</span>
</div>
<div class="list-content">
<span class="list-title">Settings</span>
</div>
<div class="list-end">›</div>
</a>
<a class="list-item is-disabled" href="/billing">
<div class="list-content">
<span class="list-title">Billing</span>
<span class="list-subtitle">Unavailable on your plan</span>
</div>
</a>
</ul>
CSS Custom Properties
| Variable | Used by | Description |
|---|---|---|
var(--primary) | is-active background, is-numbered counter | Primary brand color token. |
var(--surface) | Container and row background | Surface color token — adapts to light/dark theme automatically. |
var(--body-text) | Row text, hover tint | Primary text color token. |
var(--muted) | .list-start, .list-subtitle, .list-end | Secondary text color token. |
Accessibility
- Use
<ul>or<ol>as the container for semantic list markup. - For interactive rows, use
<a>or<button>as.list-itemrather than<li>with click handlers. - Add
role="listbox"androle="option"when the listview is used as a selection widget. - For disabled items, add
aria-disabled="true"andtabindex="-1"alongside.is-disabled— CSSpointer-events: nonealone does not prevent keyboard focus. - Always provide a visible or accessible label for icon-only
.list-startslots viaaria-labelor a visually hidden<span>.
Class Reference
| Class | Applies to | Role |
|---|---|---|
listview | <ul>, <ol>, <div> | Root container. Sets flex column layout, surface background, border radius. |
list-item | <li>, <a>, <div> | Row element. Flex row with space-between alignment and bottom border. |
list-start | Child of list-item | Left slot. Fixed width, muted color, right margin. |
list-content | Child of list-item | Center slot. Flex column, takes all available space. |
list-title | Child of list-content | Primary text. 500 weight, single-line ellipsis. |
list-subtitle | Child of list-content | Secondary text. Muted color, single-line ellipsis. |
list-end | Child of list-item | Right slot. Fixed width, muted color, left margin. |
list-icon | Child of list-start or list-end | Icon sizing helper. 1.25rem font size, line-height: 1. |
is-active | list-item | Selected state. Primary background, auto-contrast text via oklch. |
is-disabled | list-item | Disabled state. 60% opacity, pointer-events none. |
is-bordered | listview | Visible container border. |
is-borderless | listview | Removes container border and all row dividers. |
is-hoverable | listview | Hover background and pointer cursor on rows. |
is-striped | listview | Zebra-stripe tint on even rows. |
is-numbered | listview | CSS counter prefix on each row. Renders in --primary at font-weight: 600. |
is-medium | list-item | Medium row padding: 1.125rem 1.25rem. |
is-large | list-item | Large row padding: 1.375rem 1.5rem. |
Notes
- Listview requires no JavaScript — CSS-only for all variants and states.
.list-start,.list-content, and.list-endare optional — use only the slots you need.- Style modifiers (
is-bordered,is-hoverable,is-striped,is-numbered) apply to the.listviewcontainer. - State modifiers (
is-active,is-disabled) and size modifiers (is-medium,is-large) apply to.list-item. .is-disableddoes not remove keyboard focus — always pair witharia-disabled="true"andtabindex="-1".- Use
<a>as.list-itemfor fully clickable rows; the entire row becomes the tap target. is-numbereduses CSS counters — no manual markup required.is-circleonis-floatingonly takes effect when combined withis-floating.- Style modifiers can be combined freely —
is-bordered is-hoverable is-stripedis valid.