Primitives

Button

The default action affordance, a themed wrapper over the native button with CVA variants and asChild slot projection.

Preview
01 Installation

Install

Pull Button from the barrel for everyday use, or the granular path when you want a tighter bundle.

tsxBarrel1 line
import { Button } from "@brika/clay";
tsxGranular1 line
import { Button } from "@brika/clay/components/button";
02 Usage

A minimal example

Drop this into a page. Native HTML attributes pass through to the underlying primitive.

tsxButton.tsx6 lines
import { Button } from '@brika/clay/components/button';

/** Default solid button, use for the primary call-to-action. */
export default function ButtonDefaultDemo() {
  return <Button>Save changes</Button>;
}
03 Examples

Button, every way

`asChild` delegates button styles to the child element, here an anchor tag.

Icon

Icon-only button, `aria-label` is required for screen readers.

Loading

Disabled with a spinner, use while an async operation is in flight.

Sizes

Four text sizes, plus matching icon-only variants (`icon-xs` through `icon-lg`).

Variants

Six emphasis tiers in one row, ordered from most to least prominent.

04 Accessibility

Accessibility

  • Focus ring uses --ring token for WCAG contrast.
  • disabled removes pointer events and reduces opacity; it does not set aria-disabled.
  • Icon-only buttons with size="icon" REQUIRE an aria-label, there is no text fallback.
  • asChild passes all button props, including role and aria-*, to the child element.
05 Tokens 31 theme-overridable

Theme tokens

Every CSS variable Button reads, with its default and the dotted path you'd write in a ThemeConfig JSON to override it. Set any of these in your theme to retune Button without touching component code.

Button tokens

31 tokens
ColorFill, border, and text colors.15
  • --button-filled-containercolorvar(--primary)
    components.button.filledContainer

    Background of the filled button variant.

  • --button-filled-labelcolorvar(--primary-foreground)
    components.button.filledLabel

    Label color of the filled button variant.

  • --button-outline-bordercolorvar(--border)
    components.button.outlineBorder

    Border color of the outline button variant.

  • --button-outline-labelcolorvar(--foreground)
    components.button.outlineLabel

    Label color of the outline button variant.

  • --button-destructive-containercolorvar(--destructive)
    components.button.destructiveContainer

    Background of the destructive button variant.

  • --button-destructive-labelcoloroklch(1 0 0)
    components.button.destructiveLabel

    Label color of the destructive button variant. Defaults to white so it stays legible regardless of `--destructive` hue.

  • --button-secondary-containercolorvar(--secondary)
    components.button.secondaryContainer

    Background of the secondary button variant.

  • --button-secondary-labelcolorvar(--secondary-foreground)
    components.button.secondaryLabel

    Label color of the secondary button variant.

  • --button-ghost-hover-containercolorvar(--accent)
    components.button.ghostHoverContainer

    Hover background of the ghost button variant. Resting state has no fill, the hover affordance is the only paint.

  • --button-ghost-hover-labelcolorvar(--accent-foreground)
    components.button.ghostHoverLabel

    Hover label color of the ghost button variant.

  • --button-link-colorcolorvar(--primary)
    components.button.linkColor

    Foreground color of the link button variant. Underline is rendered in this same color.

  • --button-invalid-bordercolorvar(--destructive)
    components.button.invalidBorder

    Border color applied when the button carries `aria-invalid`. Defaults to the destructive role so themes can retune the validation tint without affecting other destructive surfaces.

  • --button-destructive-focuscolorvar(--destructive)
    components.button.destructiveFocus

    Focus-ring color of the destructive button variant. Applied via the `focus-visible:ring-button-destructive-focus/20` (and `dark:.../40`) opacity-modified utility, so themes can shift the destructive halo independently of the fill.

  • --button-outline-containercolorvar(--background)
    components.button.outlineContainer

    Resting background of the outline button variant in light mode. Defaults to the surface background; dark mode uses `outline-dark-container` instead.

  • --button-outline-dark-containercolorvar(--input)
    components.button.outlineDarkContainer

    Dark-mode resting background of the outline button variant, applied at 30% opacity (50% on hover). Defaults to the `--input` role so the outline button picks up the same tinted dark surface as inputs.

GeometrySizes, lengths, and corner radii.5
  • --button-radiusradiusvar(--radius-control)
    components.button.radius

    Button corner radius. Falls back to `radius-control`.

  • --button-heightsize2.25rem
    components.button.height

    Default button height.

  • --button-padding-xsizecalc(var(--spacing) * 4)
    components.button.paddingX

    Inline padding inside the button.

  • --button-padding-ysizecalc(var(--spacing) * 2)
    components.button.paddingY

    Block padding inside the button.

  • --button-gapsizecalc(var(--spacing) * 2)
    components.button.gap

    Gap between adjacent children inside the button.

BorderBorder width and style.2
  • --button-border-widthborder-width0px
    components.button.borderWidth

    Border width on the button. Set non-zero for outline-style variants.

  • --button-border-styleborder-stylesolid
    components.button.borderStyle

    Border style on the button (`solid`, `dashed`, `double`, `none`).

TypographyTypeface, size, weight, spacing.6
  • --button-font-familyfont-familyvar(--font-sans)
    components.button.fontFamily

    Typeface for button.

  • --button-font-sizefont-sizevar(--text-body-md)
    components.button.fontSize

    Font size for button.

  • --button-font-weightfont-weight500
    components.button.fontWeight

    Font weight for button.

  • --button-line-heightline-height1.25
    components.button.lineHeight

    Line height for button.

  • --button-letter-spacingletter-spacing0
    components.button.letterSpacing

    Letter spacing for button. Useful for caps labels.

  • --button-text-transformtext-transformnone
    components.button.textTransform

    Text transform for button (`uppercase`, `lowercase`, `capitalize`, `none`).

ElevationDrop shadow and depth.1
  • --button-shadowshadowvar(--shadow-surface)
    components.button.shadow

    Resting elevation under a button.

MotionAnimation duration and easing.2
  • --button-durationdurationvar(--motion-standard-duration)
    components.button.duration

    Transition duration for button state changes.

  • --button-easingeasingvar(--motion-standard-easing)
    components.button.easing

    Transition easing for button state changes.

Override in a theme

Authoring a theme is plain JSON. Drop overrides under components.button, the names are camelCase versions of the variable suffix.

jsonmy-theme.json12 lines
{
  "id": "my-theme",
  "name": "My Theme",
  "description": "...",
  "accentSwatches": ["#000"],

  "components": {
    "button": {
      "paddingX": "calc(var(--spacing) * 4)"
    }
  }
}

Then apply with applyTheme(myTheme) (see the theming guide), scope it to a subtree with themeToCssVars(myTheme, mode), or consume any token directly in your own JSX with the matching Tailwind utility (e.g. bg-button-…) — see the chips on each token row.

06 API reference

API reference

Props specific to Button. Native HTML attributes pass through to the underlying primitive, see the component source on GitHub for the full type signature.

  • asChilddefault false
    boolean
    When true, render the child element instead of the default DOM node and merge props. Lets you compose with <a>, <Link>, or another primitive while keeping behavior and styling.
  • sizedefault 'default'
    'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-xs' | 'icon-sm' | 'icon-lg'
    Text or icon-only sizing preset.
  • variantdefault 'default'
    'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
    Emphasis tier for the button.