button-unification
Standardize button component heights, padding, and transitions across all variants. Ensures consistent appearance for default, outline, secondary, ghost, and link variants. Updates button.tsx with unified styles.
When & Why to Use This Skill
This Claude skill automates the standardization of React button components to ensure visual consistency across a design system. It specifically targets the unification of heights, padding, transitions, and typography for all UI variants (default, outline, secondary, ghost, and link) using Tailwind CSS and CVA (Class Variance Authority). By auditing and updating component files, it eliminates design debt and ensures a professional, cohesive user interface.
Use Cases
- Design System Alignment: Automatically update legacy button components to match new brand guidelines for consistent spacing, border-radius, and sizing.
- UI Refactoring: Clean up inconsistent Tailwind CSS classes in 'button.tsx' to ensure that buttons of different variants align perfectly when placed side-by-side.
- Icon Integration: Standardize dimensions for icon-only buttons and buttons with leading icons to maintain vertical rhythm across the application.
- Next.js Navigation Optimization: Correctly implement the 'asChild' pattern for Next.js Link components within buttons to ensure seamless client-side routing and styling.
| name | button-unification |
|---|---|
| description | Standardize button component heights, padding, and transitions across all variants. Ensures consistent appearance for default, outline, secondary, ghost, and link variants. Updates button.tsx with unified styles. |
Button Unification
Standardize the button component for consistent heights, padding, and transitions across all variants.
Workflow
- Find Button Component - Locate
website/components/ui/button.tsx - Audit Current Styles - Review existing buttonVariants
- Standardize Heights - Unified height per size
- Standardize Padding - Consistent padding per size
- Standardize Typography - Same font size and weight and variant
- Unify Border-Radius - Same radius for all variants
- Unify Transitions - Same transition for all variants
- Background - Ensure background styles are consistent for each variant
- Icon Sizes - Standardize icon button dimensions for each size
- Text Color - Ensure text colors are consistent per variant
- Verify Consistency - Test all variant + size combinations
Size Specifications
Heights (Unified)
| Size | Height | Class |
|---|---|---|
| default | 36px | h-9 |
| sm | 32px | h-8 |
| lg | 40px | h-10 |
| icon | 36px | size-9 |
| icon-sm | 32px | size-8 |
| icon-lg | 40px | size-10 |
Padding (Unified)
| Size | Horizontal | With Icon | Gap |
|---|---|---|---|
| default | px-4 | has-[>svg]:px-3 | gap-2 |
| sm | px-3 | has-[>svg]:px-2.5 | gap-1.5 |
| lg | px-6 | has-[>svg]:px-4 | gap-2 |
Base Styles (All Variants)
All button variants share these base styles:
// Base class (applied to ALL variants)
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm text-white font-medium transition-all"
Key unified properties:
rounded-md- Consistent border radiustext-sm font-medium text-white- Consistent typographytransition-all- Consistent transitionsgap-2(or gap-1.5 for sm) - Consistent spacing
Updated buttonVariants
See references/button-patterns.md for the complete CVA configuration.
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]",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-white hover:bg-destructive/90",
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 px-6 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
Visual Consistency Check
After updating, verify:
- Same Height - Buttons in a row align perfectly
- Same Padding - Text has consistent spacing
- Same Radius - Corners match across variants
- Same Transition - Hover effects are smooth and consistent
- Same Font - Text appears identical size
- Icon Sizes - Icon buttons have correct dimensions
Button with Next.js Link
When a button needs to navigate, use the asChild prop with Next.js <Link>:
import Link from "next/link";
import { Button } from "@/components/ui/button";
// Correct pattern - Button wraps Link with asChild
<Button asChild>
<Link href="/login">Login</Link>
</Button>
<Button asChild variant="outline" size="sm">
<Link href="/contact">Contact</Link>
</Button>
<Button asChild variant="ghost" size="icon">
<Link href="/settings">
<Settings className="size-4" />
</Link>
</Button>
Key points:
- Always use
asChildprop when wrapping Link - Link is the child, Button provides styling
- Works with all variants and sizes
- Preserves Next.js client-side navigation
DO NOT do this:
// WRONG - Button onClick with router.push
<Button onClick={() => router.push('/login')}>Login</Button>
// WRONG - Link wrapping Button
<Link href="/login"><Button>Login</Button></Link>
Testing
Test all variant + size combinations:
// All variants at same size should have identical height
<div className="flex gap-2">
<Button variant="default">Default</Button>
<Button variant="outline">Outline</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
</div>
Checklist
- All sizes have unified heights (h-9, h-8, h-10)
- All sizes have unified padding (px-4, px-3, px-6)
- All variants use rounded-md
- All variants use transition-all
- All sizes use text-sm font-medium
- Icon sizes use square dimensions (size-9, size-8, size-10)
- Gap is consistent per size (gap-2, gap-1.5)
- Focus ring styles are consistent across variants
- Navigation buttons use
asChildwith<Link>