Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

Components

Icon Button

Icon buttons are used to trigger actions and are typically found in toolbars, cards, and dialogs.

Full support Supported since v125. Full support Supported since v128. Full support Supported since v18.
<button class="icon-button" aria-label="Delete">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M13.5 6.5V7h5v-.5a2.5 2.5 0 0 0-5 0m-2 .5v-.5a4.5 4.5 0 1 1 9 0V7H28a1 1 0 1 1 0 2h-1.508L24.6 25.568A5 5 0 0 1 19.63 30h-7.26a5 5 0 0 1-4.97-4.432L5.508 9H4a1 1 0 0 1 0-2zm2.5 6.5a1 1 0 1 0-2 0v10a1 1 0 1 0 2 0zm5-1a1 1 0 0 0-1 1v10a1 1 0 1 0 2 0v-10a1 1 0 0 0-1-1">
</path>
</svg>
</button>
<button class="icon-button" aria-label="Edit">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M21.65 3.434a4.889 4.889 0 1 1 6.915 6.914l-.902.901l-6.914-6.914zM19.335 5.75L4.357 20.73a3.7 3.7 0 0 0-1.002 1.84l-1.333 6.22a1 1 0 0 0 1.188 1.188l6.22-1.333a3.7 3.7 0 0 0 1.84-1.002l14.98-14.98z">
</path>
</svg>
</button>

Variants

Icon buttons come in three variants: outlined, tonal and filled.

<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
<button class="icon-button outlined" aria-label="Outlined">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M5 12h14" />
</svg>
</button>
<button class="icon-button tonal" aria-label="Tonal">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M5 12h14" />
</svg>
</button>
<button class="icon-button filled" aria-label="Filled">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M5 12h14" />
</svg>
</button>
<button class="icon-button primary outlined" aria-label="Primary Outlined">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M5 12h14" />
</svg>
</button>
<button class="icon-button primary tonal" aria-label="Primary Tonal">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M5 12h14" />
</svg>
</button>
<button class="icon-button primary filled" aria-label="Primary Filled">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5v14M5 12h14" />
</svg>
</button>
</div>

Colors

Add a .primary or .critical class to apply a brand or destructive color. The default is a neutral gray.

<button class="icon-button primary filled" aria-label="Primary">
<svg fill="none" height="20" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
viewBox="0 0 24 24" width="20" xmlns="http://www.w3.org/2000/svg">
<path d="M5 12h14"></path>
<path d="M12 5v14"></path>
</svg>
</button>
<button class="icon-button critical filled" aria-label="Critical">
<svg fill="none" height="20" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
viewBox="0 0 24 24" width="20" xmlns="http://www.w3.org/2000/svg">
<path d="M5 12h14"></path>
<path d="M12 5v14"></path>
</svg>
</button>

aria-label?

Yes! In order for screen readers to understand what the button is for we can add aria-label to the <button> element.

Read more: Icon Button accessibility.

Sizes

Make the button smaller with the .small modifier.

<button class="icon-button small" aria-label="Edit">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M21.65 3.434a4.889 4.889 0 1 1 6.915 6.914l-.902.901l-6.914-6.914zM19.335 5.75L4.357 20.73a3.7 3.7 0 0 0-1.002 1.84l-1.333 6.22a1 1 0 0 0 1.188 1.188l6.22-1.333a3.7 3.7 0 0 0 1.84-1.002l14.98-14.98z">
</path>
</svg>
</button>
<button class="icon-button" aria-label="Edit">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M21.65 3.434a4.889 4.889 0 1 1 6.915 6.914l-.902.901l-6.914-6.914zM19.335 5.75L4.357 20.73a3.7 3.7 0 0 0-1.002 1.84l-1.333 6.22a1 1 0 0 0 1.188 1.188l6.22-1.333a3.7 3.7 0 0 0 1.84-1.002l14.98-14.98z">
</path>
</svg>
</button>

Disabled

Prevent user interaction by adding the disabled attribute.

<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
<button class="icon-button" disabled aria-label="Delete">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M13.5 6.5V7h5v-.5a2.5 2.5 0 0 0-5 0m-2 .5v-.5a4.5 4.5 0 1 1 9 0V7H28a1 1 0 1 1 0 2h-1.508L24.6 25.568A5 5 0 0 1 19.63 30h-7.26a5 5 0 0 1-4.97-4.432L5.508 9H4a1 1 0 0 1 0-2zm2.5 6.5a1 1 0 1 0-2 0v10a1 1 0 1 0 2 0zm5-1a1 1 0 0 0-1 1v10a1 1 0 1 0 2 0v-10a1 1 0 0 0-1-1">
</path>
</svg>
</button>
<button class="icon-button outlined" disabled aria-label="Delete">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M13.5 6.5V7h5v-.5a2.5 2.5 0 0 0-5 0m-2 .5v-.5a4.5 4.5 0 1 1 9 0V7H28a1 1 0 1 1 0 2h-1.508L24.6 25.568A5 5 0 0 1 19.63 30h-7.26a5 5 0 0 1-4.97-4.432L5.508 9H4a1 1 0 0 1 0-2zm2.5 6.5a1 1 0 1 0-2 0v10a1 1 0 1 0 2 0zm5-1a1 1 0 0 0-1 1v10a1 1 0 1 0 2 0v-10a1 1 0 0 0-1-1">
</path>
</svg>
</button>
<button class="icon-button tonal" disabled aria-label="Delete">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M13.5 6.5V7h5v-.5a2.5 2.5 0 0 0-5 0m-2 .5v-.5a4.5 4.5 0 1 1 9 0V7H28a1 1 0 1 1 0 2h-1.508L24.6 25.568A5 5 0 0 1 19.63 30h-7.26a5 5 0 0 1-4.97-4.432L5.508 9H4a1 1 0 0 1 0-2zm2.5 6.5a1 1 0 1 0-2 0v10a1 1 0 1 0 2 0zm5-1a1 1 0 0 0-1 1v10a1 1 0 1 0 2 0v-10a1 1 0 0 0-1-1">
</path>
</svg>
</button>
<button class="icon-button filled" disabled aria-label="Delete">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<path fill="currentColor"
d="M13.5 6.5V7h5v-.5a2.5 2.5 0 0 0-5 0m-2 .5v-.5a4.5 4.5 0 1 1 9 0V7H28a1 1 0 1 1 0 2h-1.508L24.6 25.568A5 5 0 0 1 19.63 30h-7.26a5 5 0 0 1-4.97-4.432L5.508 9H4a1 1 0 0 1 0-2zm2.5 6.5a1 1 0 1 0-2 0v10a1 1 0 1 0 2 0zm5-1a1 1 0 0 0-1 1v10a1 1 0 1 0 2 0v-10a1 1 0 0 0-1-1">
</path>
</svg>
</button>
</div>

Accessibility

To have an accessible label you can choose between three approaches.

Variant Usage in Icon Button component
Add a aria-label on the <button> element Default behavior.
Provide a label inside the <button> element Not used (but possible with the .sr-only util).
Have a visible label that you reference with aria-labelledby Not used.

Anatomy

  1. Container: <button class="icon-button">
  2. Icon: <svg>
  3. Label text: <button aria-label="">

API

Type Modifiers Default Description
Variants .outlined, .tonal, .filled - The visual style of the icon button.
Sizes .small - The size of the element.
Colors .critical, .primary - Color modifiers for the icon button. Default is a neutral gray.
States [disabled] - Attribute to disable the icon button.

Browser support

Full support Supported since v125. Full support Supported since v128. Full support Supported since v18.

See also the full browser support guide.

Installation

@layer components.root {
:where(.icon-button) {
--_color: light-dark(var(--color-16), var(--color-1));
--_color-contrast: light-dark(var(--color-1), var(--color-16));
--_color-tonal: var(--surface-tonal);
--_accent: var(--_color);
--_accent-contrast: var(--_color-contrast);
--_accent-tonal: var(--_color-tonal);
--_accent-tonal-contrast: var(--_color);
--_default-hover-bg-color: light-dark(oklch(from var(--_accent) l 0.01 h / 10%),
oklch(from var(--_accent) l 0.01 h / 20%));
--_default-active-bg-color: light-dark(oklch(from var(--_accent) l 0.06 h / 20%),
oklch(from var(--_accent) l 0.06 h / 30%));
--_filled-hover-bg-color: light-dark(oklch(from var(--_accent) calc(l + 0.2) c h),
oklch(from var(--_accent) calc(l - 0.1) c h));
--_filled-active-bg-color: light-dark(oklch(from var(--_accent) calc(l + 0.3) c h),
oklch(from var(--_accent) calc(l - 0.15) c h));
--_outlined-active-bg-color: var(--_filled-active-bg-color);
--_bg-color: transparent;
--_border-color: transparent;
--_border-width: var(--border-size-1);
--_text-color: inherit;
align-items: center;
aspect-ratio: 1;
background-color: var(--_bg-color);
block-size: var(--size-6);
border: var(--_border-width) solid var(--_border-color);
border-radius: var(--radius-round);
color: var(--_text-color);
display: inline-flex;
inline-size: var(--size-6);
justify-content: center;
padding: 0;
transform-style: preserve-3d;
transition: background-color .1s ease, color .1s ease,
border-color .1s ease, box-shadow .1s ease;
&:where([disabled]) {
--_text-color: light-dark(rgb(0, 0, 0/0.3), rgb(255, 255, 255/0.26));
cursor: not-allowed;
opacity: 0.64;
}
svg {
block-size: auto;
inline-size: auto;
max-block-size: var(--size-5);
max-inline-size: var(--size-5);
pointer-events: none;
}
/* Ripple effect, utils.css */
&::before {
--highlight-size: 130%;
background-color: transparent;
@media (prefers-reduced-motion: no-preference) {
transition: transform 0.2s ease, background-color .1s ease;
}
}
/* Colors — shared interaction model for critical and primary.
Each color modifier below only swaps --_color; the tonal surface,
contrast text, and hover/active strategy are identical.
The .critical scope class in theme.css redirects --palette-source so
--color-6 below becomes a tint of the active color. */
&.critical,
&.primary {
--_color-contrast: var(--gray-1);
--_color-tonal: var(--color-6);
--_accent-tonal-contrast: var(--text-primary-contrast);
--_default-hover-bg-color: oklch(from var(--_accent) l c h / 15%);
--_default-active-bg-color: oklch(from var(--_accent) l c h / 25%);
--_filled-hover-bg-color: oklch(from var(--_accent) calc(l - 0.1) c h);
--_filled-active-bg-color: oklch(from var(--_accent) calc(l - 0.15) c h);
--_outlined-active-bg-color: oklch(from var(--_accent) calc(l - 0.1) c h);
}
&.critical {
--_color: var(--critical);
}
&.primary {
--_color: var(--primary);
}
/* Element states */
&:where(:not([disabled])) {
&:where(:not(:active):hover) {
&::before {
background-color: var(--_interaction-hover-color, var(--_default-hover-bg-color));
}
}
&:where(:hover:active),
&[aria-current="page"] {
&::before {
background-color: var(--_interaction-active-color, var(--_default-active-bg-color));
}
}
}
/* Size */
&.small {
block-size: var(--size-4);
inline-size: var(--size-4);
svg {
max-block-size: var(--size-4);
max-inline-size: var(--size-4);
}
}
/* Variants */
&.outlined {
--_bg-color: transparent;
--_border-color: var(--_accent);
--_text-color: var(--_accent);
}
&.tonal {
--_bg-color: var(--_accent-tonal);
--_text-color: var(--_accent-tonal-contrast);
}
&.filled {
--_bg-color: var(--_accent);
--_interaction-active-color: var(--_filled-active-bg-color);
--_interaction-hover-color: var(--_filled-hover-bg-color);
--_text-color: var(--_accent-contrast);
}
}
}