Jump to a component
Core · Actions
IconButton
IconButton is a circular control holding one icon, with a required accessible label.
Live, from @anicanon/sakuga 2.0.0
Sakuga for iOS and Android is coming
Its glass example is native on iOS and Android, so it doesn’t draw on the web.
iOS
Coming soon
IconButton for iOS is in the roughs. The tokens it will read are the ones on this page, the same on every platform.
Android
Coming soon
IconButton for Android is in the roughs. The tokens it will read are the ones on this page, the same on every platform.
- Use it for
- An action that one glyph says better than a word: back, share, like, captions, the bell.
Guidelines
- solid on desktop surfaces; plain inside a bar that already has a fill (player controls).
- glass for phone chrome floating over the page (back, share); media over video or manga pages, which stays dark in both themes.
pressedturns it into a toggle (aria-pressed); the pressed glyph takesicon-button-pressed-fgand settles in once when turned on. Use it for Like and Captions.- Pressing sinks it to 90% on
motion-instant; it settles back onmotion-fast. - With sound on, a press plays the press sound (README · Sound).
- 44px by default,
size="lg"52px for the desktop action rail. - The label names the object: "Share EBA", "Next page", "Remind me about episode 2".
- With
hrefit renders a link, for an icon that goes somewhere (Back, Open in Cut); without it, a button. - primary: the one action as an icon alone, filled (a phone's floating New). One per screen.
attention: something waits behind it, such as unread notifications on the bell. The state dot, the same one a current row and a waiting line use, sits on the glyph's upper corner. The label says how many: "Notifications, 3 new".
import { useState } from 'react';
import { IconButton } from '@anicanon/sakuga/ui';
export default function IconButtonExample() {
const [pressed, setPressed] = useState(false);
return (
<>
<IconButton icon="search" label="Search" />
<IconButton icon="share" label="Share EBA" variant="solid" size="lg" />
<IconButton
icon="heart"
label="Like"
pressed={pressed}
onClick={() => setPressed(!pressed)}
/>
<IconButton icon="settings" label="Settings" variant="plain" />
<IconButton
icon="bell"
label="Notifications, 3 new"
variant="plain"
attention
/>
<img
src="/art/eba-key-art.webp"
alt=""
style={{
position: 'absolute',
inset: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
}}
/>
<IconButton icon="back" label="Back" variant="glass" />
<IconButton icon="language" label="Audio and subtitles" variant="media" />
</>
);
}PropsIconButtonProps
labelrequiredstringiconIconNamevariant'solid' | 'media' | 'plain' | 'primary'size'md' | 'lg'iconSizenumber | stringpressedbooleanattentionbooleanhrefstringchildrenReactNodeclassNamestringAn IconName is any of the names Icon lists.
Each token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.
Colour
icon-button-solid-bgIconButton solid: fill (desktop).bg-surface-raisedneutral-100#ececf0bg-surface-raisedneutral-875#1a1a21icon-button-solid-hover-bgIconButton solid: hover fill.bg-surface-hoverneutral-150#e1e1e7bg-surface-hoverneutral-850#26262eicon-button-fgIconButton: glyph colour for solid, glass and plain.text-primaryneutral-925#0b0a10text-primaryneutral-0#fffffficon-button-glass-bgIconButton glass: fill (phone chrome).bg-glassglass-light-a62rgba(250,250,252,0.62)bg-glassglass-dark-a42rgba(28,28,34,0.42)icon-button-glass-borderIconButton glass: edge.border-glasswhite-a70rgba(255,255,255,0.7)border-glasswhite-a16rgba(255,255,255,0.16)icon-button-media-bgIconButton media: fill over video or pages.bg-glass-mediaglass-dark-a42rgba(28,28,34,0.42)bg-glass-mediaglass-dark-a42rgba(28,28,34,0.42)icon-button-media-borderIconButton media: edge.border-glass-mediawhite-a16rgba(255,255,255,0.16)border-glass-mediawhite-a16rgba(255,255,255,0.16)icon-button-media-fgIconButton media: glyph.text-on-medianeutral-0#fffffftext-on-medianeutral-0#fffffficon-button-media-focusIconButton media: focus ring over video, both themes.text-on-medianeutral-0#fffffftext-on-medianeutral-0#fffffficon-button-pressed-fgIconButton: glyph when toggled on (a liked heart).accentcamellia-600#c2335faccentcamellia-400#f4779fib-primary-bgIconButton: the primary action as an icon alone, filled (a phone's floating action).action-primaryneutral-925#0b0a10action-primaryneutral-0#ffffffSpace and shape
icon-button-sizesize-control-mdicon-button-size-lgsize-control-lgicon-button-radiusradius-fullicon-button-active-scalescale-press-smicon-button-glass-blurblur-glassicon-button-glass-inset-ring-widthline-hairlineicon-button-media-blurblur-glassicon-button-media-inset-ring-widthline-hairlineicon-button-disabled-opacityopacity-disabledicon-button-icon-sizeicon-size-mdicon-button-primary-hover-filtericon-button-after-attention-insetinset-smicon-button-forced-edge-widthline-hairlineMotion
icon-button-durationmotion-fasticon-button-easingease-standardicon-button-active-durationmotion-instanticon-button-flip-settle-animationmotion-settleChecked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.
- Result
- No issues found, in light or in dark.
- By design
- A button (a link with
href): Tab reaches it, Enter activates it, and Space does too on a button; the icon is hidden. Name it withlabel; without one it has no name, and withpressedset it is a toggle announced "pressed" or "not pressed". A link is never a toggle: a pressed link is said to be the current one (aria-current) and keeps the pressed look. Withhrefanddisabledit is a disabled button. In forced colours it keeps an edge, and pressed takes Highlight. - Not tested
- The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.