/// Inputs
IconButton
A square, icon-only action control — Button's affordances with a required aria-label.
import { IconButton } from "@protocore/pds";Basics
A compact, square button that carries a single icon and nothing else. Because there is no visible text, aria-label is a required prop — TypeScript will flag its absence.
Variants
The same four weights as Button. IconButton defaults to secondary (a quiet outline) rather than primary, since icon-only actions are usually secondary controls.
Sizes
Square sizes match the control scale: sm 32 · md 36 · lg 40 · xl 48. Scale the glyph with the button so it stays optically centred.
In a toolbar
Drop IconButtons into a ButtonGroup to build a seamed toolbar — a common pattern for editors, filters, and row actions.
When to use
Use IconButton when the action is well-understood from its glyph alone and space is tight — toolbars, table row actions, the close affordance on a panel.
- If the meaning isn't unmistakable from the icon, use a text Button (with a
startIconif you like) instead — an ambiguous glyph is worse than a word. - Always supply an
aria-label, and consider pairing the control with a Tooltip so sighted users get the same label on hover. - For a menu of actions behind one trigger, wrap an IconButton with DropdownMenu.
Do & don't
Do
- Give every IconButton a concise, verb-first aria-label ("Delete node", not "Trash").
- Use it for universally-legible glyphs — close, refresh, play, more.
- Pair it with a Tooltip so the label is visible on hover as well as to screen readers.
- Keep icon and button sizes proportional so the glyph stays centred.
Don't
- Don't ship an IconButton without an aria-label — the control is then unnamed.
- Don't use an obscure icon to save a few pixels; prefer a text Button when meaning is unclear.
- Don't put a text label inside — that's a Button with a startIcon.
- Don't reuse the same generic label ("Button") across several actions.
Accessibility
| Keys | Action |
|---|---|
| Tab / Shift+Tab | Move focus onto / off the button. |
| Space or Enter | Activate the button. |
- `aria-label` is required by the type — it becomes the control's accessible name.
- The icon is rendered `aria-hidden`; the label alone names the control.
- `loading` sets `aria-busy` and disables the control while a background action runs.
- Inherits Button's variants and CSS interaction states (no React hover).
Mobile (React Native)
Preview. @protocore/pds-mobile ships the React Native sibling of IconButton. It mirrors the web API where React Native allows; the package is a preview with no device-level QA yet, so pin it and expect small changes.
Import it from the mobile package (not @protocore/pds), inside a <PdsProvider> — there is no stylesheet, so style (a ViewStyle) replaces className and every value comes from the theme:
import { IconButton } from "@protocore/pds-mobile";Parity with web. Same interaction model as the mobile Button — square, icon-only, Pressable-based.
accessibilityLabelis required (typed non-optional) — it replaces web'saria-labelfor the icon-only control.- The icon is passed as
children; press inverts/fills exactly like Button. onPressreplacesonClick.
<IconButton accessibilityLabel="Refresh" onPress={reload}>
<RefreshIcon />
</IconButton>IconButton props
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label * | string | — | Accessible name for the control. Required — an icon-only button has no visible text. |
asChild | boolean | false | Render as the single child element (Radix `Slot`) instead of a `<button>`. |
className | string | — | |
loading | boolean | false | Show an inline spinner, set `aria-busy`, and disable the control while a background action runs. |
size | enum | md | Square control size: sm 32 · md 36 · lg 40 · xl 48. |
style | CSSProperties | — | |
variant | enum | secondary | Visual weight, matching Button: `primary` inverts on hover; `secondary`/`ghost` are quieter; `danger` fills on hover. |