← スキル一覧に戻る

shadcn-svelte-components
by exceptionless
shadcn-svelte-componentsは、データ分析と処理を支援するスキルです。大規模データセットから価値ある洞察を抽出し、データドリブンな意思決定をサポートします。
⭐ 2,449🍴 513📅 2026年1月22日
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
context7for 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]Dialogpattern - Avoid generic names like
showDialogorisOpen
<script lang="ts">
let openSuspendOrganizationDialog = $state(false);
let openMarkStackDiscardedDialog = $state(false);
let openInviteUserDialog = $state(false);
</script>
DropdownMenu with Options
<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')}>
Navigation Preference
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
レビュー
💬
レビュー機能は近日公開予定です


