关于 shadcn-svelte Components
面向使用 shadcn-svelte 和 bits-ui 构建界面的开发者,提供组件用法约定与代码示例。内容涵盖触发器、对话框、菜单、样式类和导航模式。
主要功能
- 01组件导入模式
- 02触发器子片段模式
- 03对话框命名约定
- 04下拉菜单选项组织
- 05条件样式与导航建议
适用场景
- 01构建可访问的菜单触发器
- 02按约定实现对话框状态
- 03整理下拉菜单选项
- 04为界面添加滑出面板
MCP 相关
README 提及使用 context7 查询 API 参考,但未说明该技能如何与 MCP 交互。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ exceptionless/ exceptionless --skill shadcn-svelte-components安装后在 Claude Code 中输入/shadcn-svelte-components确认已加载。
详细介绍
shadcn-svelte 组件
文档: shadcn-svelte.com | 使用
context7查阅 API 参考
使用 shadcn-svelte 组件(bits-ui)构建 UI。使用命名空间模式导入。
导入模式
<script lang="ts">
import * as Dialog from '$comp/ui/dialog';
import * as DropdownMenu from '$comp/ui/dropdown-menu';
import * as Tooltip from '$comp/ui/tooltip';
import { Button } from '$comp/ui/button';
import { Input } from '$comp/ui/input';
</script>
触发器组件 — 子片段模式
使用带有 Button 等自定义元素的触发器组件时,始终使用 child 片段模式:
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<Button {...props} variant="ghost" size="icon">
<Icon />
</Button>
{/snippet}
</Tooltip.Trigger>
<Tooltip.Content>Tooltip text</Tooltip.Content>
</Tooltip.Root>
为什么使用此模式?
- 单一 Tab 停靠点: 只创建一个可聚焦元素
- 正确委托属性: ARIA 属性能够正确传递
- 无障碍性: 保持键盘导航功能
- 官方模式: shadcn-svelte/bits-ui 文档中介绍的模式
错误模式
<Tooltip.Trigger>
<Button>Content</Button>
</Tooltip.Trigger>
<Tooltip.Trigger class="hover:bg-accent inline-flex...">
<Icon />
</Tooltip.Trigger>
应用于所有触发器
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline">
Open Menu
<ChevronDown />
</Button>
{/snippet}
</DropdownMenu.Trigger>
<Popover.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline" class="w-70">
Select Date
<CalendarIcon />
</Button>
{/snippet}
</Popover.Trigger>
<Dialog.Trigger>
{#snippet child({ props })}
<Button {...props}>Open Dialog</Button>
{/snippet}
</Dialog.Trigger>
Dialog 模式
<script lang="ts">
import * as Dialog from '$comp/ui/dialog';
import { Button } from '$comp/ui/button';
let openCreateDialog = $state(false);
</script>
<Button onclick={() => (openCreateDialog = true)}>Create</Button>
{#if openCreateDialog}
<Dialog.Root bind:open={openCreateDialog}>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Create Organization</Dialog.Title>
<Dialog.Description>
Add a new organization to your account.
</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Button variant="outline" onclick={() => (openCreateDialog = false)}>
Cancel
</Button>
<Button type="submit">Create</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
{/if}
Dialog 命名约定
- 使用
open[ComponentName]Dialog模式 - 避免使用
showDialog或isOpen等通用名称
<script lang="ts">
let openSuspendOrganizationDialog = $state(false);
let openMarkStackDiscardedDialog = $state(false);
let openInviteUserDialog = $state(false);
</script>
带选项的 DropdownMenu
<script lang="ts">
import * as DropdownMenu from '$comp/ui/dropdown-menu';
import { statusOptions } from './options';
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline">
Select Status
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{#each statusOptions as option}
<DropdownMenu.Item onclick={() => handleSelect(option.value)}>
{option.label}
</DropdownMenu.Item>
{/each}
</DropdownMenu.Content>
</DropdownMenu.Root>
选项文件模式
// options.ts
import type { DropdownItem } from '$shared/types';
export enum Status {
Active = 'active',
Inactive = 'inactive',
Pending = 'pending'
}
export const statusOptions: DropdownItem<Status>[] = [
{ value: Status.Active, label: 'Active' },
{ value: Status.Inactive, label: 'Inactive' },
{ value: Status.Pending, label: 'Pending' }
];
Sheet(滑出面板)
<Sheet.Root bind:open={openFiltersSheet}>
<Sheet.Content side="right">
<Sheet.Header>
<Sheet.Title>Filters</Sheet.Title>
</Sheet.Header>
<Sheet.Footer>
<Button onclick={applyFilters}>Apply</Button>
</Sheet.Footer>
</Sheet.Content>
</Sheet.Root>
使用数组语法合并类名
使用 Svelte 数组语法处理条件类名(不要使用 cn 工具):
<Button class={['w-full', isActive && 'bg-primary']}>
Click me
</Button>
<div class={['flex items-center', expanded && 'bg-muted', className]}>
Content
</div>
<Button class={cn('w-full', isActive && 'bg-primary')}>
导航偏好
优先使用 href 导航,而非 onclick/goto:
<Button href="/organizations/new">Create</Button>
<Button onclick={async () => {
await saveData();
goto('/success');
}}>
Save and Continue
</Button>
评论
支持 Markdown