Token导航 LogoToken导航TokenDH.com
研究检索需要联网unknown未标认证来源可访问许可证需确认审计通过

josechifflet-shadcn-uijosechifflet shadcn/ui UI 搜索

Agent Skill

用于辅助界面设计、视觉规范、排版、配色、布局和交互体验优化。它适合让 Agent 根据产品场景整理页面结构、生成 UI 方案、检查视觉一致性或改进组件层级。使用时需要结合现有品牌、设计系统和用户任务,不应只堆装饰元素;涉及真实页面改动时,应通过截图或浏览器预览检查文本溢出、对齐和响应式表现。

总安装

864

周安装

36

下载量

288
Local Agent

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:josechifflet-shadcn-ui(josechifflet shadcn/ui UI 搜索)
来源仓库:https://smithery.ai
仓库路径:josechifflet-shadcn-ui
安装命令:
# Install shadcn CLI
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 对应宿主 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.sh安装方式未标明
# Install shadcn CLI

简介

josechifflet shadcn/ui UI 搜索提供组件设计规范与最佳实践指南。

  • 适合前端开发者构建类型安全、无障碍且移动端友好的界面。
  • 强调 Tailwind CSS 扩展、表单集成和响应式设计原则。
  • 使用前需已有 React + TypeScript 项目基础。
  • 建议复制而非导入组件,保持代码可控性。

SKILL.md

shadcn/ui Component Usage

Purpose

Provide comprehensive patterns for implementing shadcn/ui components in Next.js 16 applications with Atlas-specific conventions. Focus on composition over props, accessibility-first design, and type-safe integration with tRPC and react-hook-form.

When To Use This Skill

Component Implementation:

  • Adding new shadcn/ui components to the project
  • Customizing component variants and styles
  • Building composite components from primitives (Dialog, Form, Table)
  • Implementing responsive mobile/desktop patterns

Form Integration:

  • Integrating react-hook-form with shadcn Form components
  • Validating forms with Zod schemas
  • Connecting forms to tRPC mutations
  • Handling form state and errors

Data Display:

  • Creating data tables with sorting and filtering
  • Building card layouts and list views
  • Implementing skeleton loading states

Interactive Patterns:

  • Building modal dialogs and drawers (sheets)
  • Implementing toast notifications
  • Creating dropdown menus and popovers
  • Adding tooltips and hover states

Accessibility:

  • Ensuring keyboard navigation works correctly
  • Adding proper ARIA labels (especially icon buttons)
  • Implementing focus management
  • Meeting WCAG 2.1 AAA standards (44px minimum touch targets)

Core Principles

1. Copy-Not-Import Philosophy

shadcn/ui components are copied into the project, not imported as dependencies. Customize directly in src/components/ui/.

// ✅ Customize directly
// src/components/ui/button.tsx
const buttonVariants = cva("...", {
  variants: {
    variant: {
      default: "bg-primary text-primary-foreground",
      // Add your custom variant
      atlas: "bg-blue-600 text-white hover:bg-blue-700",
    },
  },
});

2. Composition Over Props

Build complex components by composing primitives rather than adding props:

// ❌ Avoid: Too many props
<Dialog
  title="Delete Item"
  description="Are you sure?"
  showCloseButton={true}
  size="lg"
/>

// ✅ Prefer: Composition
<Dialog>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Delete Item</DialogTitle>
      <DialogDescription>Are you sure?</DialogDescription>
    </DialogHeader>
  </DialogContent>
</Dialog>

3. className Customization

Extend styles via className prop using Tailwind utilities:

<Button variant="default" className="w-full sm:w-auto shadow-lg">
  Submit
</Button>

4. Accessibility First

All components are accessible by default (built on Radix UI):

  • Keyboard navigation
  • Screen reader support
  • ARIA attributes
  • Focus management

Icon buttons require labels for accessibility:

// ❌ Inaccessible
<Button size="icon">
  <TrashIcon />
</Button>

// ✅ Accessible
<Button size="icon" aria-label="Delete item">
  <TrashIcon />
</Button>

Quick Reference

Common Components

ComponentUse CaseKey Features
ButtonActions, triggersVariants, sizes, loading state
InputText entryTypes, validation states
FormForm validationreact-hook-form integration
DialogModalsPortal, overlay, animations
SheetSide panelsMobile-friendly drawers
TableData displaySemantic HTML, responsive
SelectDropdownsSearchable, keyboard nav
CheckboxBoolean inputIndeterminate state
LabelForm labelsAuto-linked to inputs
TextareaMulti-line inputAuto-resize support
TabsNavigationKeyboard accessible
CardContent containersHeader, content, footer
BadgeStatus labelsVariants for states
SkeletonLoading statesPlaceholder UI
SeparatorVisual dividersHorizontal/vertical
Dropdown MenuActions menuNested menus, shortcuts
AlertNotificationsInfo, warning, error
ProgressLoading indicatorsDeterminate/indeterminate
TooltipHover hintsDelay, positioning

Button Variants & Sizes

<Button variant="default">Primary Action</Button>
<Button variant="secondary">Secondary Action</Button>
<Button variant="outline">Outlined</Button>
<Button variant="ghost">Subtle</Button>
<Button variant="destructive">Delete</Button>
<Button variant="link">Link Style</Button>

<Button size="sm">Small</Button>      {/* 32px mobile, 40px desktop */}
<Button size="default">Default</Button> {/* 44px mobile, 36px desktop */}
<Button size="lg">Large</Button>      {/* 48px mobile, 40px desktop */}
<Button size="icon" aria-label="Add"> {/* 44x44px - WCAG 2.1 AAA */}
  <PlusIcon />
</Button>

Loading States

<Button loading={isPending} loadingText="Saving...">
  Save Changes
</Button>

// Or manually:
<Button disabled={isPending}>
  {isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
  Save Changes
</Button>

Form Integration

Basic Form Pattern

"use client";

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

const schema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
});

type FormValues = z.infer<typeof schema>;

export function MyFormClient() {
  const form = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: { email: "", name: "" },
  });

  const onSubmit = form.handleSubmit(async (data) => {
    // Handle submission
  });

  return (
    <Form {...form}>
      <form onSubmit={onSubmit} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="you@example.com" {...field} />
              </FormControl>
              <FormDescription>We'll never share your email.</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />

        <Button type="submit" loading={form.formState.isSubmitting}>
          Submit
        </Button>
      </form>
    </Form>
  );
}

Form with tRPC Mutation

"use client";

import { api } from "@/lib/api/react";
import { toast } from "sonner";

export function CreateStackFormClient() {
  const form = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: {
      /* ... */
    },
  });

  const utils = api.useUtils();
  const createMutation = api.stacks.create.useMutation({
    onSuccess: () => {
      toast.success("Stack created successfully");
      utils.stacks.list.invalidate(); // Refetch list
      form.reset();
    },
    onError: (error) => {
      toast.error(error.message);
    },
  });

  const onSubmit = form.handleSubmit((data) => {
    createMutation.mutate(data);
  });

  return (
    <Form {...form}>
      <form onSubmit={onSubmit} className="space-y-4">
        {/* Form fields */}
        <Button
          type="submit"
          loading={createMutation.isPending}
          disabled={createMutation.isPending}
        >
          Create Stack
        </Button>
      </form>
    </Form>
  );
}

Common Form Field Patterns

Number Input:

<FormField
  control={form.control}
  name="quantity"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Quantity</FormLabel>
      <FormControl>
        <Input
          type="number"
          inputMode="numeric"
          placeholder="100"
          {...field}
          onChange={(e) => field.onChange(e.target.valueAsNumber)}
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

Date Input:

<FormField
  control={form.control}
  name="scheduledFor"
  render={({ field: { value, onChange, ...field } }) => (
    <FormItem>
      <FormLabel>Scheduled Date</FormLabel>
      <FormControl>
        <Input
          type="date"
          {...field}
          value={value instanceof Date ? value.toISOString().split("T")[0] : ""}
          onChange={(e) => {
            const dateStr = e.target.value;
            if (dateStr) onChange(new Date(dateStr));
          }}
        />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

Select/Dropdown:

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

<FormField
  control={form.control}
  name="status"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Status</FormLabel>
      <Select onValueChange={field.onChange} defaultValue={field.value}>
        <FormControl>
          <SelectTrigger>
            <SelectValue placeholder="Select a status" />
          </SelectTrigger>
        </FormControl>
        <SelectContent>
          <SelectItem value="active">Active</SelectItem>
          <SelectItem value="inactive">Inactive</SelectItem>
        </SelectContent>
      </Select>
      <FormMessage />
    </FormItem>
  )}
/>;

Textarea:

import { Textarea } from "@/components/ui/textarea";

<FormField
  control={form.control}
  name="notes"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Notes</FormLabel>
      <FormControl>
        <Textarea placeholder="Optional notes..." maxLength={1000} {...field} />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>;

Checkbox:

import { Checkbox } from "@/components/ui/checkbox";

<FormField
  control={form.control}
  name="acceptTerms"
  render={({ field }) => (
    <FormItem className="flex flex-row items-start gap-3 space-y-0">
      <FormControl>
        <Checkbox checked={field.value} onCheckedChange={field.onChange} />
      </FormControl>
      <div className="space-y-1 leading-none">
        <FormLabel>Accept terms and conditions</FormLabel>
        <FormDescription>You agree to our Terms of Service.</FormDescription>
      </div>
    </FormItem>
  )}
/>;

Dialog Patterns

Basic Dialog

import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";

<Dialog>
  <DialogTrigger asChild>
    <Button variant="outline">Open Dialog</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Are you sure?</DialogTitle>
      <DialogDescription>This action cannot be undone.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <Button variant="outline">Cancel</Button>
      <Button variant="destructive">Delete</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>;

Controlled Dialog with Form

"use client";

import { useState } from "react";

export function CreateDialogClient() {
  const [open, setOpen] = useState(false);
  const form = useForm({
    /* ... */
  });

  const createMutation = api.stacks.create.useMutation({
    onSuccess: () => {
      toast.success("Created successfully");
      form.reset();
      setOpen(false); // Close dialog
    },
  });

  const onSubmit = form.handleSubmit((data) => {
    createMutation.mutate(data);
  });

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        <Button>Create New</Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Create Stack</DialogTitle>
          <DialogDescription>Add a new stack to the system.</DialogDescription>
        </DialogHeader>

        <Form {...form}>
          <form onSubmit={onSubmit} className="space-y-4">
            {/* Form fields */}
            <DialogFooter>
              <Button
                type="button"
                variant="outline"
                onClick={() => setOpen(false)}
                disabled={createMutation.isPending}
              >
                Cancel
              </Button>
              <Button type="submit" loading={createMutation.isPending}>
                Create
              </Button>
            </DialogFooter>
          </form>
        </Form>
      </DialogContent>
    </Dialog>
  );
}

Responsive Dialog/Sheet

Atlas has a custom ResponsiveDialogSheet that shows Dialog on desktop, Sheet on mobile:

import { ResponsiveDialogSheet } from "@/components/features/common";

<ResponsiveDialogSheet
  open={open}
  onOpenChange={setOpen}
  title="Create Stack"
  description="Add a new stack to the system"
  trigger={<Button>Open</Button>}
>
  {/* Content - works on both desktop and mobile */}
  <Form {...form}>
    <form onSubmit={onSubmit}>{/* Fields */}</form>
  </Form>
</ResponsiveDialogSheet>;

Data Table Patterns

Basic Table

import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    {invoices.map((invoice) => (
      <TableRow key={invoice.id}>
        <TableCell>{invoice.invoice}</TableCell>
        <TableCell>{invoice.status}</TableCell>
        <TableCell className="text-right">{invoice.amount}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>;

Loading Skeleton

import { Skeleton } from "@/components/ui/skeleton";

if (isLoading) {
  return (
    <div className="space-y-3">
      <Skeleton className="h-10 w-full" />
      <Skeleton className="h-10 w-full" />
      <Skeleton className="h-10 w-full" />
    </div>
  );
}

Toast Notifications

Atlas uses Sonner for toast notifications:

import { toast } from "sonner";

// Success
toast.success("Stack created successfully");

// Error
toast.error("Failed to create stack");

// Info
toast.info("Processing your request...");

// Warning
toast.warning("This action is irreversible");

// With action
toast.success("Stack deleted", {
  action: {
    label: "Undo",
    onClick: () => {
      /* Undo logic */
    },
  },
});

// Promise toast (auto-updates based on promise state)
toast.promise(createMutation.mutateAsync(data), {
  loading: "Creating stack...",
  success: "Stack created successfully",
  error: "Failed to create stack",
});

Styling Patterns

Responsive Design

<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
  {/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
</div>

<Button className="w-full sm:w-auto">
  {/* Full width on mobile, auto on desktop */}
</Button>

Conditional Styles with cn()

import { cn } from "@/lib/utils";

<Button
  className={cn(
    "base-classes",
    isActive && "bg-blue-600",
    isDisabled && "opacity-50 cursor-not-allowed",
    variant === "large" && "text-lg px-6",
  )}
>
  Click me
</Button>;

Dark Mode Support

All shadcn components support dark mode automatically via CSS variables:

<div className="bg-background text-foreground">
  {/* Automatically adapts to light/dark mode */}
</div>

Common Mistakes

❌ Missing aria-label on icon buttons

// Bad
<Button size="icon"><TrashIcon /></Button>

// Good
<Button size="icon" aria-label="Delete item"><TrashIcon /></Button>

❌ Not using FormControl

// Bad - missing ARIA attributes
<FormField
  control={form.control}
  name="email"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Email</FormLabel>
      <Input {...field} />  {/* Missing FormControl */}
      <FormMessage />
    </FormItem>
  )}
/>

// Good - proper ARIA linkage
<FormField
  control={form.control}
  name="email"
  render={({ field }) => (
    <FormItem>
      <FormLabel>Email</FormLabel>
      <FormControl>
        <Input {...field} />
      </FormControl>
      <FormMessage />
    </FormItem>
  )}
/>

❌ Forgetting DialogTrigger asChild

// Bad - creates nested button
<DialogTrigger><Button>Open</Button></DialogTrigger>

// Good - merges props into Button
<DialogTrigger asChild><Button>Open</Button></DialogTrigger>

Installation

# Install shadcn CLI
pnpm dlx shadcn@latest init

# Add components
pnpm dlx shadcn@latest add button input form dialog table select checkbox textarea label

Resources

  • Official docs: https://ui.shadcn.com
  • Examples: See references/examples.md
  • Recipes: See references/recipes.md
  • Patterns: See references/patterns.md
  • Atlas patterns: @/framework/patterns/shadcn.md

Summary

Key practices:

  1. Copy, don't import - customize in src/components/ui/
  2. Compose, don't prop - build from primitives
  3. className over props - extend with Tailwind
  4. Accessibility first - labels on icon buttons
  5. Forms with react-hook-form - use Form components
  6. Mobile-first - 44px minimum touch targets
  7. Type-safe - derive types from tRPC RouterOutputs

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Local Agent

93.56%
按下载量换算269

安全审计

Socket

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills