Skip to main content
Graphite UI

Contained list

A row of a list, and the title bar that heads the list: a title, room for a marker before it and a control after it, at one of four heights. Use it for a flat list a reader scans; when the rows share columns worth comparing, use Data table.

Contract 2.1.0Kit · 7 sets · 43 variantsOpen in Figma

Live preview

Rendered by the component itself from the same generated tokens as the rest of the site.

Components

BTButton
TBTabs
TGTag

Installation

Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.

npx shadcn@latest add https://www.graphite-ui.com/r/contained-list.json
import { ContainedList } from '@/components/ui/contained-list'

Anatomy

The header above, then rows: a 16-inset cell with the marker and the title, any further cells, and the trailing control flush to the right edge at the row’s height. A 1px rule sits under each row. Only the title is required, and it stays on one line and truncates.

Components

BTButton
TBTabs
  1. LeadingOptionalAn icon, tag or other short marker.
  2. TitleRequiredTypography, set in Body/3.
  3. DescriptionOptionalThe code's own second line at Caption/1; the kit draws none. Extra large is the size laid out for it.
  4. CellsOptionalThe kit's Item 2 and Item 3, further text cells after the title, each inset 16.
  5. TrailingOptionalTag, Button, or a control, flush to the row's right edge. The kit draws a ghost icon Button at the row's height.
  6. HeaderOptionalThe kit's list title item, a separate export that sits directly above the rows. On page is a Title/5 heading on the page's background at the rows' height, with the rule under it; Disclosed is a 32px Caption/1 bar on elevation-01. It takes one action at its right edge, and the kit's Filterable search, an expandable Search sized to the bar (2.1.0, #267).

Variants

Size sets the row’s height: 32, 40, 48 or 64, with Extra large setting its content at the top, where the code’s description line fits. The list’s Type is its header: On page or Disclosed. Item 2 and 3 are further cells; Inset stops the divider 16 short of each end.

Type: On page

Components

BTButton
TBTabs
Type: Disclosed

Components

BTButton
TBTabs
Filterable search

Components

BTButton
TBTabs
Size: Small
Row title
Size: Medium
Row title
Size: Large
Row title
Size: Extra large
Row titleRow description
Item 2 and 3
ButtonGoverned2.5.0
Divider: Inset
Row title

States

Hover climbs one rung of the elevation ladder and active two, the same tone-step move as Button’s hover; the divider steps aside while they show. Focus is a ring inside the row, drawn when the caller’s link round it takes focus. Disabled dims the text and marker.

Enabled
Row title
Hover
Row title
Active
Row title
Focus
Row title
Disabled
Row title

API reference

Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.

Contained list props
PropTypeDefaultNotes
size'sm' | 'md' | 'lg' | 'xl'—The kit's row Size, 32, 40, 48 and 64 tall. Large by default, as the kit's list uses it. Extra large sets the content at the top.
interactivebooleanfalseVisual only. Adds the pointer cursor and the hover and active tone steps. The row takes no role, no tab stop and no click handler, so the caller supplies the interactive element; a link wrapped round the row draws the focus ring inside it.
disabledboolean—The kit's Disabled. Text and marker dim and the row stops responding; the caller disables its link or control too.
insetDividerboolean—The kit's divider Inset. The rule under the row stops 16 short of each end.

Design tokens

Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.

  • on-surfaceTitle, and the On page header.
  • surfaceResting row background. The kit fills a raw white, un-migrated Carbon; surface reads the same in Light and works in Dark.
  • elevationHover (elevation-02) and active (elevation-03), the kit's layer-hover-01 and layer-active-01, one and two rungs up the ladder. The Disclosed header sits on elevation-01.
  • outlineThe row divider and the header rule, through the subtle step (outline-subtle, the kit's border-subtle-00). It steps aside on hover, active and focus.
  • backgroundThe On page header, the kit's Background/background.
  • primaryThe focus ring inside the row, through the family's focus step; and disabled text, through its disabled-content step.
  • on-surface-variantDescription text, and the Disclosed header.
  • textThe title at Body/3, the description at Caption/1, the On page header at Title/5 SemiBold.
  • spacingRow heights and padding, the 16 inset and the gaps between slots.

Usage

The contract's prohibitions, written as the choices you will actually face.

Do

  • Put a Tag, an icon or a short code in the leading slot. It is a marker, not a second title.
  • Keep the trailing slot to one control cluster: a ghost icon Button at the row’s size, a Tag, or a Menu.
  • Set interactive only when clicking the row really does something, and supply that something: wrap the row in a link, or put the Button in the trailing slot.
  • Build list views and table rows from this row, so a hover change lands everywhere at once.

Don’t

  • Stack two or three buttons in the trailing slot. When a row needs several actions, put a Menu there instead.
  • Rely on the description to carry something essential. It is the code’s own, truncates to one line, and the kit does not draw it.
  • Set interactive on a row that only displays information. The hover tells the reader it will respond.
  • Give a list its own row highlight. Hover and active are the elevation ladder’s rungs, so every list moves together.

Accessibility

What the component does for you, and what it leaves to you.

Roles
The row is a plain container with no role. Wrap the rows in a list element, or a table, when the structure matters to the reader.
Keyboard
The row itself is never focusable, even when interactive: the prop is visual only. Keyboard access comes from the link that wraps the row or the control in its trailing slot.
Focus
A link wrapped round the row draws a 2px primary-focus ring inside it when it takes focus. The trailing control brings its own ring.
Disabled
disabled dims the row and stops its hover, and sets aria-disabled; disable the link or control the row holds as well, since the row has none of its own.
Header
The header renders a real heading, h3 unless you pass another level, so the list sits in the page’s outline.
Contrast
Title is on-surface and description on-surface-variant, one tone step lower. Both are measured against surface at the theme’s target.
Truncation
Title and description clip with an ellipsis. The full text is still in the DOM, so assistive tech reads all of it.

Figma parity

The kit's Contained list page ships 43 variants across 7 sets. One is public; the rest are the private row, cell and title items it is built from. The code is the row and the header, on every axis.

Kit axes and how they map to code
Kit axisValuesCodeHow it maps
TypeOn page · DisclosedContainedListHeader variantThe kit’s title item: Title/5 SemiBold on background at the rows’ height, or a 32px Caption/1 bar on elevation-01. One action at its right edge.
Search · Filterable searchBooleanContainedListHeader searchThe governed Search, expandable, sized to the bar. Filtering the rows is the caller’s.
SizeSmall · Medium · Large · Extra largesizesm, md, lg and xl: 32, 40, 48 and 64. Extra large sets its content at the top.
StateEnabled · Hover · Focus · Active · Disabledinteractive · disabledHover and Active are pseudo-classes on an interactive row (governance rule 7): elevation-02 and -03. Focus is the ring a wrapping link draws. Disabled dims text and marker; the kit keeps the icon bright, a slip.
Item 2 · Item 3BooleanscellsFurther text cells, each inset 16.
Divider: InsetTrue · FalseinsetDividerThe rule stops 16 short of each end.
DescriptionNot drawndescriptionThe code’s own, at Caption/1. Extra large is laid out for a second line it never draws.
FillRaw #ffffff—An un-migrated Carbon value. surface, which reads the same in Light.