card.tsx
1 import * as React from "react"; 2 3 import { VariantProps, cva } from "class-variance-authority"; 4 import { cn } from "../../lib/utils"; 5 6 const cardVariants = cva("shadow", { 7 variants: { 8 size: { 9 xs: "text-xs", 10 sm: "text-sm", 11 md: "text-base", 12 lg: "text-lg", 13 xl: "text-xl", 14 }, 15 padding: { 16 none: "p-0", 17 xs: "p-1", 18 sm: "p-2", 19 md: "p-4", 20 lg: "p-8", 21 xl: "p-10", 22 }, 23 margin: { 24 none: "m-0", 25 xs: "m-1", 26 sm: "m-2", 27 md: "m-4", 28 lg: "m-8", 29 xl: "m-10", 30 }, 31 color: { 32 primary: "bg-primary/5 border-primary", 33 secondary: "bg-secondary/5 border-secondary", 34 error: "bg-error/5 border-error", 35 success: "bg-success/5 border-success", 36 warning: "bg-warning/5 border-warning", 37 info: "bg-info/5 border-info", 38 muted: "bg-gray-400/5 border-gray-500", 39 }, 40 border: { 41 0: "border-0", 42 1: "border-1", 43 2: "border-2", 44 3: "border-3", 45 4: "border-4", 46 }, 47 round: { 48 none: "rounded-none", 49 both: "rounded-sm", 50 left: "rounded-l-sm", 51 right: "rounded-r-sm", 52 top: "rounded-t-sm", 53 bottom: "rounded-b-sm", 54 topLeft: "rounded-tl-sm", 55 topRight: "rounded-tr-sm", 56 bottomLeft: "rounded-bl-sm", 57 bottomRight: "rounded-br-sm", 58 }, 59 }, 60 defaultVariants: { 61 color: "muted", 62 size: "md", 63 border: 1, 64 round: "both", 65 padding: "none", 66 margin: "xs", 67 }, 68 }); 69 70 export interface CardProps 71 extends Omit<React.HTMLAttributes<HTMLDivElement>, "color">, 72 VariantProps<typeof cardVariants> { 73 asChild?: boolean; 74 noShadow?: boolean; 75 } 76 77 const Card = React.forwardRef<HTMLDivElement, CardProps>( 78 ( 79 { 80 className, 81 size, 82 noShadow, 83 color, 84 border, 85 round, 86 padding, 87 margin, 88 ...props 89 }, 90 ref, 91 ) => ( 92 <div 93 ref={ref} 94 className={cn( 95 cardVariants({ round, size, border, color, padding, margin }), 96 `rounded-${round}`, 97 noShadow && "shadow-none", 98 className, 99 )} 100 {...props} 101 /> 102 ), 103 ); 104 Card.displayName = "Card"; 105 106 const CardHeader = React.forwardRef< 107 HTMLDivElement, 108 React.HTMLAttributes<HTMLDivElement> 109 >(({ className, ...props }, ref) => ( 110 <div 111 ref={ref} 112 className={cn("flex flex-col space-y-1.5 p-6", className)} 113 {...props} 114 /> 115 )); 116 CardHeader.displayName = "CardHeader"; 117 118 const CardTitle = React.forwardRef< 119 HTMLParagraphElement, 120 React.HTMLAttributes<HTMLHeadingElement> 121 >(({ className, ...props }, ref) => ( 122 <h3 123 ref={ref} 124 className={cn("font-semibold leading-none tracking-tight", className)} 125 {...props} 126 /> 127 )); 128 CardTitle.displayName = "CardTitle"; 129 130 const CardDescription = React.forwardRef< 131 HTMLParagraphElement, 132 React.HTMLAttributes<HTMLParagraphElement> 133 >(({ className, ...props }, ref) => ( 134 <p 135 ref={ref} 136 className={cn("text-sm text-slate-500 dark:text-slate-400", className)} 137 {...props} 138 /> 139 )); 140 CardDescription.displayName = "CardDescription"; 141 142 const CardContent = React.forwardRef< 143 HTMLDivElement, 144 React.HTMLAttributes<HTMLDivElement> 145 >(({ className, ...props }, ref) => ( 146 <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> 147 )); 148 CardContent.displayName = "CardContent"; 149 150 const CardFooter = React.forwardRef< 151 HTMLDivElement, 152 React.HTMLAttributes<HTMLDivElement> 153 >(({ className, ...props }, ref) => ( 154 <div 155 ref={ref} 156 className={cn("flex items-center p-6 pt-0", className)} 157 {...props} 158 /> 159 )); 160 CardFooter.displayName = "CardFooter"; 161 162 export { 163 Card, 164 CardContent, 165 CardDescription, 166 CardFooter, 167 CardHeader, 168 CardTitle, 169 };