← 返回 Skills 市场
shenghoo123-png

Shadcn Ui

作者 shenghoo123-png · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ 安全检测通过
92
总下载
0
收藏
0
当前安装
1
版本数
在 OpenClaw 中安装
/install shadcn-ui-pro
功能描述
Use when building UI with shadcn/ui components, Tailwind CSS layouts, form patterns with react-hook-form and zod, theming, dark mode, sidebar layouts, mobile...
使用说明 (SKILL.md)

shadcn/ui Expert

Comprehensive guide for building production UIs with shadcn/ui, Tailwind CSS, react-hook-form, and zod.

Core Concepts

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.

Installation

# Initialize shadcn/ui in a Next.js project
npx shadcn@latest init

# Add individual components
npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add dialog
npx shadcn@latest add form
npx shadcn@latest add input
npx shadcn@latest add select
npx shadcn@latest add table
npx shadcn@latest add toast
npx shadcn@latest add dropdown-menu
npx shadcn@latest add sheet
npx shadcn@latest add tabs
npx shadcn@latest add sidebar

# Add multiple at once
npx shadcn@latest add button card input label textarea select checkbox

Component Categories & When to Use

Layout & Navigation

Component Use When
sidebar App-level navigation with collapsible sections
navigation-menu Top-level site navigation with dropdowns
breadcrumb Showing page hierarchy/location
tabs Switching between related views in same context
separator Visual divider between content sections
sheet Slide-out panel (mobile nav, filters, detail views)
resizable Adjustable panel layouts

Forms & Input

Component Use When
form Any form with validation (wraps react-hook-form)
input Text, email, password, number inputs
textarea Multi-line text input
select Choosing from a list (native-like)
combobox Searchable select (uses command + popover)
checkbox Boolean or multi-select toggles
radio-group Single selection from small set
switch On/off toggle (settings, preferences)
slider Numeric range selection
date-picker Date selection (uses calendar + popover)
toggle Pressed/unpressed state (toolbar buttons)

Feedback & Overlay

Component Use When
dialog Modal confirmation, forms, or detail views
alert-dialog Destructive action confirmation ("Are you sure?")
sheet Side panel for forms, filters, mobile nav
toast Brief non-blocking notifications (via sonner)
alert Inline status messages (info, warning, error)
tooltip Hover hints for icons/buttons
popover Rich content on click (color pickers, date pickers)
hover-card Preview content on hover (user profiles, links)
skeleton Loading placeholders
progress Task completion indicators

Data Display

Component Use When
table Tabular data display
data-table Tables with sorting, filtering, pagination (uses @tanstack/react-table)
card Content containers with header, body, footer
badge Status labels, tags, counts
avatar User profile images
accordion Collapsible FAQ or settings sections
carousel Image/content slideshows
scroll-area Custom scrollable containers

Actions

Component Use When
button Primary actions, form submissions
dropdown-menu Context menus, action menus
context-menu Right-click menus
menubar Application menu bars
command Command palette / search (⌘K)

Form Patterns (react-hook-form + zod)

Complete Form Example

npx shadcn@latest add form input select textarea checkbox button
'use client'

import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { Button } from '@/components/ui/button'
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'
import { toast } from 'sonner'

const formSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email address'),
  role: z.enum(['admin', 'user', 'editor'], { required_error: 'Select a role' }),
  bio: z.string().max(500).optional(),
  notifications: z.boolean().default(false),
})

type FormValues = z.infer\x3Ctypeof formSchema>

export function UserForm() {
  const form = useForm\x3CFormValues>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      name: '',
      email: '',
      bio: '',
      notifications: false,
    },
  })

  async function onSubmit(values: FormValues) {
    try {
      await createUser(values)
      toast.success('User created successfully')
      form.reset()
    } catch (error) {
      toast.error('Failed to create user')
    }
  }

  return (
    \x3CForm {...form}>
      \x3Cform onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
        \x3CFormField
          control={form.control}
          name="name"
          render={({ field }) => (
            \x3CFormItem>
              \x3CFormLabel>Name\x3C/FormLabel>
              \x3CFormControl>
                \x3CInput placeholder="John Doe" {...field} />
              \x3C/FormControl>
              \x3CFormMessage />
            \x3C/FormItem>
          )}
        />

        \x3CFormField
          control={form.control}
          name="email"
          render={({ field }) => (
            \x3CFormItem>
              \x3CFormLabel>Email\x3C/FormLabel>
              \x3CFormControl>
                \x3CInput type="email" placeholder="[email protected]" {...field} />
              \x3C/FormControl>
              \x3CFormMessage />
            \x3C/FormItem>
          )}
        />

        \x3CFormField
          control={form.control}
          name="role"
          render={({ field }) => (
            \x3CFormItem>
              \x3CFormLabel>Role\x3C/FormLabel>
              \x3CSelect onValueChange={field.onChange} defaultValue={field.value}>
                \x3CFormControl>
                  \x3CSelectTrigger>
                    \x3CSelectValue placeholder="Select a role" />
                  \x3C/SelectTrigger>
                \x3C/FormControl>
                \x3CSelectContent>
                  \x3CSelectItem value="admin">Admin\x3C/SelectItem>
                  \x3CSelectItem value="editor">Editor\x3C/SelectItem>
                  \x3CSelectItem value="user">User\x3C/SelectItem>
                \x3C/SelectContent>
              \x3C/Select>
              \x3CFormMessage />
            \x3C/FormItem>
          )}
        />

        \x3CFormField
          control={form.control}
          name="bio"
          render={({ field }) => (
            \x3CFormItem>
              \x3CFormLabel>Bio\x3C/FormLabel>
              \x3CFormControl>
                \x3CTextarea placeholder="Tell us about yourself..." {...field} />
              \x3C/FormControl>
              \x3CFormDescription>Max 500 characters\x3C/FormDescription>
              \x3CFormMessage />
            \x3C/FormItem>
          )}
        />

        \x3CFormField
          control={form.control}
          name="notifications"
          render={({ field }) => (
            \x3CFormItem className="flex flex-row items-start space-x-3 space-y-0">
              \x3CFormControl>
                \x3CCheckbox checked={field.value} onCheckedChange={field.onChange} />
              \x3C/FormControl>
              \x3Cdiv className="space-y-1 leading-none">
                \x3CFormLabel>Email notifications\x3C/FormLabel>
                \x3CFormDescription>Receive emails about account activity\x3C/FormDescription>
              \x3C/div>
            \x3C/FormItem>
          )}
        />

        \x3CButton type="submit" disabled={form.formState.isSubmitting}>
          {form.formState.isSubmitting ? 'Creating...' : 'Create User'}
        \x3C/Button>
      \x3C/form>
    \x3C/Form>
  )
}

Form with Server Action

'use client'

import { useFormState } from 'react-dom'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'

export function ContactForm() {
  const form = useForm\x3CFormValues>({
    resolver: zodResolver(schema),
  })

  async function onSubmit(values: FormValues) {
    const formData = new FormData()
    Object.entries(values).forEach(([key, value]) => formData.append(key, String(value)))
    await submitContact(formData)
  }

  return (
    \x3CForm {...form}>
      \x3Cform onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        {/* fields */}
      \x3C/form>
    \x3C/Form>
  )
}

Theming & Dark Mode

Setup with next-themes

npm install next-themes
npx shadcn@latest add dropdown-menu
// app/providers.tsx
'use client'
import { ThemeProvider } from 'next-themes'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    \x3CThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
      {children}
    \x3C/ThemeProvider>
  )
}
// components/theme-toggle.tsx
'use client'
import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'
import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'

export function ThemeToggle() {
  const { setTheme } = useTheme()
  return (
    \x3CDropdownMenu>
      \x3CDropdownMenuTrigger asChild>
        \x3CButton variant="outline" size="icon">
          \x3CSun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          \x3CMoon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          \x3Cspan className="sr-only">Toggle theme\x3C/span>
        \x3C/Button>
      \x3C/DropdownMenuTrigger>
      \x3CDropdownMenuContent align="end">
        \x3CDropdownMenuItem onClick={() => setTheme('light')}>Light\x3C/DropdownMenuItem>
        \x3CDropdownMenuItem onClick={() => setTheme('dark')}>Dark\x3C/DropdownMenuItem>
        \x3CDropdownMenuItem onClick={() => setTheme('system')}>System\x3C/DropdownMenuItem>
      \x3C/DropdownMenuContent>
    \x3C/DropdownMenu>
  )
}

Custom Colors in globals.css

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96.1%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;
    --border: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    /* ... etc */
  }
}

Common Layouts

App Shell with Sidebar

import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
import { AppSidebar } from '@/components/app-sidebar'

export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    \x3CSidebarProvider>
      \x3CAppSidebar />
      \x3Cmain className="flex-1">
        \x3Cheader className="flex h-14 items-center gap-4 border-b px-6">
          \x3CSidebarTrigger />
          \x3Ch1 className="text-lg font-semibold">Dashboard\x3C/h1>
        \x3C/header>
        \x3Cdiv className="p-6">{children}\x3C/div>
      \x3C/main>
    \x3C/SidebarProvider>
  )
}

Responsive Header with Mobile Nav

import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
import { Button } from '@/components/ui/button'
import { Menu } from 'lucide-react'

export function Header() {
  return (
    \x3Cheader className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur">
      \x3Cdiv className="container flex h-14 items-center">
        \x3Cdiv className="mr-4 hidden md:flex">
          \x3CLogo />
          \x3Cnav className="flex items-center gap-6 text-sm ml-6">
            \x3CLink href="/dashboard">Dashboard\x3C/Link>
            \x3CLink href="/settings">Settings\x3C/Link>
          \x3C/nav>
        \x3C/div>

        {/* Mobile hamburger */}
        \x3CSheet>
          \x3CSheetTrigger asChild>
            \x3CButton variant="outline" size="icon" className="md:hidden">
              \x3CMenu className="h-5 w-5" />
            \x3C/Button>
          \x3C/SheetTrigger>
          \x3CSheetContent side="left" className="w-[300px]">
            \x3Cnav className="flex flex-col gap-4 mt-8">
              \x3CLink href="/dashboard">Dashboard\x3C/Link>
              \x3CLink href="/settings">Settings\x3C/Link>
            \x3C/nav>
          \x3C/SheetContent>
        \x3C/Sheet>

        \x3Cdiv className="flex flex-1 items-center justify-end gap-2">
          \x3CThemeToggle />
          \x3CUserMenu />
        \x3C/div>
      \x3C/div>
    \x3C/header>
  )
}

Card Grid

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'

export function StatsGrid({ stats }: { stats: Stat[] }) {
  return (
    \x3Cdiv className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
      {stats.map((stat) => (
        \x3CCard key={stat.label}>
          \x3CCardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
            \x3CCardTitle className="text-sm font-medium">{stat.label}\x3C/CardTitle>
            \x3Cstat.icon className="h-4 w-4 text-muted-foreground" />
          \x3C/CardHeader>
          \x3CCardContent>
            \x3Cdiv className="text-2xl font-bold">{stat.value}\x3C/div>
            \x3Cp className="text-xs text-muted-foreground">{stat.description}\x3C/p>
          \x3C/CardContent>
        \x3C/Card>
      ))}
    \x3C/div>
  )
}

Tailwind CSS Patterns

Common Utility Patterns

// Centering
\x3Cdiv className="flex items-center justify-center min-h-screen">

// Container with max-width
\x3Cdiv className="container mx-auto px-4">

// Responsive grid
\x3Cdiv className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">

// Sticky header
\x3Cheader className="sticky top-0 z-50 border-b bg-background/95 backdrop-blur">

// Truncated text
\x3Cp className="truncate">Very long text...\x3C/p>

// Line clamp
\x3Cp className="line-clamp-3">Multi-line truncation...\x3C/p>

// Aspect ratio
\x3Cdiv className="aspect-video rounded-lg overflow-hidden">

// Animations
\x3Cdiv className="animate-pulse">    {/* Loading skeleton */}
\x3Cdiv className="animate-spin">     {/* Spinner */}
\x3Cdiv className="transition-all duration-200 hover:scale-105">

Button Variants

\x3CButton>Default\x3C/Button>
\x3CButton variant="secondary">Secondary\x3C/Button>
\x3CButton variant="outline">Outline\x3C/Button>
\x3CButton variant="ghost">Ghost\x3C/Button>
\x3CButton variant="link">Link\x3C/Button>
\x3CButton variant="destructive">Delete\x3C/Button>
\x3CButton size="sm">Small\x3C/Button>
\x3CButton size="lg">Large\x3C/Button>
\x3CButton size="icon">\x3CPlus className="h-4 w-4" />\x3C/Button>
\x3CButton disabled>Disabled\x3C/Button>
\x3CButton asChild>\x3CLink href="/page">As Link\x3C/Link>\x3C/Button>

Toast Notifications

npx shadcn@latest add sonner
// app/layout.tsx
import { Toaster } from '@/components/ui/sonner'

export default function RootLayout({ children }) {
  return (
    \x3Chtml>\x3Cbody>{children}\x3CToaster />\x3C/body>\x3C/html>
  )
}

// Usage anywhere
import { toast } from 'sonner'

toast.success('User created')
toast.error('Something went wrong')
toast.info('New update available')
toast.warning('This action cannot be undone')
toast.promise(asyncAction(), {
  loading: 'Creating...',
  success: 'Created!',
  error: 'Failed to create',
})

Command Palette (⌘K)

'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import {
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from '@/components/ui/command'

export function CommandPalette() {
  const [open, setOpen] = useState(false)
  const router = useRouter()

  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen((open) => !open)
      }
    }
    document.addEventListener('keydown', down)
    return () => document.removeEventListener('keydown', down)
  }, [])

  return (
    \x3CCommandDialog open={open} onOpenChange={setOpen}>
      \x3CCommandInput placeholder="Type a command or search..." />
      \x3CCommandList>
        \x3CCommandEmpty>No results found.\x3C/CommandEmpty>
        \x3CCommandGroup heading="Navigation">
          \x3CCommandItem onSelect={() => { router.push('/dashboard'); setOpen(false) }}>
            Dashboard
          \x3C/CommandItem>
          \x3CCommandItem onSelect={() => { router.push('/settings'); setOpen(false) }}>
            Settings
          \x3C/CommandItem>
        \x3C/CommandGroup>
      \x3C/CommandList>
    \x3C/CommandDialog>
  )
}
安全使用建议
This skill is a coherent, text-only guide for working with shadcn/ui. Before running any npx shadcn@latest commands yourself, consider pinning a specific version rather than @latest and inspect the package you fetch (or run in an isolated/dev environment). Also note the package's source/homepage is unspecified in the skill metadata — if you need a verifiable upstream, locate the official repo or npm package before installing.
能力评估
Purpose & Capability
Name/description match the content: the SKILL.md contains guidance for shadcn/ui, Tailwind, react-hook-form/zod, and shows CLI usage (npx shadcn...). There are no unexpected environment variables, binaries, or unrelated credentials declared.
Instruction Scope
Runtime instructions are focused on adding shadcn components, form patterns, and UI patterns. The only shell commands are npx shadcn@latest init/add, and examples are project-local code samples. The instructions do not ask the agent to read unrelated system files, exfiltrate data, or call unknown endpoints.
Install Mechanism
This is an instruction-only skill (no install spec or code files). It suggests using npx shadcn@latest, which will fetch a package from the npm ecosystem when executed by the user — this is expected for the stated purpose but the user should be aware running npx @latest pulls remote code at runtime.
Credentials
No environment variables, credentials, or config paths are requested. The SKILL.md does not reference hidden secrets or other services beyond typical project dependencies.
Persistence & Privilege
Skill is not forced-always, is user-invocable, and allows normal autonomous invocation by the agent (default). It does not request permanent system-wide presence or modify other skills' configs.
如何使用
  1. 确保已安装 OpenClaw(本地或 Docker 部署)
  2. 在对话框中输入安装命令:/install shadcn-ui-pro
  3. 安装完成后,直接呼叫该 Skill 的名称或使用 /shadcn-ui-pro 触发
  4. 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
版本历史
v1.0.0
shadcn-ui-pro 1.0.0 – Initial Release - Provides an expert skill for implementing UI with shadcn/ui components, Tailwind CSS, react-hook-form, and zod. - Includes a concise guide covering component categories, usage scenarios, and theming/dark mode support. - Lists installation commands and examples for adding shadcn/ui components to your project. - Documents core layout, form, overlay, and data display patterns with usage recommendations. - Supplies a complete, ready-to-use react-hook-form + zod form example integrating multiple shadcn/ui components.
元数据
Slug shadcn-ui-pro
版本 1.0.0
许可证 MIT-0
累计安装 0
当前安装数 0
历史版本数 1
常见问题

Shadcn Ui 是什么?

Use when building UI with shadcn/ui components, Tailwind CSS layouts, form patterns with react-hook-form and zod, theming, dark mode, sidebar layouts, mobile... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 92 次。

如何安装 Shadcn Ui?

在 OpenClaw 或 Claude Code 对话框中运行命令「/install shadcn-ui-pro」即可一键安装,无需额外配置。

Shadcn Ui 是免费的吗?

是的,Shadcn Ui 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。

Shadcn Ui 支持哪些平台?

Shadcn Ui 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。

谁开发了 Shadcn Ui?

由 shenghoo123-png(@shenghoo123-png)开发并维护,当前版本 v1.0.0。

💬 留言讨论