New components: - ui/button.tsx — Button with primary/accent/surface/ghost/destructive variants - ui/input.tsx — Input + Textarea with focus ring and CSS var theming - ui/label.tsx — Radix Label with peer-disabled support - ui/separator.tsx — Radix Separator - ui/tooltip.tsx — Radix Tooltip with themed content - ui/dialog.tsx — Radix Dialog replacing custom modal implementations - ui/table.tsx — Table/TableHeader/TableBody/TableRow/TableHead/TableCell - ui/typography.tsx — PageTitle/SectionHeading/Body/Muted/Caption/MonoCaption Wired throughout all components: - ConfirmDialog + FormModal migrated to Radix Dialog (focus trap, Escape, ARIA) - All raw <button> → Button, <input>/<textarea> → Input/Textarea - All repeated text patterns → typography components - All hardcoded hex/rgba strings → COLOR constants
71 lines
1.9 KiB
TypeScript
71 lines
1.9 KiB
TypeScript
import { cn } from "@/lib/utils";
|
|
import { Slot } from "@radix-ui/react-slot";
|
|
import { type VariantProps, cva } from "class-variance-authority";
|
|
import { forwardRef } from "react";
|
|
|
|
const buttonVariants = cva(
|
|
[
|
|
"inline-flex items-center justify-center gap-1.5 rounded-lg font-medium transition-all",
|
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-1)]",
|
|
"disabled:opacity-50 disabled:pointer-events-none",
|
|
],
|
|
{
|
|
variants: {
|
|
variant: {
|
|
primary: [
|
|
"text-white",
|
|
"[background:var(--accent)]",
|
|
"focus-visible:ring-[var(--accent)]",
|
|
],
|
|
accent: [
|
|
"[background:var(--accent-dim)] [color:var(--accent-text)]",
|
|
"[border:1px_solid_var(--accent-border)]",
|
|
"focus-visible:ring-[var(--accent)]",
|
|
],
|
|
surface: [
|
|
"[background:var(--surface)] [color:var(--text-2)]",
|
|
"[border:1px_solid_var(--border)]",
|
|
"focus-visible:ring-[var(--border)]",
|
|
],
|
|
ghost: [
|
|
"[color:var(--text-3)]",
|
|
"hover:[background:var(--surface)]",
|
|
"focus-visible:ring-[var(--border)]",
|
|
],
|
|
destructive: [
|
|
"bg-[rgba(239,68,68,0.08)] text-[#f87171]",
|
|
"border border-[rgba(239,68,68,0.2)]",
|
|
"focus-visible:ring-[#f87171]",
|
|
],
|
|
},
|
|
size: {
|
|
default: "px-4 py-2 text-sm",
|
|
sm: "px-3 py-1.5 text-xs",
|
|
icon: "p-1.5 text-sm",
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
variant: "accent",
|
|
size: "default",
|
|
},
|
|
},
|
|
);
|
|
|
|
export interface ButtonProps
|
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
VariantProps<typeof buttonVariants> {
|
|
asChild?: boolean;
|
|
}
|
|
|
|
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
const Comp = asChild ? Slot : "button";
|
|
return (
|
|
<Comp ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props} />
|
|
);
|
|
},
|
|
);
|
|
Button.displayName = "Button";
|
|
|
|
export { buttonVariants };
|