重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
shadcn-component-review by mattbx/shadcn-skills
npx skills add https://github.com/mattbx/shadcn-skills --skill shadcn-component-review确保自定义组件和布局符合 shadcn 设计模式、官方主题样式以及 Radix UI 最佳实践的系统化审查流程。
在构建组件之前:
src/ui/ 和 src/components/ 中的类似组件构建组件之后:
data-slot 属性和组合广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
对 flex/grid 容器使用 gap-*。间距因主题样式而异:
| 主题 | 间距 | 形状 |
|---|---|---|
| Vega | 标准 | 经典 shadcn |
| Nova | 紧凑 | 减少内边距/外边距 |
| Maia | 宽松 | 柔和、圆润 |
| Lyra | 标准 | 方正、锐利 |
| Mira | 密集 | 紧凑界面 |
查看 references/theme-styles.md 了解特定主题的模式。
data-slot 属性:data-slot="component-name"ComponentName.Header、ComponentName.Contentsrc/ui/*)仅使用语义令牌 - 绝不硬编码颜色:
// ✅ text-muted-foreground, bg-muted, hover:bg-accent
// ❌ text-neutral-500, bg-gray-100, hover:bg-neutral-50
md: (768px+), lg: (1024px+)min-w-0 以防止溢出查看 references/review-checklist.md 获取详细清单。
shadcn 组件使用 CVA 来实现类型安全、声明式的变体:
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center font-medium transition-colors",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input bg-background hover:bg-accent",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 px-3 text-sm",
},
},
defaultVariants: { variant: "default", size: "default" },
}
)
interface ButtonProps extends VariantProps<typeof buttonVariants> {}
关键点:
VariantProps 实现类型安全的属性始终使用 cn() 来处理条件和覆盖类:
import { cn } from "@/lib/utils"
// 结合了 clsx(条件) + tailwind-merge(冲突解决)
<div className={cn(
"base-classes",
isActive && "active-classes",
className // 允许使用者覆盖
)} />
为何重要:
shadcn 主题使用 CSS 变量来实现跨组件的一致样式。
边框半径 通过 --radius 由主题定义:
// ✅ 使用主题半径(适应 Maia 的圆润或 Lyra 的锐利)
<Button className="rounded-md"> // 使用 --radius 变量
<Card className="rounded-lg">
// ❌ 硬编码(忽略主题设置)
<Button className="rounded-[20px]">
主题 CSS 变量(在您的主题 CSS 中定义):
--radius - 基础半径单位--background, --foreground - 基础颜色--primary, --secondary, --accent - 语义颜色--muted, --card, --popover - 表面颜色自定义主题扩展:如果您的项目需要可切换主题的半径(例如,药丸形 vs 锐利),请创建映射到 CSS 变量的工具类:
/* 示例:可切换主题的半径 */
.rounded-theme-button {
border-radius: var(--radius-button);
}
使用一致、微妙的动画。查看 references/animation-patterns.md 了解:
data-state 动画模式motion-safe: 前缀)视觉样式(通过 npx shadcn create):
新工具组件:
input-group / button-group - 分组控件empty - 空状态模式field - 表单字段包装器spinner - 加载指示器技术更新:
@theme 指令)对照 shadcn 模式检查组件结构:
// ✅ 良好:具有 data-slot 的正确结构
<div data-slot="component-name" className="flex flex-col gap-4">
<div data-slot="component-header" className="flex flex-col gap-2">
{/* 头部内容 */}
</div>
<div data-slot="component-content">
{/* 主要内容 */}
</div>
</div>
// ❌ 不良:缺少 data-slot,间距不一致
<div className="space-y-4">
<div className="mb-2">
{/* 头部内容 */}
</div>
<div>
{/* 主要内容 */}
</div>
</div>
验证间距是否遵循您的主题模式:
gap-* 而不是 space-y-* 或外边距gap-X md:gap-Y 模式仅验证语义令牌:
# 检查硬编码颜色
grep -r "neutral-\|gray-\|slate-" [component-file]
确保组件可以:
在断点处测试:
// ✅ 遵循 shadcn 模式
export function PageContent({
heading,
description,
contentBlock,
children,
}: PageContentProps) {
return (
<div
data-slot="page-content"
className="min-w-0 flex flex-col gap-4 md:gap-6"
>
<div data-slot="page-content-header" className="flex flex-col gap-2">
<h1 className="text-xl md:text-2xl tracking-tight font-semibold text-foreground">
{heading}
</h1>
{description && (
<p className="text-sm text-muted-foreground">{description}</p>
)}
</div>
{contentBlock && (
<div data-slot="page-content-block">{contentBlock}</div>
)}
{children}
</div>
);
}
// ❌ 违反多种模式
export function PageContent({ heading, description }: Props) {
return (
<div className="space-y-6">
<div className="mb-4">
<h1 className="text-2xl font-bold text-gray-900">{heading}</h1>
<p className="mt-2 text-sm text-neutral-500">{description}</p>
</div>
</div>
);
}
问题:
space-y-* 而不是 gap-*text-gray-900、text-neutral-500)data-slot 属性mb-4、mt-2 而不是 flex gap)每周安装次数
66
仓库
GitHub 星标
1
首次出现
2026年2月1日
安全审计
安装于
codex54
cursor53
gemini-cli53
github-copilot52
opencode51
kimi-cli49
Systematic review process for ensuring custom components and layouts align with shadcn design patterns, official theme styles, and Radix UI best practices.
Before building a component:
src/ui/ and src/components/After building a component:
data-slot attributes and compositionUse gap-* for flex/grid containers. Spacing varies by theme style:
| Theme | Spacing | Shape |
|---|---|---|
| Vega | Standard | Classic shadcn |
| Nova | Compact | Reduced padding/margins |
| Maia | Generous | Soft, rounded |
| Lyra | Standard | Boxy, sharp |
| Mira | Dense | Compact interfaces |
See references/theme-styles.md for theme-specific patterns.
data-slot attributes: data-slot="component-name"ComponentName.Header, ComponentName.Contentsrc/ui/* directly)Semantic tokens only - never hardcoded colors:
// ✅ text-muted-foreground, bg-muted, hover:bg-accent
// ❌ text-neutral-500, bg-gray-100, hover:bg-neutral-50
md: (768px+), lg: (1024px+)min-w-0 to prevent overflowSee references/review-checklist.md for detailed checklists.
shadcn components use CVA for type-safe, declarative variants:
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center font-medium transition-colors",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input bg-background hover:bg-accent",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 px-3 text-sm",
},
},
defaultVariants: { variant: "default", size: "default" },
}
)
interface ButtonProps extends VariantProps<typeof buttonVariants> {}
Key points:
VariantPropsAlways use cn() for conditional and override classes:
import { cn } from "@/lib/utils"
// Combines clsx (conditionals) + tailwind-merge (conflict resolution)
<div className={cn(
"base-classes",
isActive && "active-classes",
className // allows consumer overrides
)} />
Why it matters:
shadcn themes use CSS variables for consistent styling across components.
Border radius is theme-defined via --radius:
// ✅ Uses theme radius (adapts to Maia rounded vs Lyra sharp)
<Button className="rounded-md"> // Uses --radius variable
<Card className="rounded-lg">
// ❌ Hardcoded (ignores theme settings)
<Button className="rounded-[20px]">
Theme CSS variables (defined in your theme's CSS):
--radius - Base radius unit--background, --foreground - Base colors--primary, --secondary, --accent - Semantic colors--muted, --card, --popover - Surface colorsCustom theme extensions : If your project needs theme-switchable radius (e.g., pill vs sharp), create utility classes mapped to CSS variables:
/* Example: Theme-switchable radius */
.rounded-theme-button {
border-radius: var(--radius-button);
}
Use consistent, subtle animations. See references/animation-patterns.md for:
data-state animation patternsmotion-safe: prefixes)Visual Styles (via npx shadcn create):
New utility components :
input-group / button-group - Grouped controlsempty - Empty state patternsfield - Form field wrapperspinner - Loading indicatorTechnical updates :
@theme directive)Check component structure against shadcn patterns:
// ✅ Good: Proper structure with data-slot
<div data-slot="component-name" className="flex flex-col gap-4">
<div data-slot="component-header" className="flex flex-col gap-2">
{/* Header content */}
</div>
<div data-slot="component-content">
{/* Main content */}
</div>
</div>
// ❌ Bad: Missing data-slot, inconsistent spacing
<div className="space-y-4">
<div className="mb-2">
{/* Header content */}
</div>
<div>
{/* Main content */}
</div>
</div>
Verify spacing follows your theme's patterns:
gap-* not space-y-* or marginsgap-X md:gap-Y patternVerify semantic tokens only:
# Check for hardcoded colors
grep -r "neutral-\|gray-\|slate-" [component-file]
Ensure component can be:
Test at breakpoints:
// ✅ Follows shadcn patterns
export function PageContent({
heading,
description,
contentBlock,
children,
}: PageContentProps) {
return (
<div
data-slot="page-content"
className="min-w-0 flex flex-col gap-4 md:gap-6"
>
<div data-slot="page-content-header" className="flex flex-col gap-2">
<h1 className="text-xl md:text-2xl tracking-tight font-semibold text-foreground">
{heading}
</h1>
{description && (
<p className="text-sm text-muted-foreground">{description}</p>
)}
</div>
{contentBlock && (
<div data-slot="page-content-block">{contentBlock}</div>
)}
{children}
</div>
);
}
// ❌ Violates multiple patterns
export function PageContent({ heading, description }: Props) {
return (
<div className="space-y-6">
<div className="mb-4">
<h1 className="text-2xl font-bold text-gray-900">{heading}</h1>
<p className="mt-2 text-sm text-neutral-500">{description}</p>
</div>
</div>
);
}
Issues:
space-y-* instead of gap-*text-gray-900, text-neutral-500)data-slot attributesmb-4, mt-2 instead of flex gap)Weekly Installs
66
Repository
GitHub Stars
1
First Seen
Feb 1, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex54
cursor53
gemini-cli53
github-copilot52
opencode51
kimi-cli49
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
122,000 周安装