Button component for interface actions. Renders as a regular link when href is provided. Buttons should primarily be used to trigger events or actions.

Open in new tab
import { Button, Stack } from "@kesko/components";
import { IconBasketAddTo, IconExternalLink } from "@kesko/iconography/react";

export default () => (
  <Stack direction="row" gap="2xs" align="center" wrap>
    <Button variant="primary">
      <IconBasketAddTo /> Primary
    </Button>
    <Button variant="primary" iconOnly>
      <IconBasketAddTo label="Add to basket" />
    </Button>
    <Button>
      Default <IconExternalLink />
    </Button>
    <Button disabled>Disabled</Button>
    <Button variant="plain">Plain</Button>
    <Button variant="text">Text</Button>
  </Stack>
);
import type { CSSProperties } from "react";
import { Button, Stack } from "@kesko/components";

export default () => (
  <Stack direction="row" gap="2xs" align="center">
    <Button
      variant="primary"
      style={
        {
          "--k-button-bg": "var(--k-color-status-error)",
          "--k-button-text": "var(--k-color-text-on-error)",
          "--k-button-border": "var(--k-color-status-error)",
        } as CSSProperties
      }
    >
      Delete account
    </Button>
    <Button
      variant="primary"
      style={
        {
          "--k-button-bg": "var(--k-color-status-success)",
          "--k-button-text": "var(--k-color-text-on-success)",
          "--k-button-border": "var(--k-color-status-success)",
        } as CSSProperties
      }
    >
      Confirm order
    </Button>
  </Stack>
);
import { Button } from "@kesko/components";

export default () => (
  <Button variant="primary" onClick={() => alert("Clicked!")}>
    Click me
  </Button>
);
import { Button, Stack } from "@kesko/components";
import { IconArrowUp } from "@kesko/iconography/react";

export default () => (
  <Stack direction="row" gap="2xs" align="center">
    <Button variant="primary" expand>
      Full width
    </Button>
    <Button variant="primary" expand>
      Back to top <IconArrowUp />
    </Button>
  </Stack>
);
import { Fragment } from "react";
import { Button, Stack } from "@kesko/components";
import { IconBasketAddTo } from "@kesko/iconography/react";

const sizes = [
  { size: "sm" as const, label: "Small" },
  { size: "md" as const, label: "Medium" },
  { size: "lg" as const, label: "Large" },
];

export default () => (
  <Stack
    direction="row"
    gap="2xs"
    align="center"
    wrap
    style={{
      padding: "0.5rem",
      background: "#000",
      color: "var(--k-color-text-on-accent)",
      borderRadius: "0.5rem",
    }}
  >
    {sizes.map(s => (
      <Fragment key={s.size}>
        <Button variant="inverted" size={s.size}>
          <IconBasketAddTo /> {s.label}
        </Button>
        <Button iconOnly variant="inverted" size={s.size}>
          <IconBasketAddTo label={`Inverted ${s.label}`} />
        </Button>
      </Fragment>
    ))}
  </Stack>
);
import { Button, Stack } from "@kesko/components";

export default () => (
  <Stack direction="row" gap="2xs" align="center" wrap>
    <Button variant="primary" loading>
      Loading
    </Button>
    <Button loading>Loading</Button>
    <Button loading variant="plain">
      Loading
    </Button>
  </Stack>
);
import { Fragment } from "react";
import { Button, Stack } from "@kesko/components";
import { IconBasketAddTo } from "@kesko/iconography/react";

const variants = [
  { key: "primary", label: "Primary", props: { variant: "primary" as const } },
  { key: "secondary", label: "Secondary", props: { variant: "secondary" as const } },
  { key: "default", label: "Default", props: {} },
  { key: "plain", label: "Plain", props: { variant: "plain" as const } },
  { key: "text", label: "Text", props: { variant: "text" as const } },
];

const sizes = [
  { size: "sm" as const, label: "Small" },
  { size: "md" as const, label: "Medium" },
  { size: "lg" as const, label: "Large" },
];

export default () => (
  <Stack gap="2xs">
    {variants.map(row => (
      <Stack key={row.key} direction="row" gap="2xs" align="center" wrap>
        <strong style={{ fontSize: "0.875rem", inlineSize: "6rem" }}>{row.label}</strong>
        {sizes.map(s => (
          <Fragment key={s.size}>
            <Button {...row.props} size={s.size}>
              <IconBasketAddTo /> {s.label}
            </Button>
            <Button iconOnly {...row.props} size={s.size}>
              <IconBasketAddTo label={`${row.label} ${s.label}`} />
            </Button>
          </Fragment>
        ))}
      </Stack>
    ))}
  </Stack>
);
import { Button, Stack } from "@kesko/components";

const variants = [
  { key: "primary", label: "Primary", props: { variant: "primary" as const } },
  { key: "secondary", label: "Secondary", props: { variant: "secondary" as const } },
  { key: "default", label: "Default", props: {} },
  { key: "plain", label: "Plain", props: { variant: "plain" as const } },
  { key: "text", label: "Text", props: { variant: "text" as const } },
];

const sizes = ["sm", "md", "lg"] as const;

export default () => (
  <Stack gap="2xs">
    {variants.map(row => (
      <Stack key={row.key} direction="row" gap="2xs" align="center" wrap>
        <strong style={{ fontSize: "0.875rem", inlineSize: "6rem" }}>{row.label}</strong>
        {sizes.map(size => (
          <Button key={size} {...row.props} size={size} loading>
            Loading
          </Button>
        ))}
      </Stack>
    ))}
  </Stack>
);
import { Fragment } from "react";
import { Button, Stack } from "@kesko/components";
import { IconBasketAddTo } from "@kesko/iconography/react";

const sizes = [
  { size: "sm" as const, label: "Small" },
  { size: "md" as const, label: "Medium" },
  { size: "lg" as const, label: "Large" },
];

export default () => (
  <Stack
    direction="row"
    gap="2xs"
    align="center"
    wrap
    style={{ padding: "0.5rem", background: "var(--k-color-bg-hover)", borderRadius: "0.5rem" }}
  >
    {sizes.map(s => (
      <Fragment key={s.size}>
        <Button transparent size={s.size}>
          <IconBasketAddTo /> {s.label}
        </Button>
        <Button iconOnly transparent size={s.size}>
          <IconBasketAddTo label={`Transparent ${s.label}`} />
        </Button>
      </Fragment>
    ))}
  </Stack>
);
import { Button, Stack } from "@kesko/components";
import { IconBasketAddTo } from "@kesko/iconography/react";

export default () => (
  <Stack direction="row" gap="2xs" align="center" wrap>
    <Button variant="primary">
      <IconBasketAddTo /> Primary
    </Button>
    <Button variant="primary" iconOnly>
      <IconBasketAddTo label="Add to basket" />
    </Button>
    <Button variant="secondary">
      <IconBasketAddTo /> Secondary
    </Button>
    <Button>Default</Button>
    <Button disabled>Disabled</Button>
    <Button variant="plain">Plain</Button>
    <Button variant="text">Text</Button>
  </Stack>
);

Props

NameDescriptionTypeDefault
variant

The style variant of the button.

"default" | "primary" | "secondary" | "plain" | "text" | "inverted""default"
size

The size of the button.

"sm" | "md" | "lg""md"
expand

Makes the button fit its container.

booleanfalse
transparent

Removes the background from the default and plain variants.

booleanfalse
disabled

Disables the component. Native disabled on buttons, aria-disabled on links.

booleanfalse
loading

Shows loading state with spinner.

booleanfalse
iconOnly

Renders the button as a square with no horizontal padding for a single icon child. Pair with aria-label for accessibility.

booleanfalse
aria-label

Accessible name. Required for iconOnly buttons that have no visible text.

string
aria-labelledby

id of an element that labels this button. Use instead of aria-label when the label is already visible elsewhere on the page.

string
aria-describedby

id of an element that describes this button (typically helper text).

string
aria-controls

id of the element(s) controlled by this button.

string
aria-expanded

Whether a grouping controlled by this button is expanded.

boolean | "true" | "false"
aria-haspopup

Availability and type of popup this button opens.

boolean | "menu" | "listbox" | "tree" | "grid" | "dialog" | "true" | "false"
href

Renders as an anchor element.

string
target

Where to open the linked URL.

"_self" | "_blank" | "_parent" | "_top"
download

Triggers a file download. Pass true to use the server filename or a string to override it.

boolean | string

Events

NameDescriptionType
onClick

Handler called on click (mouse, touch, or keyboard activation).

(event: MouseEvent<ButtonTarget>) => void
onFocus

Handler called when the element receives focus.

(event: FocusEvent<ButtonTarget>) => void
onBlur

Handler called when the element loses focus.

(event: FocusEvent<ButtonTarget>) => void

CSS custom properties

CSS custom properties provide finer-grained control than props. Prefer the props API when it suits your need.

NameDescriptionDefault
--k-button-bg

Overrides the background color.

var(--k-color-bg)
--k-button-text

Overrides the text color.

var(--k-color-text-link)
--k-button-border

Overrides the border color.

var(--k-color-text-link)
--k-button-radius

Overrides the border radius.

var(--k-radius-button)
--k-button-focus

Overrides the focus ring color.

var(--k-color-border-focus)
--k-button-bg-hover

Overrides the hover background color.

var(--k-button-bg)
--k-button-bg-active

Overrides the pressed background color.

var(--k-button-bg)
--k-button-text-hover

Overrides the hover text color.

--k-button-text-active

Overrides the pressed text color.

--k-button-ring-hover

Overrides the grow-ring color on hover.

--k-button-ring-active

Overrides the grow-ring color when pressed.

--k-button-height

Overrides the button height at the active size.