Button

Button is a foundational action component for UI flows.

It supports default, secondary, and outline variants and reads visual values from --ui-* CSS tokens so consuming apps can override brand styling without modifying component code.

When to use

  • Triggering primary and secondary actions.
  • Linking to next-step interactions in docs and dashboards.
  • Providing a theme-consistent action component across multiple apps.

Behavior notes

  • Supports polymorphic rendering with asChild.
  • Uses semantic theme variables for color and border.
  • Designed for external consumers to override tokens safely.

Example

<Button variant='outline'>Learn more</Button>

Preview

Usage

import { Button } from '@branditdev/ui';

export function ButtonDemo() {
  return (
    <div className='flex flex-wrap items-center gap-3'>
      <Button>Default</Button>
      <Button variant='destructive'>Destructive</Button>
      <Button variant='success'>Success</Button>
      <Button variant='warning'>Warning</Button>
      <Button variant='outline'>Outline</Button>
      <Button variant='secondary'>Secondary</Button>
      <Button variant='ghost'>Ghost</Button>
      <Button variant='link'>Link</Button>
    </div>
  );
}

API

const Button: ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & RefAttributes<HTMLButtonElement>>

Defined in: components/button/button.tsx:64

Reusable button component for BranditDev products.

Uses semantic theme tokens so consumers can override visual design by setting design tokens such as --primary, --background, and --radius-md.

Uses forwardRef for React 18 compatibility (e.g. Base UI Popover render prop). Remove forwardRef when upgrading to React 19.

Param

Button properties including size, variant, and polymorphic rendering.

Returns

A styled button element.

Source

import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';

import { cn } from '@branditdev/ui';

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
  {
    variants: {
      variant: {
        unstyled:
          'bg-transparent text-foreground hover:bg-transparent hover:text-foreground',
        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
        destructive:
          'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
        success: 'bg-success text-success-foreground hover:opacity-90',
        warning: 'bg-warning text-warning-foreground hover:opacity-90',
        outline:
          'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
        secondary:
          'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        ghost:
          'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
        link: 'text-primary underline-offset-4 hover:underline cursor-pointer',
      },
      size: {
        default: 'h-9 px-4 py-2 has-[>svg]:px-3',
        xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
        sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
        lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
        icon: 'size-9',
        'icon-xs': "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
        'icon-sm': 'size-8',
        'icon-lg': 'size-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
);

export type ButtonProps = React.ComponentProps<'button'> &
  VariantProps<typeof buttonVariants>;

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
  { className, variant, size, ...props },
  ref
) {
  return (
    <button
      ref={ref}
      data-slot='button'
      data-variant={variant}
      data-size={size}
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  );
});

export { Button, buttonVariants };