ListItemButton — spec¶
Synthesized from MUI
ListItemButton. The clickable variant ofListItem. Use this whenever a list row should respond to clicks (settings, nav, picker). Without it,ListItemis just a structural container.
When to use¶
- Settings rows ("계정 정보", "알림 설정").
- Nav lists in drawers / sidebars.
- Picker / chooser rows where the row itself is the selection target.
- ANY list row with a primary click action.
Anatomy¶
┌──────────────────────────────────────────────────┐
│ [icon] Primary text [trailing icon] │
│ Secondary text │
└──────────────────────────────────────────────────┘
─ entire row is the click target ─
For rows with a separate trailing action (button), wrap ListItemButton inside ListItem and put the action in secondaryAction (which sits outside the button's hit area).
API¶
<List>
<ListItem disablePadding>
<ListItemButton selected={current === 'profile'} onClick={() => navigate('/profile')}>
<ListItemIcon><PersonIcon /></ListItemIcon>
<ListItemText primary="프로필" secondary="이름, 이메일, 사진" />
</ListItemButton>
</ListItem>
</List>
| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
— | Usually ListItemIcon + ListItemText + optional trailing icon |
selected |
boolean |
false |
Highlighted (current/active item in nav) |
disabled |
boolean |
false |
Non-interactive; greyed |
dense |
boolean |
false |
Reduce vertical padding |
divider |
boolean |
false |
Bottom divider line |
onClick |
(e) => void |
— | Click handler |
href |
string |
— | Renders as <a> (use for nav lists) |
component |
ElementType |
'div' |
Override element (e.g., Link from react-router) |
disableRipple |
boolean |
false |
Skip the click-ripple effect |
States¶
| State | Visual |
|---|---|
| Default | transparent bg, fg-default |
| Hover | bg-subtle |
| Focus-visible | bg-subtle + 2px focus ring inset |
| Active | bg-pressed |
| Selected | bg-selected (~ brand-50/dark mode brand-900-30%), fg-on-selected |
| Selected + hover | bg-selected-hover (slight shift) |
| Disabled | reduced opacity, no hover effect |
Tokens consumed¶
--list-item-min-height-44 /* default touch target */
--list-item-min-height-36 /* dense */
--color-bg-default
--color-bg-subtle /* hover */
--color-bg-pressed /* active */
--color-bg-selected /* selected */
--color-fg-default
--color-fg-on-selected
--space-md /* horizontal padding */
--motion-duration-100 /* hover/active transition */
Accessibility¶
- Default semantic:
<div role="button">withtabIndex=0. Withhref: renders<a>(preferred for nav). selected={true}→aria-selected="true". For nav lists also addaria-current="page"when the row matches the current route.- Keyboard: Enter / Space activates. Disabled rows skipped in tab order.
- Touch target: ≥ 44pt mobile (default 48px). Don't use
denseon mobile primary nav. - Cite
knowledge/a11y/keyboard-and-focus.md.
Edge cases¶
- Long primary text — wraps to 2 lines, then ellipsis. For nav rows, prefer concise labels.
href+onClick—onClickfires before navigation; useful for analytics.- Inside React Router — pass
component={RouterLink}+to="/path"for SPA-style nav (no full page reload). - Selected without persistence —
selectedis for the current item. For multi-select (checkbox lists), useCheckboxinside the row instead ofselected. - Korean labels — slightly more vertical padding looks less cramped (Hangul reads taller). Default 48px row height works; resist
densefor Korean nav.
Code example¶
function SettingsNav({ section }: { section: string }) {
const items = [
{ id: 'profile', label: '프로필', icon: <PersonIcon /> },
{ id: 'notifications', label: '알림 설정', icon: <BellIcon /> },
{ id: 'privacy', label: '개인정보', icon: <LockIcon /> },
{ id: 'billing', label: '결제 정보', icon: <CardIcon /> },
];
return (
<List component="nav" aria-label="설정">
{items.map((it) => (
<ListItem key={it.id} disablePadding>
<ListItemButton
component={Link}
to={`/settings/${it.id}`}
selected={section === it.id}
>
<ListItemIcon>{it.icon}</ListItemIcon>
<ListItemText primary={it.label} />
</ListItemButton>
</ListItem>
))}
</List>
);
}
Don't¶
- Don't use
ListItemButtonoutside aListItemparent — semantics break. - Don't make the entire
ListItemclickable viaonClickonListItem— wrap the inner content inListItemButtonso secondary actions stay separately focusable. - Don't combine
ListItemButtonwith form controls (Checkbox, Switch) inside the same clickable area — a click on the form control conflicts with the row click. - Don't override
selectedstyling without keeping enough contrast (color-bg-selectedshould be ≥ 3:1 againstcolor-bg-default).
References¶
- MUI:
ListItemButton