feat: full shadcn/ui component system with consistent typography
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
This commit is contained in:
70
src/components/ui/button.tsx
Normal file
70
src/components/ui/button.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
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 };
|
||||
Reference in New Issue
Block a user