尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Coolify 仓库 shadcn 组件组合规范(composition.md)全解:Group 嵌套、覆盖层选择与“组件替代自定义标记“实践

Coolify 仓库 shadcn 组件组合规范(composition.md)全解:Group 嵌套、覆盖层选择与“组件替代自定义标记“实践 Coolify 仓库 shadcn 组件组合规范composition.md全解Group 嵌套、覆盖层选择与组件替代自定义标记实践【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku Netlify that lets you easily deploy static sites, databases, full-stack applications and 280 one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify本文以 Coolify 仓库中 .agents/skills/shadcn/rules/composition.md 为核心完整拆解其中 14 条组件组合Composition规则Item 必须嵌套在 Group 内、覆盖层组件选型、Dialog/Sheet/Drawer 的 Title 强制要求、Card 完整结构、Button 加载态的正确写法以及 Separator/Skeleton/Badge 替代自定义标记的模式。读完你可以直接把这些规则作为 shadcn/ui 项目或 AI Agent 辅助编码场景的组件组合检查清单并对照 SKILL.md 中的 Critical Rules 理解每条规则的来龙去脉。这份文档在 Coolify 仓库中的定位composition.md位于 .agents/skills/shadcn/rules/ 目录是 Coolify 仓库为 AI Agent 内置的一套 shadcn/ui 技能skill中的组件结构规则文件。从 SKILL.md 的 Critical Rules 部分可以看到它被明确归类为两条规则的详细出处Component Structure → composition.md覆盖 Group 嵌套、asChild/render 自定义触发器、Title 强制要求、Card 完整组合、Button 无isPending、TabsTrigger 位置、AvatarFallback 等Use Components, Not Custom Markup → composition.md覆盖 Alert、Empty、sonner Toast、Separator、Skeleton、Badge 等用现成组件而非手写标记的规则。该 skill 的适用前提是任何带components.json的 shadcn 项目SKILL.md 描述中的触发条件配套文件还有 forms.md表单布局、styling.md样式与 Tailwind、icons.md图标规范和 base-vs-radix.mdbase 与 radix 两套底层原语的 API 差异。composition.md 与它们的关系是它管组件之间怎么嵌套而不是样式怎么写或表单怎么布局。需要说明的是Coolify 主站 UI 本身是 Laravel Blade Alpine.js 技术栈见 package.json 与 TECH_STACK.md本规则文件属于面向 Agent 的通用 shadcn 技能包内容因此下文的代码示例均为 shadcn/ui 生态下的 React/TSX 用法。总览14 条组合规则清单文档开头给出的 Contents 完整列出了全部规则可视为检查清单Items always inside their Group componentItem 必须位于 Group 内Callouts use Alert提示框用 AlertEmpty states use Empty component空状态用 EmptyToast notifications use sonnerToast 用 sonnerChoosing between overlay components覆盖层组件选型Dialog, Sheet, and Drawer always need a Title覆盖层必须有 TitleCard structureCard 完整结构Button has no isPending or isLoading propButton 加载态的正确写法TabsTrigger must be inside TabsListTabsTrigger 必须在 TabsList 内Avatar always needs AvatarFallbackAvatar 必须带 FallbackUse Separator instead of raw hr or border divs分隔线用 SeparatorUse Skeleton for loading placeholders加载占位用 SkeletonUse Badge instead of custom styled spans徽章用 BadgeUse existing components instead of custom markup总原则优先用组件而非自定义标记Item 必须嵌套在 Group 组件内这是文档的第一条规则核心要求是永远不要把 Item 直接渲染在内容容器里。以 Select 为例文档给出 Incorrect/Correct 对照// 错误SelectItem 直接放在 SelectContent 下 SelectContent SelectItem valueappleApple/SelectItem SelectItem valuebananaBanana/SelectItem /SelectContent // 正确先包一层 SelectGroup SelectContent SelectGroup SelectItem valueappleApple/SelectItem SelectItem valuebananaBanana/SelectItem /SelectGroup /SelectContent文档随后给出了一张完整的 Item → Group 映射表说明该规则适用于所有基于 Group 的组件ItemGroupSelectItem、SelectLabelSelectGroupDropdownMenuItem、DropdownMenuLabel、DropdownMenuSubDropdownMenuGroupMenubarItemMenubarGroupContextMenuItemContextMenuGroupCommandItemCommandGroup这条规则同样出现在 SKILL.md 的 Critical Rules 摘要中Items always inside their Group并且在 SKILL.md 的 Workflow 第 7 步中还被用作审查从社区 registry 添加组件时的检查项——Check for missing sub-components (e.g.SelectItemwithoutSelectGroup)。也就是说从第三方 registry 拉下来的组件若缺失 Group 包裹属于需要人工/Agent 修复的组合缺陷而不是可接受的写法。补充一层背景为什么 Group 层如此重要从 base-vs-radix.md 对 Select 的 API 描述可以看出base 原语要求把数据通过items属性传给根组件、radix 则用内联 JSX两种底层的SelectContent渲染机制都依赖SelectGroup来组织列表结构包括键盘导航分组与视觉间距。因此漏掉 Group不仅是不规范的代码风格问题还可能导致组件在某个底层原语上渲染或行为异常——这也是该规则被放进always enforced始终强制类别的原因。提示框Callout使用 Alert第二条规则约定提示类内容一律用Alert组合文档给出标准结构Alert AlertTitleWarning/AlertTitle AlertDescriptionSomething needs attention./AlertDescription /Alert /parameter即Alert根组件 AlertTitleAlertDescription三段式结构。SKILL.md 的对应摘要为Callouts useAlert. Dont build custom styled divs.——禁止用自定义样式的div手搓提示框因为Alert已内置语义化角色、间距与图标位结合 icons.md 可知 Alert 内的图标由组件 CSS 处理尺寸不需要size-4之类的类名。空状态使用 Empty 组件空状态不手写而用Empty组件族完整组合。文档示例展示了四个子组件的层级Empty→EmptyHeader→EmptyMediaEmptyTitleEmptyDescription→EmptyContentEmpty EmptyHeader EmptyMedia varianticonFolderIcon //EmptyMedia EmptyTitleNo projects yet/EmptyTitle EmptyDescriptionGet started by creating a new project./EmptyDescription /EmptyHeader EmptyContent ButtonCreate Project/Button /EmptyContent /Empty /parameter要点有两处EmptyMedia通过varianticon声明媒体类型为图标并直接承载图标组件EmptyContent是放置主操作如Create Project按钮的位置。SKILL.md 的组件选择表也明确把Empty states映射到Empty这一个组件SKILL.md空状态没有第二种合规写法。Toast 通知使用 sonner文档约定 Toast 一律来自sonner库的toast()函数而不是 shadcn 自家的组件import { toast } from sonner toast.success(Changes saved.) toast.error(Something went wrong.) toast(File deleted., { action: { label: Undo, onClick: () undoDelete() }, })三种用法分别覆盖成功通知toast.success、错误通知toast.error、带操作按钮的通知第二个参数传action对象labelonClick实现撤销删除这类可交互 Toast。这与 SKILL.md 组件选择表Feedback →sonner(toast)SKILL.md一致shadcn 生态中 Toast 的职责由 sonner 承担Alert/Badge/Progress/Skeleton/Spinner 承担其余反馈形态。覆盖层组件选型Dialog、Sheet、Drawer 与 HoverCard选择哪个覆盖层组件文档给出一张按使用场景 → 组件的对照表使用场景组件需要输入的聚焦任务Dialog破坏性操作确认AlertDialog侧边面板详情或筛选Sheet移动端优先的底部面板Drawer悬停显示快速信息HoverCard点击显示小范围上下文内容Popover这张表与 SKILL.md 组件选择表中Overlays一行Dialog(modal)、Sheet(side panel)、Drawer(bottom sheet)、AlertDialog(confirmation)互为印证。选型判断可归纳为三个维度是否需要用户输入Dialog、是否不可逆AlertDialog、内容在屏幕上的空间形态居中 / 侧边 / 底部 / 浮动跟随。Dialog、Sheet、Drawer 必须提供 Title这是文档中明确的可访问性accessibility强制项DialogTitle、SheetTitle、DrawerTitle均为必需若视觉上不需要展示标题用classNamesr-only隐藏屏幕阅读器仍可读取DialogContent DialogHeader DialogTitleEdit Profile/DialogTitle DialogDescriptionUpdate your profile./DialogDescription /DialogHeader ... /DialogContent结构上遵循DialogContent→DialogHeader→DialogTitleDialogDescription的层级。SKILL.md 将这条与UseclassName\sr-only\if visually hidden一并列入 Critical RulesSKILL.md可见缺 Title会被视为违规而非风格问题。Card 结构使用完整组合而非全部塞进 CardContent文档要求 Card 使用full composition并明确反对把所有内容堆进CardContentCard CardHeader CardTitleTeam Members/CardTitle CardDescriptionManage your team./CardDescription /CardHeader CardContent.../CardContent CardFooter ButtonInvite/Button /CardFooter /Card即CardHeader承载CardTitle、CardDescription、CardContent正文、CardFooter放操作按钮等收尾内容各司其职。这与 SKILL.md 的表述一致Use full Card composition.CardHeader/CardTitle/CardDescription/CardContent/CardFooter. Dont dump everything inCardContent.。Button 没有 isPending / isLoading 属性shadcn 的Button组件不内置加载态属性没有isPending或isLoadingprop正确做法是组合Spinnerdata-icondisabledButton disabled Spinner>Tabs defaultValueaccount TabsList TabsTrigger valueaccountAccount/TabsTrigger TabsTrigger valuepasswordPassword/TabsTrigger /TabsList TabsContent valueaccount.../TabsContent /Tabs即Tabs根组件下分两支TabsList承载全部TabsTrigger与一个或多个TabsContent通过value与对应的TabsTrigger关联。defaultValue声明初始选中的 tab。这条规则在 SKILL.md 摘要中被表述为TabsTriggermust be insideTabsList. Never render triggers directly inTabs.其重要性等级与 Group 嵌套规则相同属于结构层面的硬性约束。Avatar 必须包含 AvatarFallback图片头像加载失败时必须有兜底因此AvatarFallback是必备子组件Avatar AvatarImage src/avatar.png altUser / AvatarFallbackJD/AvatarFallback /AvatarAvatarFallback通常展示用户姓氏首字母如示例中的 JD。SKILL.md 对应规则为Avataralways needsAvatarFallback. For when the image fails to load.——即使你确定图片不会失败该规则也不允许省略这是以确定性兜底换取列表页、侧边栏等大量头像场景的健壮性。用组件替代自定义标记Separator、Skeleton、Badge文档最后也是第一条规则总则的替换对照表给出了三组高频场景的不要 → 要映射Instead of不要Use要用hr或div classNameborder-tSeparator /div classNameanimate-pulse加样式 div 拼的骨架Skeleton classNameh-4 w-3/4 /span classNamerounded-full bg-green-100 ...Badge variantsecondary三条规则的共同逻辑是shadcn 组件已经封装了语义、深色模式适配与交互行为手写标记既丢失语义又会在主题切换时失配。其中 Skeleton 通过className控制尺寸形状h-4 w-3/4表示一行文本的占位这符合 SKILL.md 中classNamefor layout, not styling的原则——className只用于布局维度宽高、比例不承担换色换字的样式职责Badge 则通过variantsecondary这类内置变体表达状态色而不是写死bg-green-100之类的原始色值。SKILL.md 的关键模式示例也给出了一组对照SKILL.mdBadge variantsecondary20.1%/Badge正确span classNametext-emerald-60020.1%/span错误。与 base / radix 双底层的衔接读完 composition.md 后还有一条必要的延伸上述组件的嵌套结构与底层原语无关但属性 API会因components.json中base字段radix或base而不同。这一点由姊妹文件 base-vs-radix.md 专门覆盖与 composition 规则直接交叉的几处包括触发器自定义元素radix 用asChild如DialogTrigger asChildbase 用render如DialogTrigger render{Button /}且 base 将render目标改为非 button 元素a、span时需追加nativeButton{false}Selectbase 要求根组件传items属性、用{ value: null }项表达占位符而 radix 直接用SelectValue placeholder...——但无论哪种底层SelectContent内的SelectGroup包裹结构都保持不变ToggleGroup / Accordionbase 用multiple布尔 数组型defaultValueradix 用typesingle | multiple 字符串defaultValueSliderbase 单滑块接受标量defaultValue{50}radix 永远是数组defaultValue{[50]}。因此实际工作流是先用 composition.md 确定骨架谁包谁、缺什么子组件再查npx shadcnlatest info输出的base字段决定属性写法。SKILL.md 的 WorkflowSKILL.md把这一流程固化为获取项目上下文 → 检查已安装组件 →search找组件 →docs component拉文档 →add安装 → 审阅新增文件是否违反 Critical Rules其中就包括本文件的 Group 嵌套检查。小结把 composition.md 当作组件组合检查清单回到文档本身它的价值在于把 shadcn/ui 中组件之间如何嵌套这一最容易出错的层面压缩成 14 条可逐条勾选的硬规则结构完整性Item 在 Group 内、TabsTrigger 在 TabsList 内、覆盖层带 Title、Avatar 带 Fallback、Card 用完整五件套职责单一化Callout 归 Alert、空状态归 Empty、Toast 归 sonner、加载占位归 Skeleton、分隔线归 Separator、状态标签归 Badge组合优先于 APIButton 加载态不是找isPending属性而是Spinnerdata-icondisabled的组合。配合 forms.md表单用FieldGroup/Field/InputGroup、styling.md语义色、gap-*间距、cn()条件类、icons.mddata-icon与图标对象传递和 base-vs-radix.md双层 API 差异这套 rules 目录构成了完整的 shadcn 代码审查标准也是 SKILL.md 中always enforced规则的落地细则。【免费下载链接】coolifyAn open-source, self-hostable PaaS alternative to Vercel, Heroku Netlify that lets you easily deploy static sites, databases, full-stack applications and 280 one-click services on your own servers.项目地址: https://gitcode.com/GitHub_Trending/co/coolify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表