mcpField
S
shadcn-svelte Components

shadcn-svelte Components

github.com/exceptionless设计与 UI2.5k120 次安装今天更新
代码仓库

关于 shadcn-svelte Components

面向使用 shadcn-svelte 和 bits-ui 构建界面的开发者,提供组件用法约定与代码示例。内容涵盖触发器、对话框、菜单、样式类和导航模式。

主要功能

  1. 01组件导入模式
  2. 02触发器子片段模式
  3. 03对话框命名约定
  4. 04下拉菜单选项组织
  5. 05条件样式与导航建议

适用场景

  1. 01构建可访问的菜单触发器
  2. 02按约定实现对话框状态
  3. 03整理下拉菜单选项
  4. 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