Skip to main content

IconButton

Icon-only pressable button. Standard, filled, outlined, and tonal variants. Toggle mode with selectedIcon. Optionally swap to an MD3 ripple via enableRipple.

Quick start
import { IconButton } from "mates-ui";
import { SettingsIcon } from "mates-icons";





IconButton({ icon: SettingsIcon(), variant: "filled", label: "Settings" });

// MD3 ripple instead of the default soft bloom:
IconButton({ icon: SettingsIcon(), variant: "filled", label: "Settings", enableRipple: true });

Variants

import { html, renderApp } from 'mates';
import { IconButton } from 'mates-ui';
import { HomeIcon, SettingsIcon, EditIcon, StarIcon } from 'mates-icons';

const App = () => () => html`
<x-row gap="var(--md-space-2)">
${IconButton({ icon: HomeIcon(), variant: "standard", label: "Home" })}
${IconButton({ icon: SettingsIcon(), variant: "filled", label: "Settings" })}
${IconButton({ icon: EditIcon(), variant: "outlined", label: "Edit" })}
${IconButton({ icon: StarIcon(), variant: "tonal", label: "Star" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

Sizes

import { html, renderApp } from 'mates';
import { IconButton } from 'mates-ui';
import { StarIcon } from 'mates-icons';

const App = () => () => html`
<x-col gap="var(--md-space-3)">
<x-row gap="var(--md-space-2)">
${IconButton({ icon: StarIcon(), size: "sm", variant: "standard", label: "Standard" })}
${IconButton({ icon: StarIcon(), size: "sm", variant: "filled", label: "Filled" })}
${IconButton({ icon: StarIcon(), size: "sm", variant: "outlined", label: "Outlined" })}
${IconButton({ icon: StarIcon(), size: "sm", variant: "tonal", label: "Tonal" })}
</x-row>
<x-row gap="var(--md-space-2)">
${IconButton({ icon: StarIcon(), size: "md", variant: "standard", label: "Standard" })}
${IconButton({ icon: StarIcon(), size: "md", variant: "filled", label: "Filled" })}
${IconButton({ icon: StarIcon(), size: "md", variant: "outlined", label: "Outlined" })}
${IconButton({ icon: StarIcon(), size: "md", variant: "tonal", label: "Tonal" })}
</x-row>
<x-row gap="var(--md-space-2)">
${IconButton({ icon: StarIcon(), size: "lg", variant: "standard", label: "Standard" })}
${IconButton({ icon: StarIcon(), size: "lg", variant: "filled", label: "Filled" })}
${IconButton({ icon: StarIcon(), size: "lg", variant: "outlined", label: "Outlined" })}
${IconButton({ icon: StarIcon(), size: "lg", variant: "tonal", label: "Tonal" })}
</x-row>
</x-col>
`;

renderApp(App, document.getElementById('app'));

Toggle mode

import { html, atom, renderApp } from 'mates';
import { IconButton } from 'mates-ui';
import { FavoriteIcon, BookmarkIcon, NotificationsIcon, NotificationsActiveIcon } from 'mates-icons';

const App = () => {
const fav = atom(false);
const bookmark = atom(false);

return () => html`
<x-row gap="var(--md-space-2)">
${IconButton({
icon: FavoriteIcon({ fill: 0 }),
selectedIcon: FavoriteIcon({ fill: 1 }),
selected: fav(),
label: "Favorite",
variant: "standard",
on: { click: () => fav.set(!fav()) },
})}
${IconButton({
icon: BookmarkIcon({ fill: 0 }),
selectedIcon: BookmarkIcon({ fill: 1 }),
selected: bookmark(),
label: "Bookmark",
variant: "tonal",
on: { click: () => bookmark.set(!bookmark()) },
})}
${IconButton({
icon: NotificationsIcon(),
selectedIcon: NotificationsActiveIcon(),
selected: false,
label: "Notify",
variant: "outlined",
})}
</x-row>
`;
};

MD3 Ripple (enableRipple)

import { html, atom, renderApp } from 'mates';
import { IconButton } from 'mates-ui';
import { HomeIcon, SettingsIcon, EditIcon, StarIcon, FavoriteIcon } from 'mates-icons';

const App = () => {
const fav = atom(false);

return () => html`
<x-row gap="var(--md-space-2)" wrap>
${IconButton({ icon: HomeIcon(), variant: "standard", label: "Home", enableRipple: true })}
${IconButton({ icon: SettingsIcon(), variant: "filled", label: "Settings", enableRipple: true })}
${IconButton({ icon: EditIcon(), variant: "outlined", label: "Edit", enableRipple: true })}
${IconButton({ icon: StarIcon(), variant: "tonal", label: "Star", enableRipple: true })}
${IconButton({
icon: FavoriteIcon({ fill: 0 }),
selectedIcon: FavoriteIcon({ fill: 1 }),
selected: fav(),
label: "Favorite",
variant: "standard",
enableRipple: true,
on: { click: () => fav.set(!fav()) },
})}
</x-row>
`;
};

renderApp(App, document.getElementById('app'));

Disabled

import { html, renderApp } from 'mates';
import { IconButton } from 'mates-ui';
import { DeleteIcon, EditIcon, ShareIcon, DownloadIcon } from 'mates-icons';

const App = () => () => html`
<x-row gap="var(--md-space-2)">
${IconButton({ icon: DeleteIcon(), disabled: true, label: "Delete" })}
${IconButton({ icon: EditIcon(), disabled: true, variant: "filled", label: "Edit" })}
${IconButton({ icon: ShareIcon(), disabled: true, variant: "outlined", label: "Share" })}
${IconButton({ icon: DownloadIcon(), disabled: true, variant: "tonal", label: "Download" })}
</x-row>
`;

renderApp(App, document.getElementById('app'));

IconButton

import { IconButton } from "mates-ui";
Prop Type Default Description
attr AttrMap HTML attributes on the root element.
classes string | string[] Extra root classes — joined and appended after built-in classes.
data DataAttrMap Root `data-*` map (`testId` → `data-testid`). `testId` prop / `attr` win over `data`.
disabled boolean Disables the button and removes interactivity.
enableBloom boolean true Enables the soft bloom press effect (default press animation). Ignored when enableRipple is true.
enableRipple boolean true When true, replaces the default soft bloom press effect with a Material Design 3-style ripple — a sharp-edged circle that grows from the contact point with hover/focus state-layer support.
href string When set, renders an <a> instead of a <button>.
icon* IconInput Icon — an icon name string or SVG TemplateResult (e.g. "home" or HomeIcon()) rendered inside the button.
id string Root `id` shorthand (`attr.id` wins). Form native-control `id` props are unchanged.
label string Accessible label (becomes aria-label). Required for screen-reader users.
on OnEventMap DOM event handlers on the root element.
onClick (e: MouseEvent) => void Shorthand for `on.click` (`on.click` wins when both are set).
selected boolean Toggles the active/selected visual state.
selectedIcon IconInput Icon shown when selected is true — an icon name string or SVG TemplateResult.
size "sm" | "md" | "lg" "md" Touch-target and icon size.
style StyleMap Inline CSS on the root element.
style StyleMap Root inline CSS (`style` prop; `style` wins if both set).
testId string Overrides default data-testid for testing.
type "button" | "submit" | "reset" "button" Native <button> type attribute.
variant "standard" | "filled" | "outlined" | "tonal" "standard" Visual style of the button.

CSS tokens

Token Role
--md-color-on-primary Filled variant icon colour
--md-color-on-secondary-container Tonal variant icon colour
--md-color-primary Filled variant background
--md-color-secondary-container Tonal variant background
--md-shape-full Circular button shape
--md-state-hover-opacity Hover state layer opacity
--md-state-pressed-opacity Pressed state layer opacity

Caveats

  • `label` is required — it becomes the `aria-label`. Icon-only buttons are inaccessible without it.
  • Toggle mode requires you to control `selected` externally and update it in `on.click`.
  • `selectedIcon` has no effect when `selected` is never toggled.