Frontend
UI Components
Reusable UI components built with Radix UI and Tailwind CSS
Overview
The component library is built on Radix UI primitives with Tailwind CSS styling, using class-variance-authority (CVA) for variant management.
Component Location
src/components/
├── ui/ # Primitive UI components
│ ├── accordion.tsx
│ ├── avatar.tsx
│ ├── badge.tsx
│ ├── button.tsx
│ ├── card.tsx
│ ├── chart.tsx
│ ├── glass-card.tsx
│ ├── input.tsx
│ ├── password-input.tsx
│ ├── progress.tsx
│ ├── select.tsx
│ └── index.ts
├── AuthBackground/
├── ErrorBoundary/
├── ParticleBackground/
└── Navbar.tsxButton Component
The Button component supports multiple variants and sizes:
import { Button } from "@/components/ui/button";
// Variants
<Button variant="default">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
// Sizes
<Button size="default">Default</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon">🔍</Button>
// As child (polymorphic)
<Button asChild>
<a href="/login">Login</a>
</Button>Button Implementation
import { cn } from "@/lib/utils";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
const buttonVariants = cva(
"inline-flex cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);Input Component
import { Input } from "@/components/ui/input";
<Input type="text" placeholder="Enter your name" />
<Input type="email" placeholder="email@example.com" />
<Input type="password" placeholder="Password" />
<Input disabled placeholder="Disabled" />Password Input
A specialized input with show/hide toggle:
import { PasswordInput } from "@/components/ui/password-input";
<PasswordInput placeholder="Enter password" />Select Component
Built on Radix UI Select:
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="employer">Employer</SelectItem>
<SelectItem value="talent">Talent</SelectItem>
</SelectContent>
</Select>Card Component
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description text</CardDescription>
</CardHeader>
<CardContent>
<p>Card content goes here</p>
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>Glass Card
A glassmorphism-styled card variant:
import { GlassCard } from "@/components/ui/glass-card";
<GlassCard>
<h2>Glassmorphism Effect</h2>
<p>Content with blur background</p>
</GlassCard>Avatar Component
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
<Avatar>
<AvatarImage src="/avatar.jpg" alt="User" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>Badge Component
import { Badge } from "@/components/ui/badge";
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>Progress Component
import { Progress } from "@/components/ui/progress";
<Progress value={33} />
<Progress value={66} />
<Progress value={100} />Accordion Component
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>Utility Function
The cn utility merges Tailwind classes:
// lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Usage:
<div className={cn("base-class", condition && "conditional-class", className)}>Importing Components
All UI components are exported from the barrel file:
import {
Button,
Input,
Card,
CardHeader,
CardContent,
Select,
Avatar,
Badge,
Progress,
} from "@/components/ui";