Icon button
Icon button — a button that is only its icon.
Beta · Actions · packages/css/src/components/icon-button.css · React on Base UI: @pepperui/icon-button
.pui-icon-button · shape --round · size --small --large · variant --secondary --ghost --danger. Always aria-label + a Tooltip.
Sizes across, states down.
What it is for
Section titled “What it is for”- Shape: square (radius 8 — actions are squares) or round (radius full — for the one floating action, never the default)
- Size: small, medium, large; five states: default, hover, focus, pressed, disabled — Button’s rules exactly
- Icon is an INSTANCE_SWAP on the Icon placeholder; the real Lucide glyph goes in when the Lucide subset lands
- No label: the accessible name is a Tooltip on hover and an aria-label in code — both, always
- Secondary and ghost are separate sets below, the way Button is split
Use when a toolbar, a table row’s actions, a dialog’s close, a topbar’s notifications — an action whose icon is universally read (close, edit, delete, search, more).
Do not use when the icon is not universally read — then it is a Button with a label and a leading icon; the action is the page’s main one (a labelled primary Button).
Relates to Button (same tokens), Tooltip (its name), Icon, Button group, Topbar, Table.
Anti-patterns an icon button with no tooltip and no aria-label · a primary icon button as the page’s main action · round by default · a 44 icon button in a table row.
Keywords: icon button, toolbar, action, close, edit, more
shadcn: Button size=“icon”
Figma — page Icon button: Icon button (30 variants) · Icon button - secondary (30 variants) · Icon button - ghost (30 variants)
shadcn: button
The markup — copy this
Section titled “The markup — copy this”The class, the parts, and nothing that is not in the package. The same block opens icon-button.css.
<button class="pui-icon-button pui-icon-button--ghost" aria-label="Close"> <span class="pui-icon">…lucide x…</span></button>The modifiers
Section titled “The modifiers”| What it is | Note | |
|---|---|---|
| Shape | (none) square, radius 8 · --round, radius full — for the one floating action, never the default | — |
| Size | --small 28 · (none) medium 36 · --large 44 (icon 24) | — |
| Variant | (none) primary · --secondary · --ghost · --danger | — |
Rule — no label: aria-label in the markup and a Tooltip on hover — both, always.
Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.
Contrast
Section titled “Contrast”| ink on ground | reads | floor | the matrix calls it | |
|---|---|---|---|---|
#544d4d on #ffffff |
8.24 | 4.5 | text.muted on surface.page (promised) | ok |
1 distinct pair(s) rendered, 0 that axe calls a violation.
The focus ring
Section titled “The focus ring”12 of 12 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured.
Keyboard
Section titled “Keyboard”Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.
Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.