Frontend revamp: Animal Crossing × Claude design with shadcn components
- New palette: deep nook green sidebar, sage user bubbles, warm cream answer cards - shadcn-style UI primitives: Button (CVA variants), Textarea, Input, Badge, Table - cn() utility (clsx + tailwind-merge) - lucide-react icons throughout (no more text-only buttons) - Simba mode: custom CSS toggle switch - Send button: circular amber button with arrow icon - AnswerBubble: amber gradient accent bar, loading dots animation - QuestionBubble: sage green pill with rounded-3xl - ToolBubble: centered leaf-green badge pill - ConversationList: active item highlighting, proper selectedId prop - Sidebar: collapsible with PanelLeftClose/Open icons, icon-only collapsed state - LoginScreen: decorative background blobs, refined rounded card - AdminPanel: proper icon buttons, leaf-green save confirmation - Fonts: Playfair Display (brand) + Nunito 800 weight added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
26
raggr-frontend/src/components/ui/badge.tsx
Normal file
26
raggr-frontend/src/components/ui/badge.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium transition-colors",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-leaf-pale text-leaf-dark border border-leaf-light/50",
|
||||
amber: "bg-amber-pale text-amber-glow border border-amber-soft/40",
|
||||
muted: "bg-sand-light/60 text-warm-gray border border-sand/40",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
export const Badge = ({ className, variant, ...props }: BadgeProps) => {
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||
};
|
||||
48
raggr-frontend/src/components/ui/button.tsx
Normal file
48
raggr-frontend/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-semibold transition-all duration-200 disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-leaf text-white shadow-sm shadow-leaf/20 hover:bg-leaf-dark hover:shadow-md hover:shadow-leaf/30 active:scale-[0.97]",
|
||||
amber:
|
||||
"bg-amber-glow text-white shadow-sm shadow-amber/20 hover:bg-amber-dark hover:shadow-md active:scale-[0.97]",
|
||||
ghost:
|
||||
"text-cream/70 hover:text-cream hover:bg-white/8 active:scale-[0.97]",
|
||||
"ghost-dark":
|
||||
"text-warm-gray hover:text-charcoal hover:bg-sand-light/60 active:scale-[0.97]",
|
||||
outline:
|
||||
"border border-sand bg-transparent text-warm-gray hover:bg-cream-dark hover:text-charcoal active:scale-[0.97]",
|
||||
destructive:
|
||||
"text-red-400 hover:text-red-600 hover:bg-red-50 active:scale-[0.97]",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-7 px-3 text-xs",
|
||||
lg: "h-11 px-6 text-base",
|
||||
icon: "h-9 w-9",
|
||||
"icon-sm": "h-7 w-7",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {}
|
||||
|
||||
export const Button = ({ className, variant, size, ...props }: ButtonProps) => {
|
||||
return (
|
||||
<button
|
||||
className={cn(buttonVariants({ variant, size }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
19
raggr-frontend/src/components/ui/input.tsx
Normal file
19
raggr-frontend/src/components/ui/input.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
export const Input = ({ className, ...props }: InputProps) => {
|
||||
return (
|
||||
<input
|
||||
className={cn(
|
||||
"flex h-8 w-full rounded-lg border border-sand bg-cream px-3 py-1",
|
||||
"text-sm text-charcoal placeholder:text-warm-gray/50",
|
||||
"focus:outline-none focus:ring-2 focus:ring-amber-soft/60",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
37
raggr-frontend/src/components/ui/table.tsx
Normal file
37
raggr-frontend/src/components/ui/table.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
export const Table = ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
|
||||
<table className={cn("w-full caption-bottom text-sm", className)} {...props} />
|
||||
);
|
||||
|
||||
export const TableHeader = ({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>) => (
|
||||
<thead className={cn("[&_tr]:border-b [&_tr]:border-sand-light", className)} {...props} />
|
||||
);
|
||||
|
||||
export const TableBody = ({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>) => (
|
||||
<tbody className={cn("[&_tr:last-child]:border-0", className)} {...props} />
|
||||
);
|
||||
|
||||
export const TableRow = ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
|
||||
<tr
|
||||
className={cn(
|
||||
"border-b border-sand-light/50 transition-colors hover:bg-cream-dark/40",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export const TableHead = ({ className, ...props }: React.ThHTMLAttributes<HTMLTableCellElement>) => (
|
||||
<th
|
||||
className={cn(
|
||||
"h-10 px-4 text-left align-middle text-xs font-semibold text-warm-gray uppercase tracking-wider",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export const TableCell = ({ className, ...props }: React.TdHTMLAttributes<HTMLTableCellElement>) => (
|
||||
<td className={cn("px-4 py-3 align-middle", className)} {...props} />
|
||||
);
|
||||
19
raggr-frontend/src/components/ui/textarea.tsx
Normal file
19
raggr-frontend/src/components/ui/textarea.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
export const Textarea = ({ className, ...props }: TextareaProps) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex w-full resize-none rounded-xl border-0 bg-transparent px-3 py-2.5",
|
||||
"text-sm text-charcoal placeholder:text-warm-gray/50",
|
||||
"focus:outline-none",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user