Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1import React from "react";
2import { clsx } from "clsx";
3
4type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
5type ButtonSize = "sm" | "md" | "lg";
6
7interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
8 variant?: ButtonVariant;
9 size?: ButtonSize;
10 loading?: boolean;
11 icon?: React.ReactNode;
12 children?: React.ReactNode;
13}
14
15const variants: Record<ButtonVariant, string> = {
16 primary: "bg-primary-600 text-white hover:bg-primary-500 shadow-sm",
17 secondary:
18 "bg-surface-100 dark:bg-surface-800 text-surface-900 dark:text-white hover:bg-surface-200 dark:hover:bg-surface-700",
19 ghost:
20 "text-surface-600 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-800 hover:text-surface-900 dark:hover:text-white",
21 danger: "bg-red-600 text-white hover:bg-red-500",
22};
23
24const sizes: Record<ButtonSize, string> = {
25 sm: "px-3 py-1.5 text-sm gap-1.5",
26 md: "px-4 py-2 text-sm gap-2",
27 lg: "px-6 py-3 text-base gap-2",
28};
29
30export default function Button({
31 variant = "primary",
32 size = "md",
33 loading = false,
34 icon,
35 children,
36 className,
37 disabled,
38 ...props
39}: ButtonProps) {
40 return (
41 <button
42 className={clsx(
43 "inline-flex items-center justify-center font-medium rounded-lg transition-all duration-200",
44 "focus:outline-none focus:ring-2 focus:ring-primary-500/20",
45 "disabled:opacity-50 disabled:cursor-not-allowed",
46 variants[variant],
47 sizes[size],
48 className,
49 )}
50 disabled={disabled || loading}
51 {...props}
52 >
53 {loading ? (
54 <svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
55 <circle
56 className="opacity-25"
57 cx="12"
58 cy="12"
59 r="10"
60 stroke="currentColor"
61 strokeWidth="4"
62 fill="none"
63 />
64 <path
65 className="opacity-75"
66 fill="currentColor"
67 d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
68 />
69 </svg>
70 ) : (
71 icon
72 )}
73 {children && <span>{children}</span>}
74 </button>
75 );
76}