skills/shadcn-ui/SKILL.md
Build premium interfaces with shadcn/ui components, Radix primitives, and Tailwind CSS. Covers installation, component customization, theming, dark mode, form patterns with react-hook-form + Zod, data tables with sorting/filtering/pagination, command palette, toast patterns, and composable component architecture. Use when building UI with shadcn/ui.
npx skillsauth add RaheesAhmed/SajiCode shadcn-ui-systemInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
npx -y shadcn@latest init -d
npx -y shadcn@latest add button card input label dialog sheet dropdown-menu tabs avatar badge separator skeleton toast sonner command table form select checkbox
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
<Card className="group cursor-pointer border-border/50 bg-card/50 backdrop-blur-sm transition-all duration-300 hover:border-primary/50 hover:shadow-lg hover:shadow-primary/5">
<CardHeader>
<CardTitle className="text-lg font-semibold tracking-tight">Title</CardTitle>
<CardDescription>Description text</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">Content area</p>
</CardContent>
</Card>
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex h-screen bg-background">
<aside className="hidden w-64 border-r border-border/50 bg-card/30 backdrop-blur-md lg:block">
<Sidebar />
</aside>
<div className="flex flex-1 flex-col overflow-hidden">
<header className="flex h-14 items-center gap-4 border-b border-border/50 bg-card/30 px-6 backdrop-blur-md">
<Header />
</header>
<main className="flex-1 overflow-auto p-6">{children}</main>
</div>
</div>
);
}
"use client";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
const formSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Invalid email address"),
});
type FormValues = z.infer<typeof formSchema>;
export function ContactForm() {
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: { name: "", email: "" },
});
async function onSubmit(values: FormValues) {
await createContact(values);
form.reset();
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField control={form.control} name="name" render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl><Input placeholder="John Doe" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<FormField control={form.control} name="email" render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input placeholder="[email protected]" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<Button type="submit" disabled={form.formState.isSubmitting}>
{form.formState.isSubmitting ? "Submitting..." : "Submit"}
</Button>
</form>
</Form>
);
}
"use client";
import { useState } from "react";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ArrowUpDown } from "lucide-react";
interface DataTableProps<T> {
data: T[];
columns: { key: keyof T; label: string; sortable?: boolean; render?: (value: T[keyof T], row: T) => React.ReactNode }[];
searchKey?: keyof T;
}
export function DataTable<T extends Record<string, unknown>>({ data, columns, searchKey }: DataTableProps<T>) {
const [search, setSearch] = useState("");
const [sortKey, setSortKey] = useState<keyof T | null>(null);
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
const filtered = searchKey
? data.filter((item) => String(item[searchKey]).toLowerCase().includes(search.toLowerCase()))
: data;
const sorted = sortKey
? [...filtered].sort((a, b) => {
const val = String(a[sortKey]).localeCompare(String(b[sortKey]));
return sortDir === "asc" ? val : -val;
})
: filtered;
return (
<div className="space-y-4">
{searchKey && <Input placeholder="Search..." value={search} onChange={(e) => setSearch(e.target.value)} className="max-w-sm" />}
<div className="rounded-xl border border-border/50 bg-card/50 backdrop-blur-sm">
<Table>
<TableHeader>
<TableRow className="hover:bg-transparent">
{columns.map((col) => (
<TableHead key={String(col.key)} className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
{col.sortable ? (
<Button variant="ghost" size="sm" onClick={() => { setSortKey(col.key); setSortDir(sortDir === "asc" ? "desc" : "asc"); }}>
{col.label} <ArrowUpDown className="ml-1 h-3 w-3" />
</Button>
) : col.label}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{sorted.map((row, i) => (
<TableRow key={i} className="transition-colors hover:bg-muted/50">
{columns.map((col) => (
<TableCell key={String(col.key)}>
{col.render ? col.render(row[col.key], row) : String(row[col.key])}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
);
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--primary: 262 83% 58%;
--primary-foreground: 0 0% 98%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--border: 240 5.9% 90%;
--ring: 262 83% 58%;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 5.9%;
--primary: 262 83% 58%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--border: 240 3.7% 15.9%;
}
}
npx shadcn@latest add [component] — NEVER manually createcn() for conditional classesprimary, muted, destructive) — NOT raw colorsnext-themescomponents/ui/ untouched — customize in feature componentsimport { toast } from "sonner"development
Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
development
Deep web research and data extraction skill. Systematically research ANY topic by fetching URLs, reading documentation, crawling API docs, evaluating npm/pypi packages, comparing technologies, and synthesizing findings into actionable recommendations. Use when researching libraries, frameworks, APIs, solutions, or any topic requiring web investigation.
development
Design and implement comprehensive test suites. Covers unit testing, integration testing, E2E testing with Playwright, API testing, mocking strategies, test data factories, TDD workflow, snapshot testing, coverage targets, and CI integration. Use when writing tests, designing test architecture, or debugging test failures.
development
Core engineering workflow that activates on EVERY task. Enforces systematic plan-before-code methodology, multi-file refactoring safety, dependency-aware changes, pre-flight verification, and zero-placeholder quality standards. Use PROACTIVELY on all coding tasks.