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
Read more: Icon Button accessibility.
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
- Container:
<button class="icon-button"> - Icon:
<svg> - 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); } }}