Button
A versatile button component with multiple variants and sizes. Perfect for actions, forms, and navigation.
Installation
Terminal
TSXReactTailwind
npx @uiblox/cli add buttonVisual Variations
Toggle between three different visual styles: solid (filled), soft (tinted background), and outline (bordered).
Main call-to-action
Secondary actions
Non-interactive state
Basic Usage
Copy & paste ready
import { Button } from "@/components/ui";
<Button>Click me</Button>Variants
Choose from 8 different button variants to match your design needs.
All Variants
Copy & paste ready
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="success">Success</Button>
<Button variant="warning">Warning</Button>Sizes
Size Options
Copy & paste ready
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon">
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
</Button>Disabled State
Copy & paste ready
<Button disabled>Disabled</Button>
<Button variant="outline" disabled>Disabled Outline</Button>With Icons
Copy & paste ready
<Button>
<svg className="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
</svg>
Upload
</Button>
<Button variant="outline">
Settings
<svg className="ml-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</Button>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "success" | "warning" | "default" | The visual style of the button |
| size | "default" | "sm" | "lg" | "icon" | "default" | The size of the button |
| disabled | boolean | false | Whether the button is disabled |
| className | string | - | Additional CSS classes to apply |
Source Code
Copy this code into src/components/ui/button.tsx:
button.tsx
TSXReactTailwind
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500",
destructive: "bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500",
outline: "border border-gray-300 dark:border-gray-700 bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800",
secondary: "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-700",
ghost: "hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-gray-100",
link: "text-primary-600 underline-offset-4 hover:underline",
success: "bg-green-600 text-white hover:bg-green-700 focus-visible:ring-green-500",
warning: "bg-yellow-600 text-white hover:bg-yellow-700 focus-visible:ring-yellow-500",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-12 rounded-md px-8 text-base",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };Accessibility
The Button component is built with accessibility in mind, following WAI-ARIA best practices.
Keyboard Support
Enter- Activates the buttonSpace- Activates the buttonTab- Moves focus to/from button
Built-in Features
- ✓Focus-visible ring for keyboard navigation
- ✓Semantic
<button>element - ✓Disabled state properly announced
Accessibility Examples
// Icon-only buttons need aria-label
<Button size="icon" aria-label="Close dialog">
<XIcon className="h-4 w-4" />
</Button>
// Loading state with aria-busy
<Button disabled aria-busy="true">
<Spinner className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />
Saving...
</Button>
// Destructive actions - confirm intent
<Button
variant="destructive"
aria-describedby="delete-warning"
>
Delete Account
</Button>
<p id="delete-warning" className="sr-only">
This action cannot be undone
</p>
// Toggle buttons use aria-pressed
<Button
aria-pressed={isActive}
onClick={() => setIsActive(!isActive)}
>
{isActive ? "Active" : "Inactive"}
</Button>
// External links with announcement
<Button asChild>
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
Visit Site
<span className="sr-only">(opens in new tab)</span>
<ExternalLinkIcon className="ml-2 h-4 w-4" aria-hidden="true" />
</a>
</Button>Best Practices
- • Use descriptive button text that explains the action
- • Always add
aria-labelto icon-only buttons - • Use
aria-busy="true"during loading states - • Don't disable buttons without explaining why
- • Ensure sufficient color contrast (4.5:1 minimum)
Customization
Adding new variants
To add a new variant, simply extend the variants object in buttonVariants:
const buttonVariants = cva("...", {
variants: {
variant: {
// existing variants...
custom: "bg-purple-600 text-white hover:bg-purple-700",
},
},
});Changing default styles
Modify the base classes in the first argument to cva() to change the default appearance of all buttons.