Gesso
Actions

Icon button

Icon-only ghost button. The label is both the tooltip and the accessible name.

Icon button runs an action from an icon alone, for toolbars, headers, and rows where a word would not fit. It is a ghost Button in a square box, wrapped in a Tooltip, and its required label becomes both the tooltip and the name a screen reader announces. It has no fill until the pointer is over it, and its corners follow the look's rounded-control, so it is a rounded square in Washi and a circle in ICS.

Usage

import { X } from '@phosphor-icons/react';
import { IconButton } from '@/components/ui/icon-button';

<IconButton icon={X} label="Close" onClick={handleClose} />

Each icon button brings its own tooltip provider, so it needs no wrapper. icon takes a component rather than an element, so set it from a client component, because a Server Component cannot pass a component to a client one.

The label is the name

label is a required string, and Icon button uses it twice. It is set as the button's aria-label, so a screen reader announces it, and it is the text of the tooltip that opens on hover and on keyboard focus. Write it as the action the button takes, like "Close" or "Add to favorites", rather than the name of the icon. The icon is hidden from assistive tech, and the button takes no children or aria-label of its own, so the label is the only name it has.

Three sizes

size sets a square box on Button's heights: sm is 36px and the default, md is 40px, and xl is 56px. Button's default is md, so match the buttons beside it, and a back arrow next to default-size actions takes md. The glyph is 20px, or 24px at xl, and iconSize sets your own. On a touch screen the box grows to at least 44px tall and keeps its width, so sm is 36px wide by 44px tall there.

Where the tooltip opens

tooltipSide opens the tooltip on the top by default, or on the right, bottom, or left, and the tooltip flips to the opposite side when there is no room. It opens as soon as the pointer arrives, and tooltipDelay sets a wait in milliseconds. Inside a TooltipProvider it takes the provider's delay unless you set one. It also opens when the button takes keyboard focus, and it closes when the button is pressed, when focus leaves, when the pointer leaves both the button and the tooltip, or on Escape. On a touch screen a tap runs the action and the tooltip does not open. Hover each button to see its side.

Props

Icon button takes every <button> attribute except children and aria-label, plus these.

PropTypeDefaultWhat it does
iconElementTypeRequired. The icon component to draw, sized to the box.
labelstringRequired. The accessible name and the tooltip text.
size"sm" | "md" | "xl""sm"A square box 36, 40, or 56px wide, and at least 44px tall on a touch screen.
iconSizenumber | stringFollows sizeSets the icon's width and height, in place of 20px, or 24px at xl.
tooltipSide"top" | "right" | "bottom" | "left""top"The side the tooltip opens on, before it flips for room.
tooltipDelaynumberThe provider's, or 0Milliseconds the pointer rests on the button before the tooltip opens.
tone"neutral" | "primary" | "support" | "danger" | "inherit""neutral"The hue, as on Button. A tone other than neutral draws the icon in the tone's own color, text-tone, and washes the hover, so danger gives a delete the destructive red.
type"button" | "submit" | "reset""button"As on <button>, except it never submits a form unless you set "submit".

It has no variant, loading, or asChild, so it is always a ghost <button>, and its ref reaches that element.

Accessibility

  • It renders a native <button>, so Enter and Space press it, and type="button" keeps it from submitting a form by accident.
  • label is the accessible name, set as aria-label, and the icon is aria-hidden, so the icon adds nothing to the name.
  • The tooltip opens on keyboard focus as well as on hover, and Escape closes it without moving focus. While it is open the button points at it with aria-describedby, and the tooltip has role="tooltip".
  • The pointer can move from the button onto the tooltip without closing it.
  • On a touch screen the tooltip does not open, so there the icon carries the meaning alone. Use an icon people already know, or a Button with a word.
  • disabled sets the native attribute, so the button leaves the tab order and assistive tech reports it as unavailable. It also ignores the pointer, so its tooltip cannot open and a sighted user has only the icon to go on.
  • Focus shows a 2px ring on :focus-visible only, offset from the edge, in the contrast ink rather than the brand color, so it reads on every look.
  • In forced colors the button keeps a transparent outline that the system paints, so focus stays visible in Windows high contrast.
  • On a touch screen it is at least 44px tall, and it keeps its width, so sm and md are narrower than 44px there. Use xl where a full 44px square matters.
  • Under reduced motion the hover color changes without a transition, and the tooltip appears without its fade.

When to use something else

  • Button when there is room for a word, or for the main action in a view. Its iconBefore keeps the icon and puts the label on screen.
  • Toggle for an icon that stays on or off, like bold or a pinned filter, because it reports whether it is pressed.
  • A link styled with buttonVariants({ variant: "ghost", size: "icon" }) inside a Tooltip, for an icon that navigates. Icon button always renders a <button>, so give the link its own aria-label.

Install

Installing it also adds Button and Tooltip, which it is built from.

shadcn add @gesso/icon-button

On this page