Actions
Icon button
Compact buttons for actions with a clear icon, in four styles, five sizes and three widths, optionally toggling.
- COMPONENT_SPEC §2
- CSS · React
- Open in the playground
Examples
Color configurations
Standard (default) takes the surrounding content color, as in Compose; filled, tonal and outlined have their own. Icon-only controls need an aria-label.
import { Icon, IconButton } from '@kyralabs/m3exp-web/react';
export default function Variants() {
return (
<>
<IconButton variant="standard" size="m" aria-label="Settings">
<Icon name="settings" />
</IconButton>
<IconButton variant="filled" size="m" aria-label="Settings">
<Icon name="settings" />
</IconButton>
<IconButton variant="tonal" size="m" aria-label="Settings">
<Icon name="settings" />
</IconButton>
<IconButton variant="outlined" size="m" aria-label="Settings">
<Icon name="settings" />
</IconButton>
</>
);
}
<button
type="button"
class="m3e-icon-button m3e-icon-button--standard m3e-icon-button--size-m"
aria-label="Settings"
>
<span class="m3e-icon" aria-hidden="true">settings</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-m"
aria-label="Settings"
>
<span class="m3e-icon" aria-hidden="true">settings</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--tonal m3e-icon-button--size-m"
aria-label="Settings"
>
<span class="m3e-icon" aria-hidden="true">settings</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--outlined m3e-icon-button--size-m"
aria-label="Settings"
>
<span class="m3e-icon" aria-hidden="true">settings</span>
</button>
Run the HTML version (CSS classes only).
Sizes and widths
Extra small to extra large, each narrow, default (uniform) or wide.
import { Icon, IconButton } from '@kyralabs/m3exp-web/react';
const SIZES = ['xs', 's', 'm', 'l', 'xl'] as const;
export default function Sizes() {
return (
<>
{SIZES.map((size) => (
<IconButton key={size} variant="filled" size={size} aria-label={`Add (${size})`}>
<Icon name="add" />
</IconButton>
))}
<IconButton variant="tonal" size="m" width="narrow" aria-label="Add (narrow)">
<Icon name="add" />
</IconButton>
<IconButton variant="tonal" size="m" width="wide" aria-label="Add (wide)">
<Icon name="add" />
</IconButton>
</>
);
}
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-xs"
aria-label="Add (xs)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-s"
aria-label="Add (s)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-m"
aria-label="Add (m)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-l"
aria-label="Add (l)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-xl"
aria-label="Add (xl)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--tonal m3e-icon-button--size-m m3e-icon-button--width-narrow"
aria-label="Add (narrow)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--tonal m3e-icon-button--size-m m3e-icon-button--width-wide"
aria-label="Add (wide)"
>
<span class="m3e-icon" aria-hidden="true">add</span>
</button>
Run the HTML version (CSS classes only).
Toggles
With isSelected or defaultSelected an icon button toggles (aria-pressed): the icon fills and the shape morphs.
import { Icon, IconButton } from '@kyralabs/m3exp-web/react';
export default function Toggle() {
return (
<>
<IconButton variant="standard" size="m" aria-label="Favorite" defaultSelected>
<Icon name="favorite" />
</IconButton>
<IconButton variant="filled" size="m" aria-label="Favorite" defaultSelected={false}>
<Icon name="favorite" />
</IconButton>
<IconButton variant="tonal" size="m" aria-label="Bookmark" defaultSelected>
<Icon name="bookmark" />
</IconButton>
<IconButton
variant="outlined"
size="m"
shape="square"
aria-label="Bookmark"
defaultSelected={false}
>
<Icon name="bookmark" />
</IconButton>
</>
);
}
<button
type="button"
class="m3e-icon-button m3e-icon-button--standard m3e-icon-button--size-m"
aria-label="Favorite"
aria-pressed="true"
>
<span class="m3e-icon" aria-hidden="true">favorite</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--filled m3e-icon-button--size-m"
aria-label="Favorite"
aria-pressed="false"
>
<span class="m3e-icon" aria-hidden="true">favorite</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--tonal m3e-icon-button--size-m"
aria-label="Bookmark"
aria-pressed="true"
>
<span class="m3e-icon" aria-hidden="true">bookmark</span>
</button>
<button
type="button"
class="m3e-icon-button m3e-icon-button--outlined m3e-icon-button--size-m m3e-icon-button--square"
aria-label="Bookmark"
aria-pressed="false"
>
<span class="m3e-icon" aria-hidden="true">bookmark</span>
</button>
Run the HTML version (CSS classes only).
Usage
Compact actions with a clear system icon; tooltip on hover (m3.material.io Icon buttons).
API
React
<IconButton>
Icon button (COMPONENT_SPEC §2). With isSelected or defaultSelected it is an IconToggleButton.
| Prop | Type | Default | Description |
|---|---|---|---|
children
required
|
ReactNode
|
The icon, e.g. <Icon name="favorite" />.
|
|
aria-label
|
string
|
Required unless aria-labelledby names the button (icon-only control).
|
|
variant
|
"filled" | "tonal" | "outlined" | "standard"
|
standard |
Color configuration. Default standard (inherits the surrounding content color).
|
vibrant
|
boolean | undefined
|
Token colors for standard/outlined instead of the inherited color (Compose "vibrant"). | |
size
|
"xs" | "s" | "m" | "l" | "xl"
|
s |
Default s (40dp).
|
width
|
"default" | "narrow" | "wide"
|
default |
Default default (uniform).
|
shape
|
"round" | "square"
|
round |
Default round.
|
type
|
'button' | 'submit' | 'reset'
|
||
isSelected
|
boolean
|
||
defaultSelected
|
boolean
|
||
className
|
string
|
||
isDisabled
|
boolean
|
||
onChange
|
(isSelected: boolean) => void
|
||
onPress
|
(e: PressEvent) => void
|
Also accepts HTML attributes not listed here.
<IconToggleButton>
Icon toggle button (COMPONENT_SPEC §2): aria-pressed; the selected icon is filled (FILL 1). Press and selection morph on the default effects spring (IconButton.kt).
| Prop | Type | Default | Description |
|---|---|---|---|
children
required
|
ReactNode
|
The icon, e.g. <Icon name="favorite" />.
|
|
aria-label
|
string
|
Required unless aria-labelledby names the button (icon-only control).
|
|
variant
|
"filled" | "tonal" | "outlined" | "standard"
|
standard |
Color configuration. Default standard (inherits the surrounding content color).
|
vibrant
|
boolean | undefined
|
Token colors for standard/outlined instead of the inherited color (Compose "vibrant"). | |
size
|
"xs" | "s" | "m" | "l" | "xl"
|
s |
Default s (40dp).
|
width
|
"default" | "narrow" | "wide"
|
default |
Default default (uniform).
|
shape
|
"round" | "square"
|
round |
Default round.
|
isSelected
|
boolean
|
||
defaultSelected
|
boolean
|
||
className
|
string
|
||
isDisabled
|
boolean
|
||
onChange
|
(isSelected: boolean) => void
|
||
onPress
|
(e: PressEvent) => void
|
Also accepts HTML attributes not listed here.
iconButtonClassName()
Classes for an icon button (the m3e-css contract).
CSS
icon-button.css
@import '@kyralabs/m3e-css/icon-button.css';
Icon button and icon toggle button (COMPONENT_SPEC §2). No JavaScript needed.
<button class="m3e-icon-button m3e-icon-button--filled" aria-label="Favorite">
<span class="m3e-icon" aria-hidden="true">favorite</span>
</button>
Variants: --standard (default) --filled --tonal --outlined, plus --vibrant for standard and outlined. Sizes: --size-xs --size-s (default) --size-m --size-l --size-xl. Widths: --width-narrow --width-default (default) --width-wide. Shapes: --round (default) --square. aria-pressed makes a toggle; selected icons switch to FILL 1 (COMPONENT_SPEC §2).
66 custom properties it reads
| Custom property | Default |
|---|---|
--m3e-comp-filled-icon-button-color
|
var(--m3e-sys-color-on-primary) |
--m3e-comp-filled-icon-button-color-disabled
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-filled-icon-button-color-selected
|
var(--m3e-sys-color-on-primary) |
--m3e-comp-filled-icon-button-color-unselected
|
var(--m3e-sys-color-on-surface-variant) |
--m3e-comp-filled-icon-button-container-color
|
var(--m3e-sys-color-primary) |
--m3e-comp-filled-icon-button-container-color-selected
|
var(--m3e-sys-color-primary) |
--m3e-comp-filled-icon-button-container-color-unselected
|
var(--m3e-sys-color-surface-container) |
--m3e-comp-filled-tonal-icon-button-color
|
var(--m3e-sys-color-on-secondary-container) |
--m3e-comp-filled-tonal-icon-button-color-disabled
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-filled-tonal-icon-button-color-selected
|
var(--m3e-sys-color-on-secondary) |
--m3e-comp-filled-tonal-icon-button-color-unselected
|
var(--m3e-sys-color-on-secondary-container) |
--m3e-comp-filled-tonal-icon-button-container-color
|
var(--m3e-sys-color-secondary-container) |
--m3e-comp-filled-tonal-icon-button-container-color-selected
|
var(--m3e-sys-color-secondary) |
--m3e-comp-large-icon-button-container-height
|
96px |
--m3e-comp-large-icon-button-container-shape-pressed
|
var(--m3e-sys-shape-corner-large) |
--m3e-comp-large-icon-button-container-shape-square
|
var(--m3e-sys-shape-corner-extra-large) |
--m3e-comp-large-icon-button-icon-size
|
32px |
--m3e-comp-large-icon-button-narrow-leading-space
|
16px |
--m3e-comp-large-icon-button-outlined-outline-width
|
2px |
--m3e-comp-large-icon-button-uniform-leading-space
|
32px |
--m3e-comp-large-icon-button-wide-leading-space
|
48px |
--m3e-comp-medium-icon-button-container-height
|
56px |
--m3e-comp-medium-icon-button-container-shape-pressed
|
var(--m3e-sys-shape-corner-medium) |
--m3e-comp-medium-icon-button-container-shape-square
|
var(--m3e-sys-shape-corner-large) |
--m3e-comp-medium-icon-button-default-leading-space
|
16px |
--m3e-comp-medium-icon-button-icon-size
|
24px |
--m3e-comp-medium-icon-button-narrow-leading-space
|
12px |
--m3e-comp-medium-icon-button-outlined-outline-width
|
1px |
--m3e-comp-medium-icon-button-wide-leading-space
|
24px |
--m3e-comp-minimum-interactive-component-size
|
48px |
--m3e-comp-outlined-icon-button-color
|
var(--m3e-sys-color-on-surface-variant) |
--m3e-comp-outlined-icon-button-color-disabled
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-outlined-icon-button-color-selected
|
var(--m3e-sys-color-inverse-on-surface) |
--m3e-comp-outlined-icon-button-color-unselected
|
var(--m3e-sys-color-on-surface-variant) |
--m3e-comp-outlined-icon-button-container-color-selected
|
var(--m3e-sys-color-inverse-surface) |
--m3e-comp-outlined-icon-button-opacity-disabled
|
0.38 |
--m3e-comp-outlined-icon-button-outline-color
|
var(--m3e-sys-color-outline-variant) |
--m3e-comp-outlined-icon-button-outline-color-unselected
|
var(--m3e-sys-color-outline-variant) |
--m3e-comp-small-icon-button-container-height
|
40px |
--m3e-comp-small-icon-button-container-shape-pressed
|
var(--m3e-sys-shape-corner-small) |
--m3e-comp-small-icon-button-container-shape-square
|
var(--m3e-sys-shape-corner-medium) |
--m3e-comp-small-icon-button-default-leading-space
|
8px |
--m3e-comp-small-icon-button-icon-size
|
24px |
--m3e-comp-small-icon-button-narrow-leading-space
|
4px |
--m3e-comp-small-icon-button-outlined-outline-width
|
1px |
--m3e-comp-small-icon-button-wide-leading-space
|
14px |
--m3e-comp-standard-icon-button-color
|
var(--m3e-sys-color-on-surface-variant) |
--m3e-comp-standard-icon-button-color-disabled
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-standard-icon-button-color-selected
|
var(--m3e-sys-color-primary) |
--m3e-comp-standard-icon-button-color-unselected
|
var(--m3e-sys-color-on-surface-variant) |
--m3e-comp-x-large-icon-button-container-height
|
136px |
--m3e-comp-x-large-icon-button-container-shape-pressed
|
var(--m3e-sys-shape-corner-large) |
--m3e-comp-x-large-icon-button-container-shape-square
|
var(--m3e-sys-shape-corner-extra-large) |
--m3e-comp-x-large-icon-button-default-leading-space
|
48px |
--m3e-comp-x-large-icon-button-icon-size
|
40px |
--m3e-comp-x-large-icon-button-narrow-leading-space
|
32px |
--m3e-comp-x-large-icon-button-outlined-outline-width
|
3px |
--m3e-comp-x-large-icon-button-wide-leading-space
|
72px |
--m3e-comp-x-small-icon-button-container-height
|
32px |
--m3e-comp-x-small-icon-button-container-shape-pressed
|
var(--m3e-sys-shape-corner-small) |
--m3e-comp-x-small-icon-button-container-shape-square
|
var(--m3e-sys-shape-corner-medium) |
--m3e-comp-x-small-icon-button-default-leading-space
|
6px |
--m3e-comp-x-small-icon-button-icon-size
|
20px |
--m3e-comp-x-small-icon-button-narrow-leading-space
|
4px |
--m3e-comp-x-small-icon-button-outlined-outline-width
|
1px |
--m3e-comp-x-small-icon-button-wide-leading-space
|
10px |
Accessibility
<button aria-label>; toggle aria-pressed.
toggle shows FILL 0 unselected, FILL 1 selected.
Every interactive part has a target of at least 48 × 48 px, a visible focus ring for
keyboard focus only, and works under forced-colors and
prefers-reduced-motion.
Sources
Values come from androidx Compose Material3 1.5.0-beta01 at 19991f2c and from
m3.material.io, as cited below from COMPONENT_SPEC §2 and the m3e-css sources.
m3.material.io/components/icon-buttons/overview; *IconButtonTokens.kt.
Verified (generated): {XSmall…XLarge}IconButtonTokens.kt@19991f2c; width = icon + leading + trailing space (IconButtonDefaults.*ContainerSize). Selected: round → SelectedContainerShapeRound, square → SelectedContainerShapeSquare (CornerFull). Colors: standard and outlined inherit the content color (Compose LocalContentColor); --vibrant uses StandardIconButtonTokens / OutlinedIconButtonTokens; filled = FilledIconButtonTokens; tonal = FilledTonalIconButtonTokens; selected outlined toggles drop the border.
icon-button.css sources
(compose/material3 @ 19991f2c):
- Size: IconButtonDefaults.*ContainerSize (IconButtonDefaults.kt): height = ContainerHeight, width = IconSize + leading + trailing space of the width option (Uniform uses the default leading space twice; LargeIconButtonTokens names it UniformLeadingSpace).
- Shapes: {XSmall,Small,Medium,Large,XLarge}IconButtonTokens. Round = CircleShape (half the smaller side). Selected: round → SelectedContainerShapeRound, square → SelectedContainerShapeSquare (CornerFull = round). Press and selection both morph on the default effects spring (IconButton.kt shapeForInteraction: "intentional here to prevent any bounce").
- Colors: IconButtonDefaults. Standard and outlined use LocalContentColor (here: the inherited color) for content and the outline; --vibrant uses StandardIconButtonTokens / OutlinedIconButtonTokens instead (iconButtonVibrantColors). Filled: FilledIconButtonTokens; tonal: FilledTonalIconButtonTokens. Selected outlined toggles drop the border.
- Touch target: ≥ LocalMinimumInteractiveComponentSize in both axes via ::after.