shadcn-svelte-components by exceptionless
Design
2.4K Stars
513 Forks
Updated Jan 12, 2026, 04:05 AM
Why Use This
This skill provides specialized capabilities for exceptionless's codebase.
Use Cases
- Developing new features in the exceptionless repository
- Refactoring existing code to follow exceptionless standards
- Understanding and working with exceptionless's codebase structure
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Skill Stats
SKILL.md 243 Lines
Total Files 1
Total Size 0 B
License NOASSERTION
---
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](https://www.shadcn-svelte.com/) | Use `context7` for API reference
Use shadcn-svelte components (bits-ui) for UI. Import with namespace pattern.
## Import Pattern
```svelte
<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**:
```svelte
<!-- ✅ 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
```svelte
<!-- ❌ 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
```svelte
<!-- 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
```svelte
<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`
```svelte
<script lang="ts">
let openSuspendOrganizationDialog = $state(false);
let openMarkStackDiscardedDialog = $state(false);
let openInviteUserDialog = $state(false);
</script>
```
## DropdownMenu with Options
```svelte
<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
```typescript
// 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)
```svelte
<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):
```svelte
<!-- ✅ 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`:
```svelte
<!-- ✅ 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>
```
Name Size