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:

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

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

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

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

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

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

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

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

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

Next Steps