Button

Action trigger for primary flows across Pixexid. Buttons are high-emphasis controls that combine semantic Tailwind tokens with Radix Slot support for flexible composition.

Overview

When to use

  • Primary call-to-action in hero sections, dialogs, and cards.
  • Secondary, destructive, and ghost styles for contextual actions.
  • Use the Slot-powered `asChild` prop to style Next.js `Link` or custom components.

Avoid when

  • Use `Link` without `asChild` for in-flow navigation that isn’t an action.
  • Toggle states are better handled with `Toggle` or `Switch` primitives.
  • Inline text actions inside copy should use the `link` variant sparingly.

Variants

Semantic variants

Each variant maps to a semantic color token so buttons stay accessible in light and dark modes.

import { Button } from "@/components/ui/button"

export function VariantExamples() {
  return (
    <div className="flex flex-wrap gap-3">
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="subtle">Subtle</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="destructive">Danger</Button>
      <Button variant="link">Link style</Button>
    </div>
  )
}

Size presets

Sizes align with the 4px spacing scale. `icon` keeps square proportions for icon-only buttons.

import { Button } from "@/components/ui/button"

export function SizeExamples() {
  return (
    <div className="flex items-center gap-3">
      <Button size="xs">XS</Button>
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
      <Button size="xl">XL</Button>
      <Button size="icon" aria-label="Settings">
        <IconSettings className="h-4 w-4" />
      </Button>
    </div>
  )
}

Slot rendering

The `asChild` prop lets you render the button as any element while keeping focus styles and animations.

import Link from "next/link"
import { Button } from "@/components/ui/button"

export function ButtonLink() {
  return (
    <Button asChild>
      <Link href="/projects/new">Create project</Link>
    </Button>
  )
}

Props

PropTypeDefaultDescription
variant'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link''default'Visual style that maps to Pixexid semantic tokens.
size'sm' | 'default' | 'lg' | 'icon''default'Controls height and horizontal padding.
asChildbooleanfalseWhen true, renders the Slot component so you can pass any element (e.g., Link) while preserving button styles.
...restReact.ButtonHTMLAttributes<HTMLButtonElement>All native button props like type, disabled, aria-* are forwarded to the underlying element.

Usage examples

Primary form submit

<form className="space-y-4">
  <input className="w-full rounded-md border border-input bg-background px-3 py-2" placeholder="Project name" />
  <Button type="submit">Create project</Button>
</form>

Button group with icon action

<div className="flex gap-2">
  <Button variant="secondary">Save draft</Button>
  <Button>Publish</Button>
  <Button size="icon" variant="outline" aria-label="Settings">
    <IconSettings className="h-4 w-4" />
  </Button>
</div>

Accessibility

Keyboard

  • Tab moves focus between buttons in DOM order.
  • Enter or Space triggers `onClick`.
  • Provide `aria-label` or visible text for icon-only buttons.

Best practices

  • Use one `variant="default"` per view to keep focus on the primary action.
  • Ensure destructive actions require a confirmation dialog.
  • Combine `disabled` state with explanatory messaging when an action is unavailable.