スキル一覧に戻る
exceptionless

shadcn-svelte-components

by exceptionless

shadcn-svelte-componentsは、データ分析と処理を支援するスキルです。大規模データセットから価値ある洞察を抽出し、データドリブンな意思決定をサポートします。

2,449🍴 513📅 2026年1月22日
GitHubで見るManusで実行

SKILL.md


name: shadcn-svelte Components description: | UI components with shadcn-svelte and bits-ui. Component patterns, trigger snippets, dialog handling, and accessibility. Keywords: shadcn-svelte, bits-ui, Button, Dialog, Sheet, Popover, DropdownMenu, Tooltip, Form, Input, Select, child snippet, trigger pattern, cn utility

shadcn-svelte Components

Documentation: shadcn-svelte.com | Use context7 for API reference

Use shadcn-svelte components (bits-ui) for UI. Import with namespace pattern.

Import Pattern

<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>

Trigger Components - Child Snippet Pattern

When using trigger components with custom elements like Button, always use the child snippet pattern:

<!-- ✅ Correct: Single tab stop, proper accessibility -->
<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>

Why This Pattern?

  • Single Tab Stop: Creates only one focusable element
  • Proper Props Delegation: ARIA attributes pass through correctly
  • Accessibility: Maintains keyboard navigation
  • Official Pattern: Documented shadcn-svelte/bits-ui pattern

Wrong Patterns

<!-- ❌ Wrong: Creates two focusable elements (double-tab issue) -->
<Tooltip.Trigger>
    <Button>Content</Button>
</Tooltip.Trigger>

<!-- ❌ Wrong: Manual styling replicates button styles -->
<Tooltip.Trigger class="hover:bg-accent inline-flex...">
    <Icon />
</Tooltip.Trigger>

Apply to All Triggers

<!-- DropdownMenu -->
<DropdownMenu.Trigger>
    {#snippet child({ props })}
        <Button {...props} variant="outline">
            Open Menu
            <ChevronDown />
        </Button>
    {/snippet}
</DropdownMenu.Trigger>

<!-- Popover -->
<Popover.Trigger>
    {#snippet child({ props })}
        <Button {...props} variant="outline" class="w-70">
            Select Date
            <CalendarIcon />
        </Button>
    {/snippet}
</Popover.Trigger>

<!-- Dialog -->
<Dialog.Trigger>
    {#snippet child({ props })}
        <Button {...props}>Open Dialog</Button>
    {/snippet}
</Dialog.Trigger>

Dialog Pattern

<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>

            <!-- Form content -->

            <Dialog.Footer>
                <Button variant="outline" onclick={() => (openCreateDialog = false)}>
                    Cancel
                </Button>
                <Button type="submit">Create</Button>
            </Dialog.Footer>
        </Dialog.Content>
    </Dialog.Root>
{/if}

Dialog Naming Convention

  • Use open[ComponentName]Dialog pattern
  • Avoid generic names like showDialog or isOpen
<script lang="ts">
    let openSuspendOrganizationDialog = $state(false);
    let openMarkStackDiscardedDialog = $state(false);
    let openInviteUserDialog = $state(false);
</script>
<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 File Pattern

// 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 (Slide-out Panel)

<Sheet.Root bind:open={openFiltersSheet}>
    <Sheet.Content side="right">
        <Sheet.Header>
            <Sheet.Title>Filters</Sheet.Title>
        </Sheet.Header>

        <!-- Filter controls -->

        <Sheet.Footer>
            <Button onclick={applyFilters}>Apply</Button>
        </Sheet.Footer>
    </Sheet.Content>
</Sheet.Root>

Class Merging with Array Syntax

Use Svelte array syntax for conditional classes (NOT cn utility):

<!-- ✅ Preferred: Array syntax -->
<Button class={['w-full', isActive && 'bg-primary']}>
    Click me
</Button>

<div class={['flex items-center', expanded && 'bg-muted', className]}>
    Content
</div>

<!-- ❌ Avoid: cn utility (older pattern) -->
<Button class={cn('w-full', isActive && 'bg-primary')}>

Prefer href navigation over onclick/goto:

<!-- ✅ Preferred: Native navigation -->
<Button href="/organizations/new">Create</Button>

<!-- Use onclick only when navigation logic required -->
<Button onclick={async () => {
    await saveData();
    goto('/success');
}}>
    Save and Continue
</Button>

スコア

総合スコア

80/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 1000以上

+15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

+5
Issue管理

オープンIssueが50未満

0/5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

レビュー

💬

レビュー機能は近日公開予定です