{
  "schemaVersion": "1.0",
  "item": {
    "slug": "shadcn-ui",
    "name": "Shadcn Ui",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/jgarrison929/shadcn-ui",
    "canonicalUrl": "https://clawhub.ai/jgarrison929/shadcn-ui",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/shadcn-ui",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=shadcn-ui",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/shadcn-ui"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/shadcn-ui",
    "agentPageUrl": "https://openagent3.xyz/skills/shadcn-ui/agent",
    "manifestUrl": "https://openagent3.xyz/skills/shadcn-ui/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/shadcn-ui/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "shadcn/ui Expert",
        "body": "Comprehensive guide for building production UIs with shadcn/ui, Tailwind CSS, react-hook-form, and zod."
      },
      {
        "title": "Core Concepts",
        "body": "shadcn/ui is not a component library — it's a collection of copy-paste components built on Radix UI primitives. You own the code. Components are added to your project, not installed as dependencies."
      },
      {
        "title": "Installation",
        "body": "# Initialize shadcn/ui in a Next.js project\nnpx shadcn@latest init\n\n# Add individual components\nnpx shadcn@latest add button\nnpx shadcn@latest add card\nnpx shadcn@latest add dialog\nnpx shadcn@latest add form\nnpx shadcn@latest add input\nnpx shadcn@latest add select\nnpx shadcn@latest add table\nnpx shadcn@latest add toast\nnpx shadcn@latest add dropdown-menu\nnpx shadcn@latest add sheet\nnpx shadcn@latest add tabs\nnpx shadcn@latest add sidebar\n\n# Add multiple at once\nnpx shadcn@latest add button card input label textarea select checkbox"
      },
      {
        "title": "Layout & Navigation",
        "body": "ComponentUse WhensidebarApp-level navigation with collapsible sectionsnavigation-menuTop-level site navigation with dropdownsbreadcrumbShowing page hierarchy/locationtabsSwitching between related views in same contextseparatorVisual divider between content sectionssheetSlide-out panel (mobile nav, filters, detail views)resizableAdjustable panel layouts"
      },
      {
        "title": "Forms & Input",
        "body": "ComponentUse WhenformAny form with validation (wraps react-hook-form)inputText, email, password, number inputstextareaMulti-line text inputselectChoosing from a list (native-like)comboboxSearchable select (uses command + popover)checkboxBoolean or multi-select togglesradio-groupSingle selection from small setswitchOn/off toggle (settings, preferences)sliderNumeric range selectiondate-pickerDate selection (uses calendar + popover)togglePressed/unpressed state (toolbar buttons)"
      },
      {
        "title": "Feedback & Overlay",
        "body": "ComponentUse WhendialogModal confirmation, forms, or detail viewsalert-dialogDestructive action confirmation (\"Are you sure?\")sheetSide panel for forms, filters, mobile navtoastBrief non-blocking notifications (via sonner)alertInline status messages (info, warning, error)tooltipHover hints for icons/buttonspopoverRich content on click (color pickers, date pickers)hover-cardPreview content on hover (user profiles, links)skeletonLoading placeholdersprogressTask completion indicators"
      },
      {
        "title": "Data Display",
        "body": "ComponentUse WhentableTabular data displaydata-tableTables with sorting, filtering, pagination (uses @tanstack/react-table)cardContent containers with header, body, footerbadgeStatus labels, tags, countsavatarUser profile imagesaccordionCollapsible FAQ or settings sectionscarouselImage/content slideshowsscroll-areaCustom scrollable containers"
      },
      {
        "title": "Actions",
        "body": "ComponentUse WhenbuttonPrimary actions, form submissionsdropdown-menuContext menus, action menuscontext-menuRight-click menusmenubarApplication menu barscommandCommand palette / search (⌘K)"
      },
      {
        "title": "Complete Form Example",
        "body": "npx shadcn@latest add form input select textarea checkbox button\n\n'use client'\n\nimport { zodResolver } from '@hookform/resolvers/zod'\nimport { useForm } from 'react-hook-form'\nimport { z } from 'zod'\nimport { Button } from '@/components/ui/button'\nimport {\n  Form,\n  FormControl,\n  FormDescription,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from '@/components/ui/form'\nimport { Input } from '@/components/ui/input'\nimport { Textarea } from '@/components/ui/textarea'\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select'\nimport { Checkbox } from '@/components/ui/checkbox'\nimport { toast } from 'sonner'\n\nconst formSchema = z.object({\n  name: z.string().min(2, 'Name must be at least 2 characters'),\n  email: z.string().email('Invalid email address'),\n  role: z.enum(['admin', 'user', 'editor'], { required_error: 'Select a role' }),\n  bio: z.string().max(500).optional(),\n  notifications: z.boolean().default(false),\n})\n\ntype FormValues = z.infer<typeof formSchema>\n\nexport function UserForm() {\n  const form = useForm<FormValues>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      name: '',\n      email: '',\n      bio: '',\n      notifications: false,\n    },\n  })\n\n  async function onSubmit(values: FormValues) {\n    try {\n      await createUser(values)\n      toast.success('User created successfully')\n      form.reset()\n    } catch (error) {\n      toast.error('Failed to create user')\n    }\n  }\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n        <FormField\n          control={form.control}\n          name=\"name\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Name</FormLabel>\n              <FormControl>\n                <Input placeholder=\"John Doe\" {...field} />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"email\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Email</FormLabel>\n              <FormControl>\n                <Input type=\"email\" placeholder=\"john@example.com\" {...field} />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"role\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Role</FormLabel>\n              <Select onValueChange={field.onChange} defaultValue={field.value}>\n                <FormControl>\n                  <SelectTrigger>\n                    <SelectValue placeholder=\"Select a role\" />\n                  </SelectTrigger>\n                </FormControl>\n                <SelectContent>\n                  <SelectItem value=\"admin\">Admin</SelectItem>\n                  <SelectItem value=\"editor\">Editor</SelectItem>\n                  <SelectItem value=\"user\">User</SelectItem>\n                </SelectContent>\n              </Select>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"bio\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Bio</FormLabel>\n              <FormControl>\n                <Textarea placeholder=\"Tell us about yourself...\" {...field} />\n              </FormControl>\n              <FormDescription>Max 500 characters</FormDescription>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"notifications\"\n          render={({ field }) => (\n            <FormItem className=\"flex flex-row items-start space-x-3 space-y-0\">\n              <FormControl>\n                <Checkbox checked={field.value} onCheckedChange={field.onChange} />\n              </FormControl>\n              <div className=\"space-y-1 leading-none\">\n                <FormLabel>Email notifications</FormLabel>\n                <FormDescription>Receive emails about account activity</FormDescription>\n              </div>\n            </FormItem>\n          )}\n        />\n\n        <Button type=\"submit\" disabled={form.formState.isSubmitting}>\n          {form.formState.isSubmitting ? 'Creating...' : 'Create User'}\n        </Button>\n      </form>\n    </Form>\n  )\n}"
      },
      {
        "title": "Form with Server Action",
        "body": "'use client'\n\nimport { useFormState } from 'react-dom'\nimport { useForm } from 'react-hook-form'\nimport { zodResolver } from '@hookform/resolvers/zod'\n\nexport function ContactForm() {\n  const form = useForm<FormValues>({\n    resolver: zodResolver(schema),\n  })\n\n  async function onSubmit(values: FormValues) {\n    const formData = new FormData()\n    Object.entries(values).forEach(([key, value]) => formData.append(key, String(value)))\n    await submitContact(formData)\n  }\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n        {/* fields */}\n      </form>\n    </Form>\n  )\n}"
      },
      {
        "title": "Setup with next-themes",
        "body": "npm install next-themes\nnpx shadcn@latest add dropdown-menu\n\n// app/providers.tsx\n'use client'\nimport { ThemeProvider } from 'next-themes'\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  return (\n    <ThemeProvider attribute=\"class\" defaultTheme=\"system\" enableSystem disableTransitionOnChange>\n      {children}\n    </ThemeProvider>\n  )\n}\n\n// components/theme-toggle.tsx\n'use client'\nimport { Moon, Sun } from 'lucide-react'\nimport { useTheme } from 'next-themes'\nimport { Button } from '@/components/ui/button'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu'\n\nexport function ThemeToggle() {\n  const { setTheme } = useTheme()\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger asChild>\n        <Button variant=\"outline\" size=\"icon\">\n          <Sun className=\"h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0\" />\n          <Moon className=\"absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100\" />\n          <span className=\"sr-only\">Toggle theme</span>\n        </Button>\n      </DropdownMenuTrigger>\n      <DropdownMenuContent align=\"end\">\n        <DropdownMenuItem onClick={() => setTheme('light')}>Light</DropdownMenuItem>\n        <DropdownMenuItem onClick={() => setTheme('dark')}>Dark</DropdownMenuItem>\n        <DropdownMenuItem onClick={() => setTheme('system')}>System</DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}"
      },
      {
        "title": "Custom Colors in globals.css",
        "body": "@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n    --border: 214.3 31.8% 91.4%;\n    --ring: 222.2 84% 4.9%;\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 11.2%;\n    /* ... etc */\n  }\n}"
      },
      {
        "title": "App Shell with Sidebar",
        "body": "import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'\nimport { AppSidebar } from '@/components/app-sidebar'\n\nexport default function DashboardLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <SidebarProvider>\n      <AppSidebar />\n      <main className=\"flex-1\">\n        <header className=\"flex h-14 items-center gap-4 border-b px-6\">\n          <SidebarTrigger />\n          <h1 className=\"text-lg font-semibold\">Dashboard</h1>\n        </header>\n        <div className=\"p-6\">{children}</div>\n      </main>\n    </SidebarProvider>\n  )\n}"
      },
      {
        "title": "Responsive Header with Mobile Nav",
        "body": "import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'\nimport { Button } from '@/components/ui/button'\nimport { Menu } from 'lucide-react'\n\nexport function Header() {\n  return (\n    <header className=\"sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur\">\n      <div className=\"container flex h-14 items-center\">\n        <div className=\"mr-4 hidden md:flex\">\n          <Logo />\n          <nav className=\"flex items-center gap-6 text-sm ml-6\">\n            <Link href=\"/dashboard\">Dashboard</Link>\n            <Link href=\"/settings\">Settings</Link>\n          </nav>\n        </div>\n\n        {/* Mobile hamburger */}\n        <Sheet>\n          <SheetTrigger asChild>\n            <Button variant=\"outline\" size=\"icon\" className=\"md:hidden\">\n              <Menu className=\"h-5 w-5\" />\n            </Button>\n          </SheetTrigger>\n          <SheetContent side=\"left\" className=\"w-[300px]\">\n            <nav className=\"flex flex-col gap-4 mt-8\">\n              <Link href=\"/dashboard\">Dashboard</Link>\n              <Link href=\"/settings\">Settings</Link>\n            </nav>\n          </SheetContent>\n        </Sheet>\n\n        <div className=\"flex flex-1 items-center justify-end gap-2\">\n          <ThemeToggle />\n          <UserMenu />\n        </div>\n      </div>\n    </header>\n  )\n}"
      },
      {
        "title": "Card Grid",
        "body": "import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\n\nexport function StatsGrid({ stats }: { stats: Stat[] }) {\n  return (\n    <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n      {stats.map((stat) => (\n        <Card key={stat.label}>\n          <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n            <CardTitle className=\"text-sm font-medium\">{stat.label}</CardTitle>\n            <stat.icon className=\"h-4 w-4 text-muted-foreground\" />\n          </CardHeader>\n          <CardContent>\n            <div className=\"text-2xl font-bold\">{stat.value}</div>\n            <p className=\"text-xs text-muted-foreground\">{stat.description}</p>\n          </CardContent>\n        </Card>\n      ))}\n    </div>\n  )\n}"
      },
      {
        "title": "Common Utility Patterns",
        "body": "// Centering\n<div className=\"flex items-center justify-center min-h-screen\">\n\n// Container with max-width\n<div className=\"container mx-auto px-4\">\n\n// Responsive grid\n<div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n\n// Sticky header\n<header className=\"sticky top-0 z-50 border-b bg-background/95 backdrop-blur\">\n\n// Truncated text\n<p className=\"truncate\">Very long text...</p>\n\n// Line clamp\n<p className=\"line-clamp-3\">Multi-line truncation...</p>\n\n// Aspect ratio\n<div className=\"aspect-video rounded-lg overflow-hidden\">\n\n// Animations\n<div className=\"animate-pulse\">    {/* Loading skeleton */}\n<div className=\"animate-spin\">     {/* Spinner */}\n<div className=\"transition-all duration-200 hover:scale-105\">"
      },
      {
        "title": "Button Variants",
        "body": "<Button>Default</Button>\n<Button variant=\"secondary\">Secondary</Button>\n<Button variant=\"outline\">Outline</Button>\n<Button variant=\"ghost\">Ghost</Button>\n<Button variant=\"link\">Link</Button>\n<Button variant=\"destructive\">Delete</Button>\n<Button size=\"sm\">Small</Button>\n<Button size=\"lg\">Large</Button>\n<Button size=\"icon\"><Plus className=\"h-4 w-4\" /></Button>\n<Button disabled>Disabled</Button>\n<Button asChild><Link href=\"/page\">As Link</Link></Button>"
      },
      {
        "title": "Toast Notifications",
        "body": "npx shadcn@latest add sonner\n\n// app/layout.tsx\nimport { Toaster } from '@/components/ui/sonner'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html><body>{children}<Toaster /></body></html>\n  )\n}\n\n// Usage anywhere\nimport { toast } from 'sonner'\n\ntoast.success('User created')\ntoast.error('Something went wrong')\ntoast.info('New update available')\ntoast.warning('This action cannot be undone')\ntoast.promise(asyncAction(), {\n  loading: 'Creating...',\n  success: 'Created!',\n  error: 'Failed to create',\n})"
      },
      {
        "title": "Command Palette (⌘K)",
        "body": "'use client'\nimport { useEffect, useState } from 'react'\nimport { useRouter } from 'next/navigation'\nimport {\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from '@/components/ui/command'\n\nexport function CommandPalette() {\n  const [open, setOpen] = useState(false)\n  const router = useRouter()\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault()\n        setOpen((open) => !open)\n      }\n    }\n    document.addEventListener('keydown', down)\n    return () => document.removeEventListener('keydown', down)\n  }, [])\n\n  return (\n    <CommandDialog open={open} onOpenChange={setOpen}>\n      <CommandInput placeholder=\"Type a command or search...\" />\n      <CommandList>\n        <CommandEmpty>No results found.</CommandEmpty>\n        <CommandGroup heading=\"Navigation\">\n          <CommandItem onSelect={() => { router.push('/dashboard'); setOpen(false) }}>\n            Dashboard\n          </CommandItem>\n          <CommandItem onSelect={() => { router.push('/settings'); setOpen(false) }}>\n            Settings\n          </CommandItem>\n        </CommandGroup>\n      </CommandList>\n    </CommandDialog>\n  )\n}"
      }
    ],
    "body": "shadcn/ui Expert\n\nComprehensive guide for building production UIs with shadcn/ui, Tailwind CSS, react-hook-form, and zod.\n\nCore Concepts\n\nshadcn/ui is not a component library — it's a collection of copy-paste components built on Radix UI primitives. You own the code. Components are added to your project, not installed as dependencies.\n\nInstallation\n# Initialize shadcn/ui in a Next.js project\nnpx shadcn@latest init\n\n# Add individual components\nnpx shadcn@latest add button\nnpx shadcn@latest add card\nnpx shadcn@latest add dialog\nnpx shadcn@latest add form\nnpx shadcn@latest add input\nnpx shadcn@latest add select\nnpx shadcn@latest add table\nnpx shadcn@latest add toast\nnpx shadcn@latest add dropdown-menu\nnpx shadcn@latest add sheet\nnpx shadcn@latest add tabs\nnpx shadcn@latest add sidebar\n\n# Add multiple at once\nnpx shadcn@latest add button card input label textarea select checkbox\n\nComponent Categories & When to Use\nLayout & Navigation\nComponent\tUse When\nsidebar\tApp-level navigation with collapsible sections\nnavigation-menu\tTop-level site navigation with dropdowns\nbreadcrumb\tShowing page hierarchy/location\ntabs\tSwitching between related views in same context\nseparator\tVisual divider between content sections\nsheet\tSlide-out panel (mobile nav, filters, detail views)\nresizable\tAdjustable panel layouts\nForms & Input\nComponent\tUse When\nform\tAny form with validation (wraps react-hook-form)\ninput\tText, email, password, number inputs\ntextarea\tMulti-line text input\nselect\tChoosing from a list (native-like)\ncombobox\tSearchable select (uses command + popover)\ncheckbox\tBoolean or multi-select toggles\nradio-group\tSingle selection from small set\nswitch\tOn/off toggle (settings, preferences)\nslider\tNumeric range selection\ndate-picker\tDate selection (uses calendar + popover)\ntoggle\tPressed/unpressed state (toolbar buttons)\nFeedback & Overlay\nComponent\tUse When\ndialog\tModal confirmation, forms, or detail views\nalert-dialog\tDestructive action confirmation (\"Are you sure?\")\nsheet\tSide panel for forms, filters, mobile nav\ntoast\tBrief non-blocking notifications (via sonner)\nalert\tInline status messages (info, warning, error)\ntooltip\tHover hints for icons/buttons\npopover\tRich content on click (color pickers, date pickers)\nhover-card\tPreview content on hover (user profiles, links)\nskeleton\tLoading placeholders\nprogress\tTask completion indicators\nData Display\nComponent\tUse When\ntable\tTabular data display\ndata-table\tTables with sorting, filtering, pagination (uses @tanstack/react-table)\ncard\tContent containers with header, body, footer\nbadge\tStatus labels, tags, counts\navatar\tUser profile images\naccordion\tCollapsible FAQ or settings sections\ncarousel\tImage/content slideshows\nscroll-area\tCustom scrollable containers\nActions\nComponent\tUse When\nbutton\tPrimary actions, form submissions\ndropdown-menu\tContext menus, action menus\ncontext-menu\tRight-click menus\nmenubar\tApplication menu bars\ncommand\tCommand palette / search (⌘K)\nForm Patterns (react-hook-form + zod)\nComplete Form Example\nnpx shadcn@latest add form input select textarea checkbox button\n\n'use client'\n\nimport { zodResolver } from '@hookform/resolvers/zod'\nimport { useForm } from 'react-hook-form'\nimport { z } from 'zod'\nimport { Button } from '@/components/ui/button'\nimport {\n  Form,\n  FormControl,\n  FormDescription,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from '@/components/ui/form'\nimport { Input } from '@/components/ui/input'\nimport { Textarea } from '@/components/ui/textarea'\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from '@/components/ui/select'\nimport { Checkbox } from '@/components/ui/checkbox'\nimport { toast } from 'sonner'\n\nconst formSchema = z.object({\n  name: z.string().min(2, 'Name must be at least 2 characters'),\n  email: z.string().email('Invalid email address'),\n  role: z.enum(['admin', 'user', 'editor'], { required_error: 'Select a role' }),\n  bio: z.string().max(500).optional(),\n  notifications: z.boolean().default(false),\n})\n\ntype FormValues = z.infer<typeof formSchema>\n\nexport function UserForm() {\n  const form = useForm<FormValues>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      name: '',\n      email: '',\n      bio: '',\n      notifications: false,\n    },\n  })\n\n  async function onSubmit(values: FormValues) {\n    try {\n      await createUser(values)\n      toast.success('User created successfully')\n      form.reset()\n    } catch (error) {\n      toast.error('Failed to create user')\n    }\n  }\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n        <FormField\n          control={form.control}\n          name=\"name\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Name</FormLabel>\n              <FormControl>\n                <Input placeholder=\"John Doe\" {...field} />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"email\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Email</FormLabel>\n              <FormControl>\n                <Input type=\"email\" placeholder=\"john@example.com\" {...field} />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"role\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Role</FormLabel>\n              <Select onValueChange={field.onChange} defaultValue={field.value}>\n                <FormControl>\n                  <SelectTrigger>\n                    <SelectValue placeholder=\"Select a role\" />\n                  </SelectTrigger>\n                </FormControl>\n                <SelectContent>\n                  <SelectItem value=\"admin\">Admin</SelectItem>\n                  <SelectItem value=\"editor\">Editor</SelectItem>\n                  <SelectItem value=\"user\">User</SelectItem>\n                </SelectContent>\n              </Select>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"bio\"\n          render={({ field }) => (\n            <FormItem>\n              <FormLabel>Bio</FormLabel>\n              <FormControl>\n                <Textarea placeholder=\"Tell us about yourself...\" {...field} />\n              </FormControl>\n              <FormDescription>Max 500 characters</FormDescription>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n\n        <FormField\n          control={form.control}\n          name=\"notifications\"\n          render={({ field }) => (\n            <FormItem className=\"flex flex-row items-start space-x-3 space-y-0\">\n              <FormControl>\n                <Checkbox checked={field.value} onCheckedChange={field.onChange} />\n              </FormControl>\n              <div className=\"space-y-1 leading-none\">\n                <FormLabel>Email notifications</FormLabel>\n                <FormDescription>Receive emails about account activity</FormDescription>\n              </div>\n            </FormItem>\n          )}\n        />\n\n        <Button type=\"submit\" disabled={form.formState.isSubmitting}>\n          {form.formState.isSubmitting ? 'Creating...' : 'Create User'}\n        </Button>\n      </form>\n    </Form>\n  )\n}\n\nForm with Server Action\n'use client'\n\nimport { useFormState } from 'react-dom'\nimport { useForm } from 'react-hook-form'\nimport { zodResolver } from '@hookform/resolvers/zod'\n\nexport function ContactForm() {\n  const form = useForm<FormValues>({\n    resolver: zodResolver(schema),\n  })\n\n  async function onSubmit(values: FormValues) {\n    const formData = new FormData()\n    Object.entries(values).forEach(([key, value]) => formData.append(key, String(value)))\n    await submitContact(formData)\n  }\n\n  return (\n    <Form {...form}>\n      <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n        {/* fields */}\n      </form>\n    </Form>\n  )\n}\n\nTheming & Dark Mode\nSetup with next-themes\nnpm install next-themes\nnpx shadcn@latest add dropdown-menu\n\n// app/providers.tsx\n'use client'\nimport { ThemeProvider } from 'next-themes'\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  return (\n    <ThemeProvider attribute=\"class\" defaultTheme=\"system\" enableSystem disableTransitionOnChange>\n      {children}\n    </ThemeProvider>\n  )\n}\n\n// components/theme-toggle.tsx\n'use client'\nimport { Moon, Sun } from 'lucide-react'\nimport { useTheme } from 'next-themes'\nimport { Button } from '@/components/ui/button'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu'\n\nexport function ThemeToggle() {\n  const { setTheme } = useTheme()\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger asChild>\n        <Button variant=\"outline\" size=\"icon\">\n          <Sun className=\"h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0\" />\n          <Moon className=\"absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100\" />\n          <span className=\"sr-only\">Toggle theme</span>\n        </Button>\n      </DropdownMenuTrigger>\n      <DropdownMenuContent align=\"end\">\n        <DropdownMenuItem onClick={() => setTheme('light')}>Light</DropdownMenuItem>\n        <DropdownMenuItem onClick={() => setTheme('dark')}>Dark</DropdownMenuItem>\n        <DropdownMenuItem onClick={() => setTheme('system')}>System</DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n\nCustom Colors in globals.css\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n    --border: 214.3 31.8% 91.4%;\n    --ring: 222.2 84% 4.9%;\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 11.2%;\n    /* ... etc */\n  }\n}\n\nCommon Layouts\nApp Shell with Sidebar\nimport { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'\nimport { AppSidebar } from '@/components/app-sidebar'\n\nexport default function DashboardLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <SidebarProvider>\n      <AppSidebar />\n      <main className=\"flex-1\">\n        <header className=\"flex h-14 items-center gap-4 border-b px-6\">\n          <SidebarTrigger />\n          <h1 className=\"text-lg font-semibold\">Dashboard</h1>\n        </header>\n        <div className=\"p-6\">{children}</div>\n      </main>\n    </SidebarProvider>\n  )\n}\n\nResponsive Header with Mobile Nav\nimport { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'\nimport { Button } from '@/components/ui/button'\nimport { Menu } from 'lucide-react'\n\nexport function Header() {\n  return (\n    <header className=\"sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur\">\n      <div className=\"container flex h-14 items-center\">\n        <div className=\"mr-4 hidden md:flex\">\n          <Logo />\n          <nav className=\"flex items-center gap-6 text-sm ml-6\">\n            <Link href=\"/dashboard\">Dashboard</Link>\n            <Link href=\"/settings\">Settings</Link>\n          </nav>\n        </div>\n\n        {/* Mobile hamburger */}\n        <Sheet>\n          <SheetTrigger asChild>\n            <Button variant=\"outline\" size=\"icon\" className=\"md:hidden\">\n              <Menu className=\"h-5 w-5\" />\n            </Button>\n          </SheetTrigger>\n          <SheetContent side=\"left\" className=\"w-[300px]\">\n            <nav className=\"flex flex-col gap-4 mt-8\">\n              <Link href=\"/dashboard\">Dashboard</Link>\n              <Link href=\"/settings\">Settings</Link>\n            </nav>\n          </SheetContent>\n        </Sheet>\n\n        <div className=\"flex flex-1 items-center justify-end gap-2\">\n          <ThemeToggle />\n          <UserMenu />\n        </div>\n      </div>\n    </header>\n  )\n}\n\nCard Grid\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\n\nexport function StatsGrid({ stats }: { stats: Stat[] }) {\n  return (\n    <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n      {stats.map((stat) => (\n        <Card key={stat.label}>\n          <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n            <CardTitle className=\"text-sm font-medium\">{stat.label}</CardTitle>\n            <stat.icon className=\"h-4 w-4 text-muted-foreground\" />\n          </CardHeader>\n          <CardContent>\n            <div className=\"text-2xl font-bold\">{stat.value}</div>\n            <p className=\"text-xs text-muted-foreground\">{stat.description}</p>\n          </CardContent>\n        </Card>\n      ))}\n    </div>\n  )\n}\n\nTailwind CSS Patterns\nCommon Utility Patterns\n// Centering\n<div className=\"flex items-center justify-center min-h-screen\">\n\n// Container with max-width\n<div className=\"container mx-auto px-4\">\n\n// Responsive grid\n<div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n\n// Sticky header\n<header className=\"sticky top-0 z-50 border-b bg-background/95 backdrop-blur\">\n\n// Truncated text\n<p className=\"truncate\">Very long text...</p>\n\n// Line clamp\n<p className=\"line-clamp-3\">Multi-line truncation...</p>\n\n// Aspect ratio\n<div className=\"aspect-video rounded-lg overflow-hidden\">\n\n// Animations\n<div className=\"animate-pulse\">    {/* Loading skeleton */}\n<div className=\"animate-spin\">     {/* Spinner */}\n<div className=\"transition-all duration-200 hover:scale-105\">\n\nButton Variants\n<Button>Default</Button>\n<Button variant=\"secondary\">Secondary</Button>\n<Button variant=\"outline\">Outline</Button>\n<Button variant=\"ghost\">Ghost</Button>\n<Button variant=\"link\">Link</Button>\n<Button variant=\"destructive\">Delete</Button>\n<Button size=\"sm\">Small</Button>\n<Button size=\"lg\">Large</Button>\n<Button size=\"icon\"><Plus className=\"h-4 w-4\" /></Button>\n<Button disabled>Disabled</Button>\n<Button asChild><Link href=\"/page\">As Link</Link></Button>\n\nToast Notifications\nnpx shadcn@latest add sonner\n\n// app/layout.tsx\nimport { Toaster } from '@/components/ui/sonner'\n\nexport default function RootLayout({ children }) {\n  return (\n    <html><body>{children}<Toaster /></body></html>\n  )\n}\n\n// Usage anywhere\nimport { toast } from 'sonner'\n\ntoast.success('User created')\ntoast.error('Something went wrong')\ntoast.info('New update available')\ntoast.warning('This action cannot be undone')\ntoast.promise(asyncAction(), {\n  loading: 'Creating...',\n  success: 'Created!',\n  error: 'Failed to create',\n})\n\nCommand Palette (⌘K)\n'use client'\nimport { useEffect, useState } from 'react'\nimport { useRouter } from 'next/navigation'\nimport {\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from '@/components/ui/command'\n\nexport function CommandPalette() {\n  const [open, setOpen] = useState(false)\n  const router = useRouter()\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault()\n        setOpen((open) => !open)\n      }\n    }\n    document.addEventListener('keydown', down)\n    return () => document.removeEventListener('keydown', down)\n  }, [])\n\n  return (\n    <CommandDialog open={open} onOpenChange={setOpen}>\n      <CommandInput placeholder=\"Type a command or search...\" />\n      <CommandList>\n        <CommandEmpty>No results found.</CommandEmpty>\n        <CommandGroup heading=\"Navigation\">\n          <CommandItem onSelect={() => { router.push('/dashboard'); setOpen(false) }}>\n            Dashboard\n          </CommandItem>\n          <CommandItem onSelect={() => { router.push('/settings'); setOpen(false) }}>\n            Settings\n          </CommandItem>\n        </CommandGroup>\n      </CommandList>\n    </CommandDialog>\n  )\n}"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/jgarrison929/shadcn-ui",
    "publisherUrl": "https://clawhub.ai/jgarrison929/shadcn-ui",
    "owner": "jgarrison929",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/shadcn-ui",
    "downloadUrl": "https://openagent3.xyz/downloads/shadcn-ui",
    "agentUrl": "https://openagent3.xyz/skills/shadcn-ui/agent",
    "manifestUrl": "https://openagent3.xyz/skills/shadcn-ui/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/shadcn-ui/agent.md"
  }
}