Skip to content

Button

Edit on GitHub

Trigger actions with filled, outlined, and text button variants. Includes the SubmitButton React component for forms.

@volvo-cars/css v2.5.1@volvo-cars/react-forms v2.0.1

Apply button-filled, button-outlined, or button-text to a semantic <button> or <a> element.

Show codeHide 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>

Primary actions. Use at most one per viewport.

Secondary actions. Can appear multiple times on a page.

Tertiary actions. Shows a forward arrow by default. Control direction with data-arrow:

Show codeHide 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.

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.

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.

Apply the class directly to an <a> or your framework’s link component. See the link example in Examples.

Use the button-group class to align buttons horizontally. The group automatically handles spacing and responsive behavior.

Show codeHide 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:

  • 16px gap between buttons
  • Automatic wrapping when buttons don’t fit
  • Full-width buttons on viewports < 768px or containers < 30rem

Use justify-* utility classes to position buttons on larger viewports.

Combine different variants for visual hierarchy.

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.

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.

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-disabled over disabled: 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 valid href is fine. Do not use <a> without href as 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 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 codeHide code
import { SubmitButton } from '@volvo-cars/react-forms';
export function SubmitButtonBasic() {
return <SubmitButton type="button">Submit</SubmitButton>;
}

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.

PropTypeRequiredDefault
hiddenboolean--
idstring--
titlestring--
dirstring--
langstring--
slotstring--
translate"yes" | "no"--
classNamestring--
styleCSSProperties--
tabIndexnumber--
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.
formstring--
Id of a form element that this button should be associated with. Defaults to the containing form element.
formActionstring--
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.
namestring--
The name of the button, submitted as a pair with the button's value as part of the form data.
valuestring--
The value associated with the button's `name` in the form data when the form is submitted using this button.
childrenReactNode-
The button label.
disabledboolean--
Disables the button. Avoid using disabled buttons whenever possible - instead show error messages explaining the next steps the user should take.
loadingboolean--
Renders a spinner in place of the button label and prevents activating the button again.
loadingLabelstring--
Label for screen readers while showing the loading spinner.
type"submit" | "button"-submit
onClickMouseEventHandler<HTMLButtonElement>--
Called when the button is clicked. Prefer to place the button within a `<form> and use the `onSubmit` event on the form instead.
onFocusFocusEventHandler<HTMLButtonElement>--
onBlurFocusEventHandler<HTMLButtonElement>--
onKeyDownKeyboardEventHandler<HTMLButtonElement>--
onKeyUpKeyboardEventHandler<HTMLButtonElement>--