SPB Git forge

spb/spb-cloud

Public
10commits 1branches 0releases
3.2 MBsize
maindefault branch
2 h agolast push
JavaScript 56.7% TypeScript 42.6%
2.9 KB · 58 lines tsx
Raw Blame History
1"use client";23import { motion } from "framer-motion";4import { Loader2 } from "lucide-react";5import { ButtonHTMLAttributes, forwardRef } from "react";67interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {8  variant?: "primary" | "secondary" | "danger" | "ghost" | "outline" | "accent";9  size?: "xs" | "sm" | "md" | "lg" | "icon";10  loading?: boolean;11}1213const variants: Record<NonNullable<ButtonProps["variant"]>, string> = {14  // Noir/blanc net — action principale neutre15  primary:16    "bg-gray-900 text-white hover:bg-gray-800 active:bg-gray-700 shadow-card dark:bg-white dark:text-gray-950 dark:hover:bg-gray-100 dark:active:bg-gray-200",17  // Accent dégradé — action « héro » (Upload, Créer…)18  accent:19    "text-white bg-accent-gradient shadow-[0_1px_2px_rgb(0_0_0/.15),inset_0_1px_0_rgb(255_255_255/.18)] hover:brightness-110 active:brightness-95 hover:shadow-glow",20  secondary:21    "bg-gray-100 text-gray-800 hover:bg-gray-200 active:bg-gray-300 border border-gray-200/80 dark:bg-white/[0.07] dark:text-white dark:hover:bg-white/[0.12] dark:active:bg-white/[0.16] dark:border-white/10",22  outline:23    "bg-transparent text-gray-700 border border-gray-300 hover:bg-gray-50 hover:border-gray-400 dark:text-gray-200 dark:border-white/15 dark:hover:bg-white/5 dark:hover:border-white/25",24  danger:25    "bg-rose-50 text-rose-600 hover:bg-rose-100 active:bg-rose-200 border border-rose-200 dark:bg-rose-500/10 dark:text-rose-400 dark:hover:bg-rose-500/20 dark:active:bg-rose-500/30 dark:border-rose-500/20",26  ghost:27    "text-gray-600 hover:text-gray-900 hover:bg-gray-100 active:bg-gray-200 dark:text-gray-400 dark:hover:text-white dark:hover:bg-white/[0.07] dark:active:bg-white/10",28};2930// Cibles tactiles ≥ 40px sur mobile31const sizes: Record<NonNullable<ButtonProps["size"]>, string> = {32  xs: "px-2.5 py-1.5 text-xs min-h-[32px] rounded-lg gap-1.5",33  sm: "px-3 py-2 text-[13px] min-h-[38px] sm:min-h-[34px] rounded-xl gap-1.5",34  md: "px-4 py-2.5 text-sm min-h-[44px] sm:min-h-[40px] rounded-xl gap-2",35  lg: "px-6 py-3.5 text-base min-h-[48px] rounded-2xl gap-2",36  icon: "w-10 h-10 min-w-[40px] min-h-[40px] rounded-xl p-0",37};3839const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(40  { variant = "primary", size = "md", loading, children, className = "", disabled, ...props },41  ref42) {43  return (44    <motion.button45      ref={ref}46      whileTap={{ scale: 0.97 }}47      className={`inline-flex items-center justify-center font-medium transition-all duration-200 ease-spring disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none select-none whitespace-nowrap ${variants[variant]} ${sizes[size]} ${className}`}48      disabled={disabled || loading}49      {...(props as Record<string, unknown>)}50    >51      {loading && <Loader2 className="w-4 h-4 animate-spin" />}52      {children}53    </motion.button>54  );55});5657export default Button;58