Zetta v1.6

Components · Actions

Icon button

A square, label-free action for toolbars and dense surfaces — a 32px ghost button holding a single 16px icon. Because it shows no text, an accessible name is mandatory.

Preview

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Icon button" component to this project using the zetta-design-md skill.

WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `icon-button` spec (variants: default, hover, disabled) and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `icon-button` spec in this project's stack.

AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.

ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility.

Anatomy & rules

┌──────────────────┐
│       [icon]     │  ← 32×32px container
└──────────────────┘

Parts:

  • Container — 32×32px, transparent at rest, carries hover/active fill and focus ring
  • Icon — 16px Material Symbol, inherits {colors.ink}
State Token set Visual change
Default button-icon Transparent background, {colors.ink} icon
Hover button-icon-hover {colors.primary-bg} fill, {colors.primary} icon
Active button-icon-active {colors.surface-pressed} fill
Focus button-icon-focus 2px solid {colors.border-focus} outline
Selected button-icon-selected {colors.primary-bg} fill, {colors.primary} icon, 1px solid {colors.primary} border
Disabled button-icon-disabled {colors.disabled-text} icon, not-allowed cursor, 50% opacity

Primary implementation reference: Table Action column. See Table component for usage context.

When to use

  • Toolbar and action bar controls where label text would not fit.
  • Secondary actions in dense list rows (edit, delete, more).

When not to use

  • When the icon's meaning is not immediately obvious — add a label or use a standard button.
  • As the primary action on a page — use button-primary instead.

Do

  • Always provide aria-label with an action verb ("Edit", "Delete", "More options").
  • Pair with a tooltip to surface the label on hover.
  • Use {icons.sizeMap.md} (16px) — do not override icon size inside this button.

Don't

  • Don't use for navigation — use a link.
  • Don't override width/height to make it smaller than 32×32px.
  • Always provide aria-label with an action verb: "Edit", "Delete", "More options", "Close".
  • Tooltip label must match aria-label exactly — do not paraphrase.
  • Use universally recognizable icons only: edit, delete, close, more_vert, add, search, visibility.
  • Loading label: "Loading…" (visually hidden via aria-live).

Keyboard

Key Behavior
Tab Move focus to the button
Enter / Space Activate the button action

Accessibility

  • ARIA role: implicit role="button" on <button>. Add role="button" and tabindex="0" if using a non-button element.
  • Required: aria-label — the icon alone is not accessible.
  • Loading state: add aria-busy="true" and a visually hidden label ("Loading…") during loading.
  • Contrast: icon color {colors.ink} on transparent surface meets 3:1 UI element threshold.

Spec

Values and token references resolved straight from the Zetta spec.

button-icon
backgroundColor
transparent
iconColor
{colors.ink}
borderColor
transparent
borderWidth
1px
borderRadius
{rounded.base}
width
32px
height
32px
iconSize
{icons.sizeMap.md}
cursor
pointer
display
flex
alignItems
center
justifyContent
center
button-icon-hover
backgroundColor
{colors.primary-bg}
textColor
{colors.primary}
button-icon-active
backgroundColor
{colors.surface-pressed}
button-icon-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-icon-selected
backgroundColor
{colors.primary-bg}
iconColor
{colors.primary}
borderColor
{colors.primary}
borderWidth
1px
button-icon-disabled
iconColor
{colors.disabled-text}
cursor
not-allowed
opacity
0.5

Known gaps

  • Shape variant (circle) — not yet specified. Currently always {rounded.base}.
  • Compact size (24×24px) — not yet specified as a token variant.