Icon Button
IconButton from @volvo-cars/react-icons renders a medium (default) 40×40 px button containing a 16×16 px icon. Every icon button requires aria-label or aria-labelledby since there is no visible text — the types enforce this.
import { IconButton } from '@volvo-cars/react-icons';
export function IconButtonVariants() { return ( <div className="flex flex-wrap gap-16 items-center"> <IconButton icon="x" aria-label="Close" variant="filled" /> <IconButton icon="x" aria-label="Close" variant="outlined" /> <IconButton icon="x" aria-label="Close" variant="clear" /> </div> );}Variants
Section titled “Variants”Filled
Section titled “Filled”High emphasis. Use on media surfaces (images, video) to guarantee contrast.
Outlined
Section titled “Outlined”Medium emphasis. Default variant. Use when the button needs more prominence than clear but less than filled.
Low emphasis. Only use where context and placement make the interactive nature obvious, such as navigation bars.
Options
Section titled “Options”destructive— delete, remove, or cancel actions. Available on all variants.subtle— gray surface with primary text. Filled only.
Omit color for the default neutral appearance.
import { IconButton } from '@volvo-cars/react-icons';
export function IconButtonColors() { return ( <div className="flex flex-wrap gap-16 items-center"> <IconButton icon="x" aria-label="Close" variant="filled" /> <IconButton icon="trash-can" aria-label="Delete" variant="filled" color="destructive" /> <IconButton icon="cogs" aria-label="Settings" variant="filled" color="subtle" /> </div> );}Clear icon buttons in medium size (default) take up 40×40 px. Set bleed to add negative margin so the button visually aligns with surrounding content while retaining the full 40×40 px touch area and hover state. Only available on the clear variant.
import { IconButton } from '@volvo-cars/react-icons';
export function IconButtonBleed() { return ( <div className="flex-col gap-16"> <p className="font-medium font-14">With bleed</p> <div className="flex gap-24"> <IconButton icon="heart" aria-label="Favorite" variant="clear" bleed /> <IconButton icon="share" aria-label="Share" variant="clear" bleed /> </div> <p className="font-medium font-14">Without bleed</p> <div className="flex gap-24"> <IconButton icon="heart" aria-label="Favorite" variant="clear" /> <IconButton icon="share" aria-label="Share" variant="clear" /> </div> </div> );}Rendering as an anchor
Section titled “Rendering as an anchor”Pass href to render an <a> instead of a <button>:
import { IconButton } from '@volvo-cars/react-icons';
export function IconButtonLink() { return ( <div className="flex gap-16 items-center"> <IconButton icon="info-circled" href="#info" aria-label="More info" variant="filled" /> <IconButton icon="cogs" href="#settings" aria-label="Settings" variant="outlined" /> <IconButton icon="arrow-up" href="#top" aria-label="Back to top" variant="clear" /> </div> );}Rendering a custom component with asChild
Section titled “Rendering a custom component with asChild”Use asChild to render onto a framework link component like Next.js Link:
import { IconButton } from '@volvo-cars/react-icons';import Link from 'next/link';
<IconButton icon="cogs" aria-label="Settings" asChild> <Link href="/settings" /></IconButton>;States and interaction
Section titled “States and interaction”Play/pause toggle
Section titled “Play/pause toggle”Buttons that toggle state should update aria-label to describe the resulting action rather than using aria-pressed. Set data-color-mode="dark" when the button sits on dark media.
import { IconButton } from '@volvo-cars/react-icons';import { useState } from 'react';
export function IconButtonPlayPause() { const [playing, setPlaying] = useState(false); return ( <div data-color-mode="dark" className="bg-always-black p-24 shape-default"> <IconButton icon={playing ? 'pause' : 'play'} aria-label={playing ? 'Pause' : 'Play'} variant="filled" onClick={() => setPlaying(!playing)} /> </div> );}Disabled
Section titled “Disabled”Prefer the disabled prop (which sets aria-disabled) over the native disabled attribute — it keeps the button focusable for screen readers and prevents click events internally. Disabled icon buttons get reduced opacity automatically.
import { IconButton } from '@volvo-cars/react-icons';
export function IconButtonDisabled() { return ( <div className="flex flex-wrap gap-16 items-center"> <IconButton icon="x" aria-label="Close" variant="filled" disabled /> <IconButton icon="x" aria-label="Close" variant="outlined" disabled /> <IconButton icon="x" aria-label="Close" variant="clear" disabled /> </div> );}Localized icon buttons
Section titled “Localized icon buttons”Pre-built icon buttons with translated accessible labels are available from @volvo-cars/react-icons/localized. These require @volvo-cars/react-locale-provider to be set up in your app.
import { BackIconButton, CloseIconButton, ForwardIconButton, NextIconButton, PlayIconButton, PlayPauseIconButton, PrevIconButton,} from '@volvo-cars/react-icons/localized';import { LocaleProvider } from '@volvo-cars/react-locale-provider';
export function IconButtonLocalized() { return ( <LocaleProvider locale="sv-SE"> <div className="flex flex-wrap gap-16 items-center"> <BackIconButton variant="outlined" /> <PrevIconButton variant="outlined" /> <NextIconButton variant="outlined" /> <ForwardIconButton variant="outlined" /> <CloseIconButton variant="outlined" /> <PlayIconButton variant="outlined" /> <PlayPauseIconButton variant="outlined" paused /> </div> </LocaleProvider> );}Interactive states
Section titled “Interactive states”ShowPasswordIconButton and PlayPauseIconButton are stateful — their icon and accessible label change based on the passwordVisible / paused prop. Click to toggle:
import { PlayPauseIconButton, ShowPasswordIconButton,} from '@volvo-cars/react-icons/localized';import { LocaleProvider } from '@volvo-cars/react-locale-provider';import { useState } from 'react';
export function IconButtonLocalizedInteractive() { const [passwordVisible, setPasswordVisible] = useState(false); const [paused, setPaused] = useState(true);
return ( <LocaleProvider locale="en"> <div className="flex gap-32 flex-wrap"> <div className="flex-col items-center gap-4"> <ShowPasswordIconButton passwordVisible={passwordVisible} onClick={() => setPasswordVisible(!passwordVisible)} /> <span className="micro"> {passwordVisible ? 'Password visible' : 'Password hidden'} </span> </div> <div className="flex-col items-center gap-4"> <PlayPauseIconButton paused={paused} onClick={() => setPaused(!paused)} /> <span className="micro">{paused ? 'Paused' : 'Playing'}</span> </div> </div> </LocaleProvider> );}Available localized buttons:
| Component | Icon | Purpose |
|---|---|---|
BackIconButton | arrow-back | Navigate back |
CloseIconButton | x | Close dialogs/overlays |
ForwardIconButton | arrow-forward | Navigate forward |
NextIconButton | chevron-forward | Next item/page |
PrevIconButton | chevron-back | Previous item/page |
PlayIconButton | play | Play media |
PlayPauseIconButton | play/pause | Toggle play/pause |
ShowPasswordIconButton | eye | Toggle password visibility |
These buttons automatically provide localized aria-label values based on the current locale from LocaleProvider.
Accessibility
Section titled “Accessibility”Key consumer responsibilities from the WCAG audit:
- Accessible name required — every icon button needs
aria-labeloraria-labelledby. The label should describe the action (“Close”, “Delete”), not the icon (“X”, “Trash”). - Sufficient contrast — icon and background must meet 4.5:1 contrast. Use filled buttons on media surfaces; never use clear or outlined on images or video.
- Bleed spacing — when using
bleed, ensure enough surrounding space for the full 40×40 px touch area. - Toggle buttons — for play/pause or similar toggles, update
aria-labelto describe the resulting action rather than the current state.
| Prop | Type | Required | Default |
|---|---|---|---|
icon | "code" | "link" | "map" | "menu" | "search" | "... | ✓ | - |
| The icon to render in the IconButton using the 1.0 icon system. | |||
iconFilled | boolean | - | - |
| Use the filled variant of the icon, typically for active or selected states. | |||
iconLoading | "eager" | "lazy" | - | lazy |
| The loading strategy for the icon image. | |||
color | "neutral" | "destructive" | "subtle" | - | neutral |
| The color of the IconButton | |||
size | "medium" | "large" | - | medium |
| The size of the IconButton. - `'medium'` — 40 px button, 16 px icon - `'large'` — 48 px button, 24 px icon | |||
disabled | boolean | - | - |
| Disables the button. Avoid using disabled buttons whenever possible - instead show error messages explaining the next steps the user should take. | |||
className | string | - | - |
| Custom class name, merged with existing classes. | |||
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
style | CSSProperties | - | - |
tabIndex | number | - | - |
variant | "outlined" | "filled" | "clear" | - | outlined |
| The visual style of the IconButton. | |||
bleed | boolean | - | false |
| Allows the background to bleed out into the surrounding layout, leaving the button to only take up the space for the icon itself. Only supported on the `clear` variant. | |||
aria-label | string | - | - |
| Defines a string value that labels the current element. @see aria-labelledby. | |||
aria-labelledby | string | - | - |
| Identifies the element (or elements) that labels the current element. @see aria-describedby. | |||
href | string | - | - |
onClick | MouseEventHandler<HTMLButtonElement> | - | - |
| Called when the button is clicked. If used within a `<form>`, prefer to set the button to `type=submit` and use the `onSubmit` event on the form instead. | |||
form | string | - | - |
| Id of a form element that this button should be associated with. Defaults to the containing form element. | |||
formAction | string | - | - |
| The URL that processes the information submitted by the button, overriding the `action` attribute of the button's form. | |||
formMethod | "post" | "get" | - | - |
| Specifies the HTTP method used to submit the form, overriding the `method` attribute of the button's form. | |||
name | string | - | - |
| The name of the button, submitted as a pair with the button's value as part of the form data. | |||
value | string | - | - |
| The value associated with the button's `name` in the form data when the form is submitted using this button. | |||
type | "button" | "submit" | - | button |
onBlur | FocusEventHandler<HTMLButtonElement> | - | - |
onFocus | FocusEventHandler<HTMLButtonElement> | - | - |
onKeyDown | KeyboardEventHandler<HTMLButtonElement> | - | - |
onKeyUp | KeyboardEventHandler<HTMLButtonElement> | - | - |
onPointerDown | PointerEventHandler<HTMLButtonElement> | - | - |
onPointerEnter | PointerEventHandler<HTMLButtonElement> | - | - |
onPointerLeave | PointerEventHandler<HTMLButtonElement> | - | - |
onPointerMove | PointerEventHandler<HTMLButtonElement> | - | - |
onPointerUp | PointerEventHandler<HTMLButtonElement> | - | - |
onAnimationEnd | AnimationEventHandler<HTMLButtonElement> | - | - |
onAnimationStart | AnimationEventHandler<HTMLButtonElement> | - | - |
onTransitionEnd | TransitionEventHandler<HTMLButtonElement> | - | - |
asChild | boolean | ✓ | - |