shadcn-svelte 组件组合规范实战指南:Composition 规则全解析

发布时间:2026/9/16 11:37:53
shadcn-svelte 组件组合规范实战指南:Composition 规则全解析
shadcn-svelte 组件组合规范实战指南Composition 规则全解析【免费下载链接】shadcn-svelteshadcn/ui, but for Svelte. ✨项目地址: https://gitcode.com/GitHub_Trending/sh/shadcn-svelte本文是 shadcn-svelte 组件库的组合规范Component Composition深度指南。它总结了在使用 shadcn-svelte 构建 Svelte 5 应用时必须遵循的组件结构与组装约定——从Select.Item必须放入Select.Group、overlay 组件如何按场景选型到 Dialog 必须带 Title、按钮加载态如何用Spinner组合等。读完本文你将掌握一套可复现、可审查的组件写法避免常见的组合错误并理解每条规则背后的无障碍与语义化动机。为什么需要组合规范shadcn-svelte 采用复制到你的项目里再修改的分发模型registry组件以源码形式存在于你的项目中例如docs/src/lib/registry/ui/目录下的select/、dialog/、card/等子目录。每个组件目录都由若干部件part组成并通过index.ts以命名空间方式统一导出。以 select/index.ts 为例import Group from ./select-group.svelte; import Item from ./select-item.svelte; // ... export { Root, Group, Label, Item, /* ... */ };因此你在业务代码中通常是import * as Select from $lib/components/ui/select再以Select.Content、Select.Group、Select.Item的形式组合使用。这种原子部件 显式组合的设计要求开发者遵守一套清晰的组装纪律——这正是本文档规则的价值所在。以下逐条展开。Items 必须放在对应的 Group 组件内规则永远不要把条目Item直接渲染在内容容器Content里必须先包一层 Group。错误写法script langts import * as Select from $lib/components/ui/select; /script Select.Content Select.Item valueappleApple/Select.Item Select.Item valuebananaBanana/Select.Item /Select.Content正确写法script langts import * as Select from $lib/components/ui/select; /script Select.Content Select.Group Select.Item valueappleApple/Select.Item Select.Item valuebananaBanana/Select.Item /Select.Group /Select.Content这一规则适用于所有基于 Group 的组件对应关系如下Item条目Group分组Select.Item、Select.LabelSelect.GroupDropdownMenu.Item、DropdownMenu.Label、DropdownMenu.SubDropdownMenu.GroupMenubar.ItemMenubar.GroupContextMenu.ItemContextMenu.GroupCommand.ItemCommand.Group从源码看这些 Group 部件都是对 bits-ui 原始组件的薄封装。例如 select-group.svelte 只是接收SelectPrimitive.GroupProps、合并 class 并透传剩余 props并未额外添加逻辑。这意味着分组语义如键盘导航的分组边界、屏幕阅读器对选项组的分组朗读直接继承自底层库若省略 Group 直接渲染 Item会丢失分组语义与正确的无障碍结构。同理可对照dropdown-menu、menubar、context-menu、command目录下的对应部件源码。提示类信息使用 Alert需要向用户展示告警、警告或需要注意的信息时使用Alert组合而不是自己拼div class...script langts import * as Alert from $lib/components/ui/alert; /script Alert.Root Alert.TitleWarning/Alert.Title Alert.DescriptionSomething needs attention./Alert.Description /Alert.Root从 alert/index.ts 可以看到Alert 由Root、Title、Description、Action四部分组成并导出alertVariants供变体扩展它们共同保证了标题-描述结构的语义完整。在 shadcn-svelte 文档站中代码示例旁的安全提醒、迁移说明等 callout 正是以这种组合方式实现的。空状态使用 Empty 组件列表无数据、搜索无结果等空状态场景统一使用Empty组件家族形成一致的视觉与结构script langts import * as Empty from $lib/components/ui/empty; import { Button } from $lib/components/ui/button; import FolderIcon from lucide/svelte/icons/folder; /script Empty.Root Empty.Header Empty.Media varianticonFolderIcon //Empty.Media Empty.TitleNo projects yet/Empty.Title Empty.Description Get started by creating a new project./Empty.Description /Empty.Header Empty.Content ButtonCreate Project/Button /Empty.Content /Empty.Root从 empty/index.ts 可见Empty 家族包含Root、Header、Media、Title、Description、Content六个部件。Empty.Media支持图标与图片两种variant用于承载引导性视觉Empty.Content用于放置操作按钮等行动号召。lucide/svelte是当前项目推荐的图标源图标源码统一维护在docs/src/lib/registry/icons/对应文档见 icons.md。Toast 通知统一使用 svelte-sonner所有轻量级 Toast 通知统一走svelte-sonner的toastAPI而不是自建提示组件script langts import { toast } from svelte-sonner; /scripttoast.success(Changes saved.); toast.error(Something went wrong.); toast(File deleted., { action: { label: Undo, onClick: () undoDelete() }, });在应用布局layout中只需挂载一次Toaster从你的 UI 目录导入即docs/src/lib/registry/ui/sonner/下的Toaster后续所有页面即可共享通知通道。svelte-sonner的完整用法与属性说明见 sonner 组件文档。注意action回调中应调用你自己作用域内的处理函数如示例中的undoDelete不要引用未定义的全局变量。overlay 组件如何选型shadcn-svelte 提供了多种覆盖层overlay组件选型依据是交互任务的形态使用场景组件需要输入、聚焦单一任务Dialog破坏性操作确认AlertDialog侧边面板承载详情或筛选Sheet移动端优先的底部面板Drawer悬停时展示快速信息HoverCard点击后展示小型上下文内容Popover这组选择的本质区别在于交互语义Dialog是模态聚焦AlertDialog强调危险动作的二次确认Sheet是内容型抽屉通常伴随筛选表单Drawer面向触屏手势HoverCard不抢占点击焦点Popover是轻量浮层。它们各自拥有独立的部件目录docs/src/lib/registry/ui/下的dialog/、alert-dialog/、sheet/、drawer/、hover-card/、popover/均可按命名空间方式组合。Dialog、Sheet、Drawer 必须提供 Title规则Dialog.Title、Sheet.Title、Drawer.Title是必选项——这些组件底层基于 ARIA dialog 角色缺失标题会破坏屏幕阅读器的可访问性。如果视觉上不需要显示标题用classsr-only隐藏而不是删掉。script langts import * as Dialog from $lib/components/ui/dialog; /script Dialog.Content Dialog.Header Dialog.TitleEdit Profile/Dialog.Title Dialog.DescriptionUpdate your profile./Dialog.Description /Dialog.Header ... /Dialog.Content从 dialog-title.svelte 的源码可以看出Dialog.Title是 bits-uiDialogPrimitive.Title的封装——底层组件本身就被设计为承载aria-labelledby语义的角色省略它意味着弹窗失去可访问名称。Sheet、Drawer的实现与之同构规范同样适用。Card 的标准结构使用完整组合不要把内容全部塞进Card.Content。应使用完整的 Card 组合script langts import * as Card from $lib/components/ui/card; import { Button } from $lib/components/ui/button; /script Card.Root Card.Header Card.TitleTeam Members/Card.Title Card.DescriptionManage your team./Card.Description /Card.Header Card.Content.../Card.Content Card.Footer ButtonInvite/Button /Card.Footer /Card.Root这样划分的好处Header统一承载标题与描述保持间距一致Content只负责主体内容Footer固定放操作按钮区对齐到卡片底部。Card家族部件同样维护在docs/src/lib/registry/ui/card/下逐个部件均有独立源码文件便于按需裁剪。Button 没有 isPending / isLoading prop用 Spinner 组合规则shadcn-svelte 的Button不提供isPending或isLoading这类加载态 prop。加载状态应当通过组合实现——在Button内部放入Spinner并配合disabledscript langts import { Button } from $lib/components/ui/button; import { Spinner } from $lib/components/ui/spinner; /script Button disabled Spinner>script langts import * as Tabs from $lib/components/ui/tabs; let tab $state(account); /script Tabs.Root bind:value{tab} Tabs.List Tabs.Trigger valueaccountAccount/Tabs.Trigger Tabs.Trigger valuepasswordPassword/Tabs.Trigger /Tabs.List Tabs.Content valueaccount.../Tabs.Content /Tabs.Root注意示例使用了 Svelte 5 的$state声明响应式状态并通过bind:value{tab}让选中值与外部状态双向同步。Tabs.List之下也可以自由组合Tabs.Trigger与Tabs.Indicator等部件见docs/src/lib/registry/ui/tabs/但Trigger永远不能脱离List存在。Avatar 必须包含 Avatar.Fallback规则Avatar组合中必须包含Avatar.Fallback用于图片加载失败或未提供图片时的降级展示script langts import * as Avatar from $lib/components/ui/avatar; /script Avatar.Root Avatar.Image src/avatar.png altUser / Avatar.FallbackJD/Avatar.Fallback /Avatar.Root从 avatar-fallback.svelte 的源码可见Avatar.Fallback封装自 bits-ui 的AvatarPrimitive.Fallback其核心行为是仅当图片尚未加载完成或加载失败时渲染通常是姓名缩写或默认图标成功加载后自动隐藏。不写 Fallback 意味着头像加载失败时会出现空缺口。多个头像组合、尺寸变体等用法可参考 avatar 文档。优先使用现成组件替代自定义标记遇到以下手写样式冲动时用库内组件替换不要这么写应该用hr或div classborder-tSeparator /import { Separator } from $lib/components/ui/separatordiv classanimate-pulse加手写占位 divSkeleton classh-4 w-3/4 /import { Skeleton } from $lib/components/ui/skeletonspan classrounded-full bg-green-100 ...Badge variantsecondaryimport { Badge } from $lib/components/ui/badge替换动机有三点语义与无障碍Separator使用roleseparator语义docs/src/lib/registry/ui/separator/比裸hr更明确Skeleton自带aria-hidden与脉动动画不会干扰读屏。主题一致性Badge的variant体系default/secondary/outline 等与全局主题色绑定手写bg-green-100这类硬编码颜色会脱离主题定制主题定义见 theming 文档 与docs/src/lib/registry/themes.ts。可维护性使用库组件后改主题或改间距只需动一处无需全局搜索手写 class。汇总组合规范的核心理念总结本规范shadcn-svelte 的组合哲学可以提炼为三条先查库再手写Separator、Skeleton、Badge、Alert、Empty等语义化部件已经覆盖了绝大多数散件需求优先组合而非造轮子尊重底层语义Group、Title、Fallback、List 这些必须项其约束来自 bits-ui / ARIA 的语义要求省略会导致功能或无障碍缺陷而非单纯的风格偏好状态外置为组合Button的加载态、Avatar的降级态等都通过部件组合表达保持组件 API 精简统一。按此规范编写的组件代码既能在npm run devSvelteKit 开发服务器下获得稳定的交互行为也便于后续按需修改。想要快速应用到自己的项目可先阅读 CLI 使用说明 与 安装指南 将所需组件安装到项目中再对照本文规则进行组装。【免费下载链接】shadcn-svelteshadcn/ui, but for Svelte. ✨项目地址: https://gitcode.com/GitHub_Trending/sh/shadcn-svelte创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考