Skip to main content

Component Library

Overview

We use Shadcn/ui for Svelte - components are copied into the project (not installed as dependencies), allowing full customization.

Components are in: src/lib/components/ui/

Button

From user-integration.svelte:

import { Button } from '$lib/components/ui/button';

// Default variant
<Button onclick={handleClick}>
Click Me
</Button>

// Outline variant
<Button variant="outline" onclick={handleClick}>
View Details
</Button>

// Destructive (red) variant
<Button variant="destructive" onclick={handleRemove}>
Remove Integration
</Button>

// Disabled state
<Button disabled={isLoading}>
{isLoading ? 'Loading...' : 'Submit'}
</Button>

// With icon
<Button variant="outline" class="gap-2">
<Eye class="h-4 w-4" />
View Details
</Button>

Variants: default, destructive, outline, secondary, ghost, link

From Sidebar.svelte:

import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
} from '$lib/components/ui/sidebar';

<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive={isActive}>
{#snippet child({ props })}
<a href="/#/search" {...props}>
<Search class="h-5 w-5" />
<span>Search</span>
</a>
{/snippet}
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>

<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton onclick={toggleTheme}>
<span>Toggle Theme</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>

SidebarTrigger

Add to pages to show/hide sidebar:

import { SidebarTrigger } from '$lib/components/ui/sidebar';

<div class="mb-4">
<SidebarTrigger />
</div>

Skeleton (Loading States)

From user-integration.svelte:

import { Skeleton } from '$lib/components/ui/skeleton';

{#if isLoading}
<div class="space-y-4">
{#each Array(3) as _}
<div class="flex items-center gap-4">
<Skeleton class="h-12 w-12 rounded" />
<div class="space-y-2">
<Skeleton class="h-5 w-32" />
<Skeleton class="h-4 w-64" />
</div>
</div>
{/each}
</div>
{/if}

AlertDialog (Confirmation)

From user-integration.svelte:

import * as AlertDialog from '$lib/components/ui/alert-dialog';

<AlertDialog.Root open={integrationToDelete !== null}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Remove Integration?</AlertDialog.Title>
<AlertDialog.Description>
Are you sure you want to remove "{integrationToDelete?.name}"?
This action cannot be undone.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel onclick={handleCancel}>
Cancel
</AlertDialog.Cancel>
<AlertDialog.Action onclick={handleConfirm} disabled={isDeleting}>
{isDeleting ? 'Removing...' : 'Remove Integration'}
</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>

Pagination

From user-integration.svelte:

import * as Pagination from '$lib/components/ui/pagination';

<Pagination.Root
count={totalItems}
bind:page={currentPage}
onPageChange={(page) => handlePageChange(page)}
>
{#snippet children({ pages, currentPage })}
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous />
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === 'ellipsis'}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item>
<Pagination.Link {page} isActive={currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.Next />
</Pagination.Item>
</Pagination.Content>
{/snippet}
</Pagination.Root>

Toast Notifications

From user-integration.svelte:

import { toast } from 'svelte-sonner';
import { Toaster } from '$lib/components/ui/sonner';

// Add to page
<Toaster />

// Success toast
toast.success('Success', {
description: 'Integration removed successfully',
});

// Error toast
toast.error('Error', {
description: 'Failed to remove integration. Please try again.',
});

// Info toast
toast('Info', {
description: 'Processing your request...',
});

Icons

From lucide-svelte:

import { Search, Eye, Trash, Settings, User } from 'lucide-svelte';

<Search class="h-5 w-5" />
<Eye class="h-4 w-4" />

Common sizes: h-4 w-4 (16px), h-5 w-5 (20px), h-6 w-6 (24px)

Browse all icons: Lucide Icons

Dark Mode Integration

All components automatically support dark mode via Tailwind's dark: prefix. The theme is managed by $lib/stores/theme.ts.

Missing Components

Some Shadcn components are not installed. Workarounds:

Badge (not available)

Use custom Tailwind classes:

// Status badge
<span class="rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-200">
Active
</span>

<span class="rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800 dark:bg-red-900 dark:text-red-200">
Error
</span>

Best Practices

  1. Import components from $lib/components/ui/
  2. Use Tailwind classes for styling (e.g., class="gap-2")
  3. Follow dark mode patterns with dark: prefix
  4. Use Skeleton components for loading states
  5. Use AlertDialog for destructive actions
  6. Use toast notifications for user feedback
  7. Check component exists before using (not all Shadcn components installed)

Adding New Components

To add a new Shadcn component:

npx shadcn-svelte@latest add [component-name]

Example:

npx shadcn-svelte@latest add card

This copies the component into src/lib/components/ui/.