deadhead-space-freight-tycoon/apps/web/src/ui/Button.tsx

58 lines
1.9 KiB
TypeScript

import type { ButtonHTMLAttributes } from "react";
export type ButtonVariant = "primary" | "secondary" | "ghost" | "success" | "danger";
export type ButtonSize = "sm" | "md" | "lg";
const VARIANTS: Record<ButtonVariant, string> = {
primary:
"bg-linear-to-b from-amber-300 to-amber-500 text-space-950 shadow-glow-amber hover:from-amber-200 hover:to-amber-400",
secondary:
"border border-space-500 bg-space-800/80 text-ink-50 hover:border-amber-400/60 hover:bg-space-700",
ghost: "text-ink-300 hover:bg-white/5 hover:text-ink-50",
success:
"bg-linear-to-b from-emerald-300 to-emerald-500 text-space-950 shadow-glow-emerald hover:from-emerald-200 hover:to-emerald-400",
danger: "border border-rose-500/60 bg-rose-500/15 text-rose-200 hover:bg-rose-500/25",
};
const SIZES: Record<ButtonSize, string> = {
sm: "min-h-9 px-3 text-xs",
md: "min-h-11 px-4 text-sm",
lg: "min-h-13 px-6 text-base",
};
/** Classes for anything that should look like a button, e.g. a router `Link`. */
export function buttonClass(
variant: ButtonVariant = "primary",
size: ButtonSize = "md",
block = false,
): string {
return [
"inline-flex items-center justify-center gap-2 rounded-lg font-display font-semibold uppercase tracking-wider",
"transition duration-150 select-none active:translate-y-px active:scale-[0.98]",
"disabled:pointer-events-none disabled:opacity-40 disabled:shadow-none disabled:saturate-50",
VARIANTS[variant],
SIZES[size],
block ? "w-full" : "",
].join(" ");
}
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
/** Full width. */
block?: boolean;
}
export function Button({
variant,
size,
block,
className = "",
type = "button",
...rest
}: ButtonProps) {
return (
<button type={type} className={`${buttonClass(variant, size, block)} ${className}`} {...rest} />
);
}