Overview
The application uses shadcn/ui components, which are beautifully designed, accessible components built on top of Radix UI . These components are not imported as a package dependency but are copied into your project’s src/components/ui/ directory, giving you full control over the code.
All UI components are fully customizable and can be modified directly in the src/components/ui/ directory.
Core UI Components
The Button component provides multiple variants and sizes for different use cases.
src/components/ui/button.tsx
Usage Example
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
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" ,
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90" ,
destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90" ,
outline: "border bg-background shadow-xs hover:bg-accent" ,
secondary: "bg-secondary text-secondary-foreground shadow-xs 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 gap-1.5 px-3" ,
lg: "h-10 rounded-md px-6" ,
icon: "size-9" ,
},
},
defaultVariants: {
variant: "default" ,
size: "default" ,
},
}
)
function Button ({ className , variant , size , asChild = false , ... props }) {
const Comp = asChild ? Slot : "button"
return (
< Comp
className = { cn ( buttonVariants ({ variant , size , className })) }
{ ... props }
/>
)
}
Key Features:
Six variants: default, destructive, outline, secondary, ghost, link
Four sizes: sm, default, lg, icon
Support for rendering as child component using Radix UI’s Slot
Automatic disabled state styling
Icon support with proper sizing
Card
The Card component provides a flexible container for displaying content with multiple sub-components.
src/components/ui/card.tsx
Usage from Messages.tsx
import * as React from "react"
import { cn } from "@/lib/utils"
function Card ({ className , ... props } : React . ComponentProps < "div" >) {
return (
< div
className = { cn (
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm" ,
className
) }
{ ... props }
/>
)
}
function CardHeader ({ className , ... props }) {
return (
< div
className = { cn (
"grid auto-rows-min items-start gap-1.5 px-6" ,
className
) }
{ ... props }
/>
)
}
function CardContent ({ className , ... props }) {
return < div className = { cn ( "px-6" , className ) } { ... props } />
}
function CardDescription ({ className , ... props }) {
return (
< div
className = { cn ( "text-muted-foreground text-sm" , className ) }
{ ... props }
/>
)
}
Available Components:
Card - Main container
CardHeader - Top section for titles
CardContent - Main content area
CardDescription - Muted text for descriptions
CardTitle - Bold title text
CardFooter - Bottom action area
CardAction - Action button area
Dialog
The Dialog component creates accessible modal dialogs built on Radix UI’s Dialog primitive.
src/components/ui/dialog.tsx
Usage from People.tsx
"use client"
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Dialog ( props ) {
return < DialogPrimitive.Root { ... props } />
}
function DialogTrigger ( props ) {
return < DialogPrimitive.Trigger { ... props } />
}
function DialogContent ({ className , children , ... props }) {
return (
< DialogPortal >
< DialogOverlay />
< DialogPrimitive.Content
className = { cn (
"fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg" ,
className
) }
{ ... props }
>
{ children }
< DialogPrimitive.Close className = "absolute top-4 right-4" >
< XIcon />
</ DialogPrimitive.Close >
</ DialogPrimitive.Content >
</ DialogPortal >
)
}
Key Features:
Accessible keyboard navigation (ESC to close)
Focus trap and return focus
Overlay with backdrop
Smooth animations
Centered positioning
Automatic close button
The Input component provides styled text input fields with validation states.
src/components/ui/input.tsx
Usage from CreatePersonForm.tsx
import * as React from "react"
import { cn } from "@/lib/utils"
function Input ({ className , type , ... props }) {
return (
< input
type = { type }
className = { cn (
"flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-base shadow-xs" ,
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]" ,
"aria-invalid:ring-destructive/20 aria-invalid:border-destructive" ,
"disabled:cursor-not-allowed disabled:opacity-50" ,
className
) }
{ ... props }
/>
)
}
Textarea
Multi-line text input for longer content.
src/components/CreateMessageForm.tsx
import { Textarea } from "./ui/textarea" ;
import { Label } from "./ui/label" ;
< div className = "space-y-2" >
< Label htmlFor = "content" className = "font-bold" >
Message
</ Label >
< Textarea name = "content" className = "h-32" />
</ div >
Select
Dropdown selection menu built on Radix UI Select primitive.
src/components/ui/select.tsx
Usage from CreateMessageForm.tsx
"use client"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon , ChevronDownIcon } from "lucide-react"
function Select ( props ) {
return < SelectPrimitive.Root { ... props } />
}
function SelectTrigger ({ className , children , ... props }) {
return (
< SelectPrimitive.Trigger
className = { cn (
"flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2" ,
className
) }
{ ... props }
>
{ children }
< SelectPrimitive.Icon asChild >
< ChevronDownIcon className = "size-4 opacity-50" />
</ SelectPrimitive.Icon >
</ SelectPrimitive.Trigger >
)
}
Custom scrollable container with styled scrollbars.
src/components/Messages.tsx
import { ScrollArea } from "./ui/scroll-area" ;
< ScrollArea className = "max-h-[calc(100vh-180px)] overflow-y-auto pr-4 mb-3" >
{ messages . map (( message : Message ) => (
< Message key = { message . id } message = { message } people = { people } />
)) }
</ ScrollArea >
The ScrollArea uses calc(100vh-180px) to ensure the list fits within the viewport while accounting for headers and footers.
Alert
Display important messages and notifications.
src/components/CreateMessageForm.tsx
import { Alert , AlertTitle } from "./ui/alert" ;
{ createState . error && (
< Alert variant = "destructive" >
< AlertTitle > { createState . error } </ AlertTitle >
</ Alert >
)}
{ createState . success && (
< Alert className = "text-green-600" >
< AlertTitle > Message Created successfully </ AlertTitle >
</ Alert >
)}
Label
Accessible form field labels built on Radix UI.
import { Label } from "./ui/label" ;
< Label htmlFor = "name" className = "font-bold" >
Name
</ Label >
Utility Function
All UI components use the cn() utility function for merging Tailwind classes:
import { clsx , type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn ( ... inputs : ClassValue []) {
return twMerge ( clsx ( inputs ))
}
This function combines:
clsx - For conditional class names
tailwind-merge - For properly merging Tailwind classes (avoiding conflicts)
Component Variants with CVA
Some components use class-variance-authority (CVA) to manage variants:
import { cva , type VariantProps } from "class-variance-authority"
const buttonVariants = cva (
"base-classes" ,
{
variants: {
variant: {
default: "variant-classes" ,
destructive: "variant-classes" ,
},
size: {
default: "size-classes" ,
sm: "size-classes" ,
},
},
defaultVariants: {
variant: "default" ,
size: "default" ,
},
}
)
Styling Philosophy
Utility-First All styling is done with Tailwind utility classes directly in components
Composition Complex components are built by composing simpler primitives
Accessibility Built on Radix UI primitives ensuring ARIA compliance
Customizable All code is in your project - modify anything you need
Dark Mode Support
All components support dark mode through Tailwind’s dark: variant:
className = "bg-background dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
The application uses class-based dark mode. Ensure your Tailwind config has darkMode: 'class' configured.
Next Steps
Forms Learn how these UI components are used in forms
Layouts See how components are arranged in layouts