Button

Displays a button, or a component that looks and behaves like one.

Usage

import { Button } from '@epilot/spark-ui/button'
<Button>Save changes</Button>

A bare <Button> follows the active theme: its look comes from the Theme's styleVariant, accentColor, and highContrast knobs. Set styleVariant and color to pin a look on a single instance, independent of the ambient theme.

Guidelines

styleVariant sets visual weight, heaviest to lightest: solid > surface / soft > outline > ghost. A bare Button inherits its weight from the theme; set styleVariant to override it, and avoid two solid buttons side by side.

color picks the scale that paints the button: accent, neutral, or danger. Use color="danger" for an error or destructive action; it composes with any style: a solid Delete, or a ghost Remove.

A <Theme> can redraw buttons by their styleVariant and color without touching the code; see Button styles and colours.

Examples

Theme-driven default

A bare <Button> takes its look from the ambient theme's styleVariant knob, so the same markup restyles automatically inside a themed region.

Styles & colors

styleVariant and color set together pin a button's look regardless of the ambient theme.

primary
neutral
destructive
solid
soft
surface
outline
ghost

Danger

color="danger" composes with any style.

High contrast

High contrast is a theme-level knob (<Theme highContrast> / data-high-contrast), not a Button prop. It strengthens any button toward higher-contrast steps automatically. Solid fills deepen; non-solid tiers darken their ink and border.

Sizes

With icons

Icons default to 16px (size-4) inside a button; give the icon its own size-* class to override.

Icon-only

Use the square icon-small / icon / icon-medium sizes for icon-only buttons, and always pass an aria-label.

Over media

styleVariant="media" is a translucent dark fill with white ink and a backdrop blur, for actions over images. It ignores color.

Loading

A Spinner inside a Button takes the button's text colour.

API Reference

PropTypeDefaultDescription
styleVariant'solid' | 'soft' | 'surface' | 'outline' | 'ghost' | 'media'—Visual weight. Pins the button's style, overriding the theme's styleVariant. media ignores color.
color'accent' | 'neutral' | 'danger'—Which scale paints the button. Setting color alone pins styleVariant to solid.
fixedLookbooleanfalseSkips the enclosing <Theme> Remaps of styleVariant and color; their Restyles still apply.
size'small' | 'default' | 'medium' | 'icon-small' | 'icon' | 'icon-medium''default'Button size, including the square icon-only sizes.
renderReactElement—Replaces the rendered element (e.g. a router <Link/>) instead of the native <button>.
nativeButtonbooleantrueSet to false alongside render when the target element isn't a native <button>.

Accessibility

  • Reconsider disabling at all. A disabled button hides why an action is unavailable, drops out of the tab order, and gives no feedback when a user clicks it anyway; see Disabled buttons suck. Prefer keeping the button enabled and surfacing the reason on interaction (a validation message, inline error, tooltip) instead of disabling it preemptively. The same reasoning applies to any control with a disabled state, not just Button.
  • If you do disable, prefer aria-disabled over disabled. It keeps the button focusable and announced instead of removing it from the tab order, and gets the same dimmed look (opacity-50) and blocked pointer events as disabled. But it doesn't block keyboard activation on its own, so guard the handler yourself (e.g. onClick={isValid ? handleSubmit : undefined}).
  • Icon-only buttons need a label. With no text content, pass aria-label to give screen readers something to announce.
  • type defaults to "button", so a button inside a form won't submit it unless you set type="submit".
  • Disabled + render: non-button elements can't be disabled, so Base UI sets aria-disabled and blocks interaction on non-native elements instead. The element stays in the tab order. Non-button render targets also get role="button".
  • The focus ring meets WCAG 1.4.11 (3:1 against adjacent colors) on every accent.

On this page