Sakuga Back to Sakuga
Theme
Sound
Jump to a component

Core · Actions

IconButton

IconButton is a circular control holding one icon, with a required accessible label.

@anicanon/sakuga/uiv1.0Used onWeb · iOS · Android

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.
Not for
  • An action whose meaning needs a wordButton
  • A glyph that only decoratesIcon
  • Asking MakiAskMaki

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.
  • pressed turns it into a toggle (aria-pressed); the pressed glyph takes icon-button-pressed-fg and settles in once when turned on. Use it for Like and Captions.
  • Pressing sinks it to 90% on motion-instant; it settles back on motion-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 href it 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".
IconButtonExample.jsxReact
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

labelrequired
string
Required accessible name, e.g. "Share EBA".
icon
IconName
Leave out when passing custom children (the CC box).
variant
'solid' | 'media' | 'plain' | 'primary'
solid on desktop, media over video or pages, plain inside a bar, primary for the one action as an icon alone.
size
'md' | 'lg'
iconSize
number | string
Pixels, or a size token: SearchField's clear passes var(--search-clear-icon-size), where index.d.ts still says number.
pressed
boolean
Makes it a toggle (aria-pressed). A link is never a toggle: a pressed link is the current one (aria-current).
attention
boolean
Something waits behind it, e.g. unread notifications on the bell: the state dot on the glyph's corner. Say how many in the label.
href
string
Renders a link instead of a button, for an icon that goes somewhere (Back, Open in Cut). With disabled it stays a button.
children
ReactNode
className
string

An IconName is any of the names Icon lists.

IconButtonExample.swiftiOS · coming soon
IconButtonExample.ktAndroid · coming soon

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#ececf0
bg-surface-raisedneutral-875#1a1a21
icon-button-solid-hover-bgIconButton solid: hover fill.
bg-surface-hoverneutral-150#e1e1e7
bg-surface-hoverneutral-850#26262e
icon-button-fgIconButton: glyph colour for solid, glass and plain.
text-primaryneutral-925#0b0a10
text-primaryneutral-0#ffffff
icon-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#ffffff
text-on-medianeutral-0#ffffff
icon-button-media-focusIconButton media: focus ring over video, both themes.
text-on-medianeutral-0#ffffff
text-on-medianeutral-0#ffffff
icon-button-pressed-fgIconButton: glyph when toggled on (a liked heart).
accentcamellia-600#c2335f
accentcamellia-400#f4779f
ib-primary-bgIconButton: the primary action as an icon alone, filled (a phone's floating action).
action-primaryneutral-925#0b0a10
action-primaryneutral-0#ffffff

Space and shape

icon-button-size
size-control-md
44px
icon-button-size-lg
size-control-lg
52px
icon-button-radius
radius-full
9999px
icon-button-active-scale
scale-press-sm
.94
icon-button-glass-blur
blur-glass
blur(22px) saturate(1.8)
icon-button-glass-inset-ring-width
line-hairline
1px
icon-button-media-blur
blur-glass
blur(22px) saturate(1.8)
icon-button-media-inset-ring-width
line-hairline
1px
icon-button-disabled-opacity
opacity-disabled
0.35
icon-button-icon-size
icon-size-md
20px
icon-button-primary-hover-filter
its own value
brightness(1.08)
icon-button-after-attention-inset
inset-sm
8px
icon-button-forced-edge-width
line-hairline
1px

Motion

icon-button-duration
motion-fast
160ms
icon-button-easing
ease-standard
cubic-bezier(0.2, 0, 0, 1)
icon-button-active-duration
motion-instant
100ms
icon-button-flip-settle-animation
motion-settle
sk-settle 240ms cubic-bezier(0, 0, 0, 1)

Checked 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 with label; without one it has no name, and with pressed set 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. With href and disabled it 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.