Skip to content
Primitiv home
Framework
Consumption mode

List

stableSource Figma

A <ul>/<ol> with custom, token-coloured markers and a controllable item gap, marker gap and indent — the styled counterpart to the bare list styles in primitiv.reset.

Playground

Density

Preview

  • Ships with zero styles
  • Accessible by construction
  • Yours to restyle
Size
Type
import { List } from "@/components/ui/list";
<List type="unordered" size="md">  <List.Item>Ships with zero styles</List.Item>  <List.Item>Accessible by construction</List.Item>  <List.Item>Yours to restyle</List.Item></List>

Density is set by a data-density ancestor — the Context system, not a List prop.

Installation

npx primitiv add list

Import

import { List } from "@/components/ui/list";

No headless primitive — this one ships only as a copied styled surface, so primitiv add is the only way in, whichever mode you are reading.

Anatomy

<List>  <List.Item /></List>

Props

List

Extends HTMLUListElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultDescription
refRef<HTMLUListElement | HTMLOListElement>List renders either a <ul> or an <ol> depending on type, so its ref can resolve to either element.
type"unordered" | "ordered"unorderedMarker style.
marker"true" | "false"trueWhether each item draws its leading bullet/number. false is the equivalent of list-style: none — the marker's box and its gap both go, so rows sit flush — for prose and landing-page lists where bullets read as noise. Independent of indent; combine both false for a fully flush list. Mirrors the Figma List set's Marker axis and ListItem's Show marker boolean.
indent"true" | "false"trueWhether the list applies its own left indent, or sits flush for inline/nested contexts where a parent already supplies it.
size"xs" | "sm" | "md" | "lg" | "xl"mdItem text size; data-density scales each size further.

List.Item

Headless only — the copied file renders this part for you and exports no separate component, so there is nothing to import under Styled.

Extends HTMLLIElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultDescription
disabledbooleanfalseDims the row to mark the entry as unavailable, matching the Figma ListItem set's State=disabled variant (RFC 0012 D9 — 50% opacity over the whole row, marker included). Presentational only. An <li> isn't interactive, so this publishes the data-disabled styling hook and nothing else — it does not remove anything focusable inside the row from the tab order, which stays the consumer's job.

Styling contract

Control frame

--primitiv-list-marker-gap--primitiv-list-indent--primitiv-list-marker-color--primitiv-list-font-family--primitiv-list-font-size--primitiv-list-line-height

Item

--primitiv-list-item-gap--primitiv-list-item-disabled-opacity--primitiv-list-item-color

Data attributes

List.Item

Headless only — the copied file renders this part for you and exports no separate component, so there is nothing to import under Styled. Target it with the class below.

className: .primitiv-list__item

AttributeValueWhen
data-disabled""disabled

Accessibility

  • List renders a real <ul> or <ol> and List.Item a real <li>, so assistive tech announces the item count and position — dropping the marker with marker={false} changes the look, never the semantics.
  • The markers are drawn by the component rather than list-style, so they take the type colour and stay aligned, but they remain decoration on top of the native list structure.
  • List.Item accepts disabled, which dims the row (50% opacity) as a visual state — it is presentational here, since a list item is not itself a control.

Examples

Ordered and unordered

type picks the element and the marker: unordered is a <ul> with token-coloured bullets, ordered an <ol> with numbers. The markers are drawn by the component, not the browser default, so they inherit the type colour and sit on the baseline.

Density
  • Ships with zero styles
  • Accessible by construction
  • Yours to restyle
  1. Run the CLI
  2. Copy the files
  3. Edit them
import { List } from "@/components/ui/list";
<List type="ordered">  <List.Item>Run the CLI</List.Item>  <List.Item>Copy the files</List.Item>  <List.Item>Edit them</List.Item></List>

A flush prose list

marker={false} drops the bullet (the equivalent of list-style: none, gap and all) and indent={false} removes the inset — combine both for a list that reads as plain lines, for prose or a nav, while staying a real <ul> so its semantics are unchanged. The two are independent.

Density
  • Ships with zero styles
  • Accessible by construction
  • Yours to restyle
import { List } from "@/components/ui/list";
<List marker={false} indent={false}>  <List.Item>Ships with zero styles</List.Item>  <List.Item>Accessible by construction</List.Item></List>