IconButton
A square, icon-only action that always carries an accessible name.
@luc/uiExamples
Four variants
Four variants, from the quiet `ghost` to the destructive `danger`. The label is required rather than optional: an icon-only control without an accessible name is a defect, not a style.
<IconButton label="Copy run id" icon={<RiFileCopyLine />} variant="solid" />
<IconButton label="Run preview" icon={<RiPlayLine />} variant="ghost" />
<IconButton label="Regenerate series" icon={<RiSparklingLine />} variant="accent" />
<IconButton label="Delete run" icon={<RiDeleteBinLine />} variant="danger" />Three sizes
A size is the control's height on the density scale, and the glyph follows it: 16, 18 and 20 pixels. That square geometry is why an icon-only action should not be a `Button` holding one glyph.
<IconButton label="More actions" icon={<RiMoreLine />} size="sm" />
<IconButton label="More actions" icon={<RiMoreLine />} size="md" />
<IconButton label="More actions" icon={<RiMoreLine />} size="lg" />Loading and disabled
`loading` keeps the control's size, blocks activation and reports `aria-busy`; every control in this row stays in the tab order except the disabled one.
<IconButton label="Refreshing" icon={<RiRefreshLine />} loading variant="solid" />
<IconButton label="Refreshing" icon={<RiRefreshLine />} loading />
<IconButton label="Settings" icon={<RiSettings3Line />} disabled />Beside a Button
The size scale is shared, so a `md` IconButton lines up with a `md` Button without a height wrapper — the reason to reach for it instead of styling a button yourself.
<Button variant="secondary">Export CSV</Button>
<IconButton label="Copy share link" icon={<RiLinkM />} />
<IconButton label="More export options" icon={<RiMoreLine />} />Usage
import { IconButton } from "@luc/ui";<IconButton label="Refresh data" icon={<RiRefreshLine />} />Package setup — peer requirements, the stylesheet and the provider — is on installation.
API
Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.
| Export | Kind | Signature |
|---|---|---|
IconButton | 7 documented props | ({ className, variant, size, label, icon, loading, showTooltip, tactile, type, title, onClick, onPointerDown, onPointerEnter, onPointerMove, onPointerLeave, ref, ...props }: IconButtonProps) => impor… |
iconButtonVariants | export | (props?: ({ variant?: "danger" | "accent" | "ghost" | "solid" | null | undefined; size?: "sm" | "md" | "lg" | null | undefined; } & import("class-variance-authority/dist/types").ClassProp) | undefine… |
IconButton props
Type: IconButtonProps
Values the compiler reads from the declaration.
| Prop | Type | Default | Notes |
|---|---|---|---|
icon | ReactNode | — | |
label | string | — | Required: an icon-only control must have an accessible name. |
loading? | boolean | undefined | — | |
showTooltip? | boolean | undefined | — | Show the label as a real tooltip on hover. Use `@luc/ui` Tooltip for rich content. |
size? | "sm" | "md" | "lg" | null | undefined | — | |
tactile? | boolean | undefined | — | Press ink and pointer light. Off for a static or print surface. |
variant? | "danger" | "accent" | "ghost" | "solid" | null | undefined | — |
Guidelines
Do
- Reach for `IconButton` instead of a `Button` holding one glyph: the square geometry, the 18 px glyph and the required `label` come with it.
- Write the label as the action (“Refresh data”), because it is what a screen reader announces and what the hover title shows by default.
- Use `variant="danger"` for a destructive icon action so it is not the same surface as the neutral control beside it.
Don't
- Ship an icon-only control with no accessible name — `label` is required rather than optional for exactly this reason.
- Treat the tooltip as the name: `showTooltip` adds a native `title` on top of `label`, and can be switched off.
- Use an `IconButton` where the action has a word: a `Button` with `leadingIcon` carries both.
Accessibility
`label` becomes `aria-label` on a real `<button>`, and the hover tooltip is a native `title` rather than a second name. `loading` sets `aria-busy`, blocks activation and keeps focus; `disabled` removes the control from the tab order.
Family
Actions
Things a person presses, and the chrome that gathers them.
8 documented entries in this family. Open the family index
Related