Button
Trigger actions with filled, outlined, and text button variants. Includes the SubmitButton React component for forms.
Apply button-filled, button-outlined, or button-text to a semantic <button> or <a> element.
Show code
<div class="flex flex-wrap gap-16"> <button class="button-filled">Filled</button> <button class="button-outlined">Outlined</button> <button class="button-text">Text</button></div>Variants
Section titled “Variants”Filled: button-filled
Section titled “Filled: button-filled”Primary actions. Use at most one per viewport.
Outlined: button-outlined
Section titled “Outlined: button-outlined”Secondary actions. Can appear multiple times on a page.
Text: button-text
Section titled “Text: button-text”Tertiary actions. Shows a forward arrow by default. Control direction with data-arrow:
Show code
<div class="flex-col items-start gap-8"> <button class="button-text">Default (forward)</button> <button class="button-text" data-arrow="back">Back</button> <button class="button-text" data-arrow="down">Down</button> <button class="button-text" data-arrow="up">Up</button> <button class="button-text" data-arrow="none">No arrow</button></div>Arrow direction automatically flips in RTL layouts.
Options
Section titled “Options”Color: data-color
Section titled “Color: data-color”Use data-color to change the button’s intent. Available values:
accent: extra prominence. Use only on plain backgrounds. Not supported on text buttons (maps back to default).destructive: delete, remove, or cancel actions.subtle: low-emphasis filled buttons with a gray surface and primary text. Filled only.
Size: data-size
Section titled “Size: data-size”Buttons default to 3rem (48px). Use data-size="medium" for 2.5rem (40px) or data-size="small" for 2rem (32px). Both are available on filled and outlined buttons. Small buttons maintain a 40px touch target via an invisible pseudo-element.
Text buttons do not support data-size: the attribute is ignored.
Use <Icon> from @volvo-cars/react-icons with color="currentColor" so the icon color follows the button. Wrap the label text in a <span> to preserve spacing. Set data-arrow="none" on text buttons to suppress the default arrow. Icons get automatic inline margin depending on whether they appear first or last.
Links as buttons
Section titled “Links as buttons”Apply the class directly to an <a> or your framework’s link component. See the link example in Examples.
Grouping buttons
Section titled “Grouping buttons”Use the button-group class to align buttons horizontally. The group automatically handles spacing and responsive behavior.
Show code
<div class="button-group"> <button type="button" class="button-filled">Primary</button> <button type="button" class="button-filled" data-color="subtle"> Secondary </button></div>The group provides:
16pxgap between buttons- Automatic wrapping when buttons don’t fit
- Full-width buttons on viewports
< 768pxor containers< 30rem
Alignment
Section titled “Alignment”Use justify-* utility classes to position buttons on larger viewports.
Button combinations
Section titled “Button combinations”Combine different variants for visual hierarchy.
States
Section titled “States”Loading
Section titled “Loading”Filled and outlined buttons support a loading indicator. Hide the label with invisible and add a <progress class="spinner">. The spinner is absolutely positioned and centered. Keeping the label in the DOM preserves the button’s width.
Disabled
Section titled “Disabled”Prefer aria-disabled="true" over the disabled attribute. It keeps the button focusable for screen reader users and keyboard navigation. Disabled buttons get reduced opacity automatically.
Avoid disabled buttons when possible. Show an explanatory message instead. See Usability Pitfalls of Disabled Buttons.
Accessibility
Section titled “Accessibility”Key consumer responsibilities from the WCAG audit:
- Accessible name: every button needs visible text or
aria-label. Consistent labeling across the interface is your responsibility. aria-disabledoverdisabled: ensures the button remains in the tab order and is announced by screen readers.- Destructive actions: provide a confirmation step (dialog, etc.) before finalizing. The
data-color="destructive"styling alone is not sufficient. - Loading status: if you add a loading spinner, ensure the state change is announced to assistive technology (e.g., with
aria-label="Loading"on the<progress>). - Links styled as buttons:
<a>with a button class and a validhrefis fine. Do not use<a>withouthrefas a button; use<button>instead. - Color modes: use light-mode buttons on light backgrounds and dark-mode buttons on dark backgrounds. Never use accent or destructive colors on colored backgrounds or media.
SubmitButton (React)
Section titled “SubmitButton (React)”SubmitButton from @volvo-cars/react-forms is a form-aware button with type="submit" by default. It supports the same filled and outlined variants as the CSS button classes, plus built-in loading state management.
Show code
import { SubmitButton } from '@volvo-cars/react-forms';
export function SubmitButtonBasic() { return <SubmitButton type="button">Submit</SubmitButton>;}Loading state
Section titled “Loading state”Set loading to show a spinner and prevent resubmission. Always provide loadingLabel for screen readers.
Prefer handling the form onSubmit event rather than onClick on the submit button.
SubmitButton Props
Section titled “SubmitButton Props”| Prop | Type | Required | Default |
|---|---|---|---|
hidden | boolean | - | - |
id | string | - | - |
title | string | - | - |
dir | string | - | - |
lang | string | - | - |
slot | string | - | - |
translate | "yes" | "no" | - | - |
className | string | - | - |
style | CSSProperties | - | - |
tabIndex | number | - | - |
onPointerDown | (PointerEventHandler<Element> & PointerEventHan... | - | - |
onPointerEnter | (PointerEventHandler<Element> & PointerEventHan... | - | - |
onPointerLeave | (PointerEventHandler<Element> & PointerEventHan... | - | - |
onPointerMove | (PointerEventHandler<Element> & PointerEventHan... | - | - |
onPointerUp | (PointerEventHandler<Element> & PointerEventHan... | - | - |
variant | "filled" | "outlined" | - | filled |
| Which design variant to render. | |||
color | "accent" | "destructive" | "neutral" | "subtle" | - | neutral |
| The `accent` color can be used to add extra prominence to the button. Use the `destructive` color for actions that require caution. | |||
size | "medium" | "small" | - | medium |
| Use the small button in cards and other constrained containers. | |||
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. | |||
children | ReactNode | ✓ | - |
| The button label. | |||
disabled | boolean | - | - |
| Disables the button. Avoid using disabled buttons whenever possible - instead show error messages explaining the next steps the user should take. | |||
loading | boolean | - | - |
| Renders a spinner in place of the button label and prevents activating the button again. | |||
loadingLabel | string | - | - |
| Label for screen readers while showing the loading spinner. | |||
type | "submit" | "button" | - | submit |
onClick | MouseEventHandler<HTMLButtonElement> | - | - |
| Called when the button is clicked. Prefer to place the button within a `<form> and use the `onSubmit` event on the form instead. | |||
onFocus | FocusEventHandler<HTMLButtonElement> | - | - |
onBlur | FocusEventHandler<HTMLButtonElement> | - | - |
onKeyDown | KeyboardEventHandler<HTMLButtonElement> | - | - |
onKeyUp | KeyboardEventHandler<HTMLButtonElement> | - | - |