UI Components
Explore the shadcn-svelte UI components used in BrewHoard for consistent and accessible interfaces.
BrewHoard uses shadcn-svelte for building consistent, accessible, and customizable UI components. All components are built on top of Tailwind CSS and follow modern design principles.
Installation and Setup
Components are installed in $lib/components/ui/. Import them directly from there:
import { Button } from '$lib/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';Button
The Button component provides various variants and sizes for different use cases.
<script>
import { Button } from '$lib/components/ui/button';
</script>
<Button>Default Button</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>Card
Cards are used to group related content and provide visual separation.
<script>
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '$lib/components/ui/card';
</script>
<Card class="w-96">
<CardHeader>
<CardTitle>Beer Details</CardTitle>
<CardDescription>A delicious IPA from local brewery</CardDescription>
</CardHeader>
<CardContent>
<div class="space-y-2">
<p><strong>ABV:</strong> 6.5%</p>
<p><strong>IBU:</strong> 45</p>
<p><strong>Style:</strong> American IPA</p>
</div>
</CardContent>
</Card>Input
Input components for text entry with built-in styling and accessibility.
<script>
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
let name = $state('');
let email = $state('');
</script>
<div class="space-y-2">
<Label for="name">Name</Label>
<Input id="name" bind:value={name} placeholder="Enter your name" />
</div>
<div class="space-y-2">
<Label for="email">Email</Label>
<Input id="email" type="email" bind:value={email} placeholder="Enter your email" />
</div>Select
Dropdown select component for choosing from predefined options.
<script>
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '$lib/components/ui/select';
let beerStyle = $state('');
</script>
<Select bind:value={beerStyle}>
<SelectTrigger class="w-48">
<SelectValue placeholder="Select beer style" />
</SelectTrigger>
<SelectContent>
<SelectItem value="ipa">IPA</SelectItem>
<SelectItem value="stout">Stout</SelectItem>
<SelectItem value="lager">Lager</SelectItem>
<SelectItem value="wheat">Wheat Beer</SelectItem>
<SelectItem value="porter">Porter</SelectItem>
</SelectContent>
</Select>Dialog
Modal dialogs for confirmations, forms, or additional information.
<script>
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '$lib/components/ui/dialog';
import { Button } from '$lib/components/ui/button';
let open = $state(false);
</script>
<Dialog bind:open>
<DialogTrigger asChild>
<Button>Open Beer Details</Button>
</DialogTrigger>
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>Beer Information</DialogTitle>
</DialogHeader>
<div class="space-y-4">
<p>Detailed information about the selected beer...</p>
<div class="flex justify-end space-x-2">
<Button variant="outline" on:click={() => open = false}>Cancel</Button>
<Button on:click={() => open = false}>Confirm</Button>
</div>
</div>
</DialogContent>
</Dialog>Toast
Toast notifications for showing temporary messages to users.
<script>
import { Button } from '$lib/components/ui/button';
import { toast } from 'svelte-sonner';
function showSuccessToast() {
toast.success('Beer added to collection!');
}
function showErrorToast() {
toast.error('Failed to add beer. Please try again.');
}
</script>
<div class="space-x-2">
<Button on:click={showSuccessToast}>Show Success</Button>
<Button variant="destructive" on:click={showErrorToast}>Show Error</Button>
</div>Custom Styling with Tailwind
All shadcn-svelte components accept a class prop for custom Tailwind styling:
<script>
import { Button } from '$lib/components/ui/button';
import { Card } from '$lib/components/ui/card';
</script>
<Button class="bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-bold py-2 px-4 rounded-lg transform hover:scale-105 transition-all duration-200">
Fancy Button
</Button>
<Card class="border-2 border-dashed border-gray-300 hover:border-blue-500 transition-colors duration-200">
<CardContent class="p-6">
<p class="text-center text-gray-600">Drop files here to upload</p>
</CardContent>
</Card>Component Composition
Components can be composed together for complex UI patterns:
<script>
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
let beerName = $state('');
let rating = $state(5);
</script>
<Card class="w-full max-w-md">
<CardHeader>
<CardTitle>Add New Beer</CardTitle>
</CardHeader>
<CardContent class="space-y-4">
<div class="space-y-2">
<Label for="beer-name">Beer Name</Label>
<Input id="beer-name" bind:value={beerName} placeholder="Enter beer name" />
</div>
<div class="space-y-2">
<Label for="rating">Rating (1-10)</Label>
<Input id="rating" type="number" min="1" max="10" bind:value={rating} />
</div>
<div class="flex space-x-2">
<Button variant="outline" class="flex-1">Cancel</Button>
<Button class="flex-1">Add Beer</Button>
</div>
</CardContent>
</Card>