File size: 1,885 Bytes
65d68f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | "use client";
import { forwardRef } from "react";
import { cn } from "@/lib/utils";
// =============================================================================
// CleanCard — White card with subtle border, optional hover lift
// =============================================================================
export type CleanCardVariant = "default" | "outlined" | "elevated" | "ghost";
export interface CleanCardProps extends React.HTMLAttributes<HTMLDivElement> {
variant?: CleanCardVariant;
padding?: "none" | "sm" | "md" | "lg";
hoverable?: boolean;
}
const variantClasses: Record<CleanCardVariant, string> = {
default:
"bg-white border border-[#E4E6EC] shadow-[0_1px_2px_rgba(15,17,23,0.03),0_8px_24px_rgba(15,17,23,0.04)]",
outlined: "bg-white border border-[#E4E6EC]",
elevated:
"bg-white border border-[#EEF0F4] shadow-[0_4px_12px_rgba(15,17,23,0.05),0_16px_40px_-12px_rgba(15,17,23,0.1)]",
ghost: "bg-transparent",
};
const paddingClasses: Record<NonNullable<CleanCardProps["padding"]>, string> = {
none: "",
sm: "p-3",
md: "p-5",
lg: "p-6",
};
export const CleanCard = forwardRef<HTMLDivElement, CleanCardProps>(
({ variant = "default", padding = "md", hoverable = false, className, children, ...props }, ref) => {
return (
<div
ref={ref}
className={cn(
"rounded-xl transition-[transform,box-shadow,border-color] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)]",
variantClasses[variant],
paddingClasses[padding],
hoverable &&
"cursor-pointer hover:-translate-y-0.5 hover:border-[#D4D7E0] hover:shadow-[0_2px_4px_rgba(15,17,23,0.04),0_14px_36px_rgba(15,17,23,0.08)] active:translate-y-0 active:scale-[0.995]",
className,
)}
{...props}
>
{children}
</div>
);
},
);
CleanCard.displayName = "CleanCard";
|