Item
A flexible list-row primitive with optional leading media, a content column, and trailing actions.
Overview
Item is a flexible list-row primitive: a horizontal band of optional leading media, a growing
content column, and trailing actions. Compose it from ItemMedia, ItemContent, ItemTitle,
ItemDescription, and ItemActions. Use it for settings rows, member lists, selectable options,
and similar list-like UI.
Anatomy
A flex row (<div>) laying out three optional slots left to right:
ItemMedia— a fixed leading slot for an icon, avatar, or thumbnail.ItemContent— the growing middle column, usually anItemTitleover anItemDescription.ItemActions— a fixed trailing slot for buttons, badges, or a chevron.
The outline variant paints a hand-drawn border via @ghds/sketch-core; all colors, spacing, and
sketch parameters come from @ghds/tokens (comp.item.*).
Variants & States
Three surface variants — default (transparent), muted (subtle fill), outline (hand-drawn
border) — plus a selected boolean that fills the selected background.
| State | Status | Notes |
|---|---|---|
| Selected | Implemented | The selected boolean fills comp.item.bg.selected. |
| Hover | Not implemented | Item paints no hover state itself — wire it in your composition if needed. |
| Focus | Not implemented | Renders a plain div; add focus styling when you make it interactive. |
| Disabled | Not implemented | — |
| Loading | Not implemented | — |
| Error | Not implemented | — |
Usage
Do
- Compose rows from ItemMedia / ItemContent / ItemTitle / ItemDescription / ItemActions rather than styling raw divs.
- Set the appropriate role on the container (listitem, option, …) to match how the list is used.
- Use selected together with aria-selected when the row is a selectable option.
Don't
- Don't rely on Item for interaction semantics — it renders a plain div, so add role, tabIndex, and handlers yourself.
- Don't mix outline and non-outline rows in the same list; keep one variant per list for a consistent rhythm.
Accessibility
Itemrenders a plain<div>with no implicit role — setrole="listitem",role="option", etc. on the container as your composition requires, and wrap it in the matching container role (list,listbox).- When you make a row activate something, add
tabIndex, a role such asbutton, and keyboard handling — none of that is built in. - Pair the
selectedprop witharia-selectedso the selected state is announced, not just painted. - See the Accessibility Guide for shared conventions.
Content
ItemTitle is the primary line — a short label, not a sentence. ItemDescription is a muted
secondary line for supporting detail. Keep media to a single icon/avatar and actions to one or two
controls so the row stays scannable.
Props API
React@ghds/react
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'muted' | 'outline' | 'default' | Surface treatment: transparent, subtle fill, or hand-drawn border. |
selected | boolean | false | Marks the row as the selected/active one (fills the selected background). |
...rest | HTMLAttributes<HTMLDivElement> | — | Native <div> attributes (role, onClick, tabIndex, …) pass through. |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'muted' | 'outline' | 'default' | Reflected attribute; surface treatment. |
selected | boolean | false | Reflected attribute; fills the selected background. |
slots | media, title, description, (default), actions | — | Slot content into media / title / description / actions; the default slot is extra body content. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'muted' | 'outline' | 'default' | Surface treatment. |
selected | boolean | false | Fills the selected background. |
onPress | (event: GestureResponderEvent) => void | — | When set, the row is exposed as a button (Pressable). |
children | ReactNode | — | Compose with ItemMedia / ItemContent / ItemTitle / ItemDescription / ItemActions. |
accessibilityLabel | string | — | Accessible label for the row. |
testID | string | — | Test handle for queries. |
The subcomponents — ItemMedia, ItemContent, ItemTitle, ItemDescription, ItemActions —
each accept the standard HTMLAttributes<HTMLDivElement> and render a styled <div>.
Code examples
// React — @ghds/react
import { Item, ItemContent, ItemTitle, ItemDescription, ItemActions } from '@ghds/react/item';
import { Badge } from '@ghds/react/badge';
<Item variant="outline">
<ItemContent>
<ItemTitle>GyeongHo Kim</ItemTitle>
<ItemDescription>Maintainer · 3 open PRs</ItemDescription>
</ItemContent>
<ItemActions>
<Badge variant="success">Online</Badge>
</ItemActions>
</Item>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
import '@ghds/web-components/item';
</script>
<gh-item variant="outline">
<span slot="title">GyeongHo Kim</span>
<span slot="description">Maintainer · 3 open PRs</span>
</gh-item>
// React Native — @ghds/react-native
import { Item, ItemContent, ItemTitle, ItemDescription } from '@ghds/react-native/item';
<Item variant="outline" onPress={() => {}} accessibilityLabel="GyeongHo Kim">
<ItemContent>
<ItemTitle>GyeongHo Kim</ItemTitle>
<ItemDescription>Maintainer · 3 open PRs</ItemDescription>
</ItemContent>
</Item>;
Live Demo
React and Web Components render live below, driven by the same tokens — toggle dark mode in the
header to see both respond identically. React Native can’t run in a browser without additional
react-native-web wiring (not set up in this site), so its usage is shown as a code sample instead.