aboutsummaryrefslogtreecommitdiffstats
path: root/frontend/src/components/ui
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/ui')
-rw-r--r--frontend/src/components/ui/button.tsx31
-rw-r--r--frontend/src/components/ui/card.tsx23
-rw-r--r--frontend/src/components/ui/input.tsx34
3 files changed, 59 insertions, 29 deletions
diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx
index a2df8dc..347beaf 100644
--- a/frontend/src/components/ui/button.tsx
+++ b/frontend/src/components/ui/button.tsx
@@ -5,32 +5,44 @@ 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-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-300 ease-in-out disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive active:scale-[0.98] will-change-transform",
{
variants: {
variant: {
default:
- "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
+ "bg-primary text-primary-foreground shadow-sm hover:shadow-md hover:bg-primary/90 hover:translate-y-[-1px]",
destructive:
- "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ "bg-destructive text-white shadow-sm hover:shadow-md hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 hover:translate-y-[-1px]",
outline:
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ "border bg-background shadow-sm hover:shadow-md hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 hover:border-primary/50",
secondary:
- "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
+ "bg-secondary text-secondary-foreground shadow-sm hover:shadow-md hover:bg-secondary/80 hover:translate-y-[-1px]",
ghost:
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
- link: "text-primary underline-offset-4 hover:underline",
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 hover:scale-[1.02]",
+ link: "text-primary underline-offset-4 hover:underline hover:text-primary/80",
+ gradient:
+ "bg-gradient-to-r from-primary to-primary/80 text-primary-foreground shadow-sm hover:shadow-md hover:opacity-90 hover:translate-y-[-1px] transition-all duration-300",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
- icon: "size-9",
+ xl: "h-12 rounded-md px-8 text-base has-[>svg]:px-6",
+ icon: "size-9 p-0 aspect-square",
+ "icon-sm": "size-8 p-0 aspect-square rounded-md",
},
+ animation: {
+ none: "",
+ pulse: "animate-pulse",
+ bounce: "animate-bounce",
+ spin: "[&_svg]:animate-spin",
+ fadeIn: "animate-in fade-in",
+ }
},
defaultVariants: {
variant: "default",
size: "default",
+ animation: "none",
},
}
)
@@ -39,6 +51,7 @@ function Button({
className,
variant,
size,
+ animation,
asChild = false,
...props
}: React.ComponentProps<"button"> &
@@ -50,7 +63,7 @@ function Button({
return (
<Comp
data-slot="button"
- className={cn(buttonVariants({ variant, size, className }))}
+ className={cn(buttonVariants({ variant, size, animation, className }))}
{...props}
/>
)
diff --git a/frontend/src/components/ui/card.tsx b/frontend/src/components/ui/card.tsx
index d05bbc6..9ec9328 100644
--- a/frontend/src/components/ui/card.tsx
+++ b/frontend/src/components/ui/card.tsx
@@ -2,12 +2,24 @@ import * as React from "react"
import { cn } from "@/lib/utils"
-function Card({ className, ...props }: React.ComponentProps<"div">) {
+const cardVariants = {
+ default: "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm transition-all duration-300",
+ interactive: "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm hover:shadow-md transition-all duration-300 hover:border-primary/20 hover:translate-y-[-2px]",
+ elevated: "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border-0 py-6 shadow-md transition-all duration-300",
+ outline: "bg-transparent text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-none transition-all duration-300",
+ gradient: "bg-gradient-to-br from-card to-background text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm transition-all duration-300",
+}
+
+interface CardProps extends React.ComponentProps<"div"> {
+ variant?: keyof typeof cardVariants;
+}
+
+function Card({ className, variant = "default", ...props }: CardProps) {
return (
<div
data-slot="card"
className={cn(
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
+ cardVariants[variant],
className
)}
{...props}
@@ -20,7 +32,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card-header"
className={cn(
- "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6 animate-in fade-in-50 duration-300",
className
)}
{...props}
@@ -65,7 +77,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
- className={cn("px-6", className)}
+ className={cn("px-6 animate-in fade-in-50 duration-300 delay-75", className)}
{...props}
/>
)
@@ -75,7 +87,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
- className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
+ className={cn("flex items-center px-6 [.border-t]:pt-6 animate-in fade-in-50 duration-300 delay-150", className)}
{...props}
/>
)
@@ -89,4 +101,5 @@ export {
CardAction,
CardDescription,
CardContent,
+ cardVariants,
}
diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx
index 03295ca..91d3df1 100644
--- a/frontend/src/components/ui/input.tsx
+++ b/frontend/src/components/ui/input.tsx
@@ -2,20 +2,24 @@ import * as React from "react"
import { cn } from "@/lib/utils"
-function Input({ className, type, ...props }: React.ComponentProps<"input">) {
- return (
- <input
- type={type}
- data-slot="input"
- className={cn(
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
- className
- )}
- {...props}
- />
- )
-}
+export interface InputProps
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
+
+const Input = React.forwardRef<HTMLInputElement, InputProps>(
+ ({ className, type, ...props }, ref) => {
+ return (
+ <input
+ type={type}
+ className={cn(
+ "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-primary/50 focus-visible:ring-2 focus-visible:ring-primary/20 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-all duration-200 shadow-sm hover:shadow-md hover:border-primary/30 file:border-0 file:bg-transparent file:text-sm file:font-medium",
+ className
+ )}
+ ref={ref}
+ {...props}
+ />
+ )
+ }
+)
+Input.displayName = "Input"
export { Input }