Button
The default action affordance, a themed wrapper over the native button with CVA variants and asChild slot projection.
Install
Pull Button from the barrel for everyday use, or the granular path when you want a tighter bundle.
import { Button } from "@brika/clay";import { Button } from "@brika/clay/components/button";A minimal example
Drop this into a page. Native HTML attributes pass through to the underlying primitive.
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>;
}Button, every way
As Link
`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.
Accessibility
- Focus ring uses
--ringtoken for WCAG contrast. -
disabledremoves pointer events and reduces opacity; it does not setaria-disabled. - Icon-only buttons with
size="icon"REQUIRE anaria-label, there is no text fallback. -
asChildpasses all button props, includingroleandaria-*, to the child element.
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--button-filled-containercolorvar(--primary)components.button.filledContainerBackground of the filled button variant.
--button-filled-labelcolorvar(--primary-foreground)components.button.filledLabelLabel color of the filled button variant.
--button-outline-bordercolorvar(--border)components.button.outlineBorderBorder color of the outline button variant.
--button-outline-labelcolorvar(--foreground)components.button.outlineLabelLabel color of the outline button variant.
--button-destructive-containercolorvar(--destructive)components.button.destructiveContainerBackground of the destructive button variant.
--button-destructive-labelcoloroklch(1 0 0)components.button.destructiveLabelLabel color of the destructive button variant. Defaults to white so it stays legible regardless of `--destructive` hue.
--button-secondary-containercolorvar(--secondary)components.button.secondaryContainerBackground of the secondary button variant.
--button-secondary-labelcolorvar(--secondary-foreground)components.button.secondaryLabelLabel color of the secondary button variant.
--button-ghost-hover-containercolorvar(--accent)components.button.ghostHoverContainerHover 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.ghostHoverLabelHover label color of the ghost button variant.
--button-link-colorcolorvar(--primary)components.button.linkColorForeground color of the link button variant. Underline is rendered in this same color.
--button-invalid-bordercolorvar(--destructive)components.button.invalidBorderBorder 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.destructiveFocusFocus-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.outlineContainerResting 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.outlineDarkContainerDark-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.
--button-radiusradiusvar(--radius-control)components.button.radiusButton corner radius. Falls back to `radius-control`.
--button-heightsize2.25remcomponents.button.heightDefault button height.
--button-padding-xsizecalc(var(--spacing) * 4)components.button.paddingXInline padding inside the button.
--button-padding-ysizecalc(var(--spacing) * 2)components.button.paddingYBlock padding inside the button.
--button-gapsizecalc(var(--spacing) * 2)components.button.gapGap between adjacent children inside the button.
--button-border-widthborder-width0pxcomponents.button.borderWidthBorder width on the button. Set non-zero for outline-style variants.
--button-border-styleborder-stylesolidcomponents.button.borderStyleBorder style on the button (`solid`, `dashed`, `double`, `none`).
--button-font-familyfont-familyvar(--font-sans)components.button.fontFamilyTypeface for button.
--button-font-sizefont-sizevar(--text-body-md)components.button.fontSizeFont size for button.
--button-font-weightfont-weight500components.button.fontWeightFont weight for button.
--button-line-heightline-height1.25components.button.lineHeightLine height for button.
--button-letter-spacingletter-spacing0components.button.letterSpacingLetter spacing for button. Useful for caps labels.
--button-text-transformtext-transformnonecomponents.button.textTransformText transform for button (`uppercase`, `lowercase`, `capitalize`, `none`).
--button-shadowshadowvar(--shadow-surface)components.button.shadowResting elevation under a button.
--button-durationdurationvar(--motion-standard-duration)components.button.durationTransition duration for button state changes.
--button-easingeasingvar(--motion-standard-easing)components.button.easingTransition 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.
{
"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.
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
falsebooleanWhen 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.