Skip to main content

reptidex Component Library

Overview

The reptidex component library implements our Forest & Stone design system using Radix UI primitives with Tailwind CSS styling. Built as @reptidex/ui, it provides production-ready components for professional reptile breeding workflows across all four frontend applications.
Architecture: Radix UI headless components + Tailwind CSS + Forest & Stone design tokens. Shared across web-public, web-breeder, web-admin, and web-embed applications.

Package Architecture

@reptidex/ui Structure

packages/ui/
├── src/
   ├── components/           # All UI components
   ├── ui/              # Base UI primitives
   ├── button/
   ├── input/
   ├── dialog/
   ├── dropdown-menu/
   └── ...
   ├── forms/           # Form components
   ├── animal-form/
   ├── breeding-form/
   └── ...
   ├── specialized/     # Domain-specific components
   ├── pedigree/
   ├── marketplace/
   └── ...
   └── layout/          # Layout components
       ├── header/
       ├── sidebar/
       └── ...
   ├── lib/                 # Utilities and helpers
   ├── utils.ts         # Class merging, validation
   ├── cn.ts           # clsx + tailwind-merge
   └── ...
   ├── hooks/              # Shared React hooks
   ├── icons/              # Icon components
   └── styles/             # Global styles and tokens
       ├── globals.css
       └── components.css
├── tailwind.config.js      # Tailwind configuration
├── tsconfig.json          # TypeScript configuration
└── package.json           # Package dependencies

Dependencies

{
  "dependencies": {
    "@radix-ui/react-accordion": "^1.1.2",
    "@radix-ui/react-alert-dialog": "^1.0.5",
    "@radix-ui/react-avatar": "^1.0.4",
    "@radix-ui/react-checkbox": "^1.0.4",
    "@radix-ui/react-dialog": "^1.0.5",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-form": "^0.0.3",
    "@radix-ui/react-label": "^2.0.2",
    "@radix-ui/react-popover": "^1.0.7",
    "@radix-ui/react-progress": "^1.0.3",
    "@radix-ui/react-select": "^2.0.0",
    "@radix-ui/react-separator": "^1.0.3",
    "@radix-ui/react-slot": "^1.0.2",
    "@radix-ui/react-switch": "^1.0.3",
    "@radix-ui/react-tabs": "^1.0.4",
    "@radix-ui/react-toast": "^1.1.5",
    "@radix-ui/react-tooltip": "^1.0.7",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.0.0",
    "lucide-react": "^0.294.0",
    "tailwind-merge": "^2.0.0",
    "tailwindcss-animate": "^1.0.7"
  }
}

Design System Foundation

Tailwind Configuration

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ["class"],
  content: [
    './pages/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
    './src/**/*.{ts,tsx}',
  ],
  theme: {
    extend: {
      colors: {
        // Forest & Stone Color System
        'deep-pine': '#3B4031',
        'off-white': '#F6F3EE',
        'moss-green': {
          DEFAULT: '#8A9A5B',
          50: '#F4F6E8',
          100: '#E8EDD1',
          200: '#D2DBA3',
          300: '#BBC975',
          400: '#A5B747',
          500: '#8A9A5B',
          600: '#6E7B49',
          700: '#535C37',
          800: '#373D25',
          900: '#1C1E12',
        },
        'warm-sand': {
          DEFAULT: '#D9CBA3',
          50: '#F7F4ED',
          100: '#EFE9DB',
          200: '#DFD3B7',
          300: '#CFBD93',
          400: '#BFA76F',
          500: '#D9CBA3',
          600: '#ADA282',
          700: '#817961',
          800: '#555040',
          900: '#2A281F',
        },
        'soft-clay': {
          DEFAULT: '#C68642',
          50: '#F5EFDF',
          100: '#EBDFBF',
          200: '#D7BF7F',
          300: '#C39F3F',
          400: '#C68642',
          500: '#C68642',
          600: '#9E6B35',
          700: '#775028',
          800: '#4F351A',
          900: '#281A0D',
        },
        // Semantic colors
        border: 'hsl(var(--border))',
        input: 'hsl(var(--input))',
        ring: 'hsl(var(--ring))',
        background: 'hsl(var(--background))',
        foreground: 'hsl(var(--foreground))',
        primary: {
          DEFAULT: '#8A9A5B',
          foreground: '#FFFFFF',
        },
        secondary: {
          DEFAULT: '#D9CBA3',
          foreground: '#3B4031',
        },
        destructive: {
          DEFAULT: 'hsl(var(--destructive))',
          foreground: 'hsl(var(--destructive-foreground))',
        },
        muted: {
          DEFAULT: 'hsl(var(--muted))',
          foreground: 'hsl(var(--muted-foreground))',
        },
        accent: {
          DEFAULT: '#C68642',
          foreground: '#FFFFFF',
        },
        popover: {
          DEFAULT: 'hsl(var(--popover))',
          foreground: 'hsl(var(--popover-foreground))',
        },
        card: {
          DEFAULT: 'hsl(var(--card))',
          foreground: 'hsl(var(--card-foreground))',
        },
      },
      borderRadius: {
        lg: '16px',
        md: '12px',
        sm: '8px',
      },
      fontFamily: {
        heading: ['Merriweather', 'serif'],
        body: ['Roboto', 'sans-serif'],
        accent: ['Playfair Display', 'serif'],
      },
      keyframes: {
        'accordion-down': {
          from: { height: 0 },
          to: { height: 'var(--radix-accordion-content-height)' },
        },
        'accordion-up': {
          from: { height: 'var(--radix-accordion-content-height)' },
          to: { height: 0 },
        },
      },
      animation: {
        'accordion-down': 'accordion-down 0.2s ease-out',
        'accordion-up': 'accordion-up 0.2s ease-out',
      },
    },
  },
  plugins: [require('tailwindcss-animate')],
}

CSS Variables

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 246 243 238; /* off-white */
    --foreground: 59 64 49; /* deep-pine */
    --card: 255 255 255;
    --card-foreground: 59 64 49;
    --popover: 255 255 255;
    --popover-foreground: 59 64 49;
    --primary: 138 154 91; /* moss-green */
    --primary-foreground: 255 255 255;
    --secondary: 217 203 163; /* warm-sand */
    --secondary-foreground: 59 64 49;
    --muted: 217 203 163;
    --muted-foreground: 59 64 49;
    --accent: 198 134 66; /* soft-clay */
    --accent-foreground: 255 255 255;
    --destructive: 220 38 38;
    --destructive-foreground: 255 255 255;
    --border: 217 203 163;
    --input: 217 203 163;
    --ring: 138 154 91;
    --radius: 16px;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground font-body;
  }
  h1, h2, h3, h4, h5, h6 {
    @apply font-heading;
  }
}

Core UI Components

Button Component

// components/ui/button/button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "inline-flex items-center justify-center whitespace-nowrap rounded-2xl text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-moss-green text-white hover:bg-moss-green-600 shadow-sm hover:shadow-md",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-warm-sand bg-background hover:bg-warm-sand/10 hover:text-accent-foreground",
        secondary: "bg-warm-sand text-deep-pine hover:bg-warm-sand-400 shadow-sm hover:shadow-md",
        accent: "bg-soft-clay text-white hover:bg-soft-clay-600 shadow-sm hover:shadow-md",
        ghost: "hover:bg-warm-sand/20 hover:text-accent-foreground",
        link: "text-moss-green underline-offset-4 hover:underline",
      },
      size: {
        default: "h-11 px-6 py-3 min-w-[120px]",
        sm: "h-9 px-4 py-2 min-w-[80px]",
        lg: "h-13 px-8 py-4 text-base min-w-[140px]",
        icon: "h-11 w-11",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button"
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"

export { Button, buttonVariants }
Usage Examples:
// Primary action
<Button>Add Animal</Button>

// Secondary action
<Button variant="secondary">Cancel</Button>

// Call-to-action
<Button variant="accent" size="lg">Upgrade Plan</Button>

// Icon button
<Button variant="ghost" size="icon">
  <Edit className="h-4 w-4" />
</Button>

// Link-style button
<Button variant="link">View Details</Button>

Form Components

// components/ui/input/input.tsx
import * as React from "react"
import { cn } from "@/lib/utils"

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-11 w-full rounded-2xl border border-warm-sand bg-background px-4 py-3 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-moss-green focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
          className
        )}
        ref={ref}
        {...props}
      />
    )
  }
)
Input.displayName = "Input"

// components/ui/label/label.tsx
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const labelVariants = cva(
  "text-sm font-medium text-deep-pine leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)

const Label = React.forwardRef<
  React.ElementRef<typeof LabelPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
    VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
  <LabelPrimitive.Root
    ref={ref}
    className={cn(labelVariants(), className)}
    {...props}
  />
))
Label.displayName = LabelPrimitive.Root.displayName

// Form field wrapper
export function FormField({
  label,
  children,
  error,
  hint,
  required = false
}: {
  label: string
  children: React.ReactNode
  error?: string
  hint?: string
  required?: boolean
}) {
  return (
    <div className="space-y-2">
      <Label className="block">
        {label}
        {required && <span className="text-destructive ml-1">*</span>}
      </Label>
      {children}
      {hint && (
        <p className="text-xs text-muted-foreground">{hint}</p>
      )}
      {error && (
        <p className="text-xs text-destructive flex items-center">
          <AlertCircle className="w-3 h-3 mr-1" />
          {error}
        </p>
      )}
    </div>
  )
}

export { Input, Label }
Usage:
<FormField
  label="Animal Name"
  required
  hint="Choose a unique name for identification"
  error={errors.name}
>
  <Input
    placeholder="Enter animal name"
    value={name}
    onChange={(e) => setName(e.target.value)}
  />
</FormField>
// components/ui/select/select.tsx
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils"

const Select = SelectPrimitive.Root

const SelectGroup = SelectPrimitive.Group

const SelectValue = SelectPrimitive.Value

const SelectTrigger = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Trigger
    ref={ref}
    className={cn(
      "flex h-11 w-full items-center justify-between rounded-2xl border border-warm-sand bg-background px-4 py-3 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-moss-green focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
      className
    )}
    {...props}
  >
    {children}
    <SelectPrimitive.Icon asChild>
      <ChevronDown className="h-4 w-4 opacity-50" />
    </SelectPrimitive.Icon>
  </SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName

const SelectScrollUpButton = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
  <SelectPrimitive.ScrollUpButton
    ref={ref}
    className={cn(
      "flex cursor-default items-center justify-center py-1",
      className
    )}
    {...props}
  >
    <ChevronUp className="h-4 w-4" />
  </SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName

const SelectScrollDownButton = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
  <SelectPrimitive.ScrollDownButton
    ref={ref}
    className={cn(
      "flex cursor-default items-center justify-center py-1",
      className
    )}
    {...props}
  >
    <ChevronDown className="h-4 w-4" />
  </SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName =
  SelectPrimitive.ScrollDownButton.displayName

const SelectContent = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
  <SelectPrimitive.Portal>
    <SelectPrimitive.Content
      ref={ref}
      className={cn(
        "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-2xl border border-warm-sand bg-white text-deep-pine shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
        position === "popper" &&
          "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
        className
      )}
      position={position}
      {...props}
    >
      <SelectScrollUpButton />
      <SelectPrimitive.Viewport
        className={cn(
          "p-1",
          position === "popper" &&
            "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
        )}
      >
        {children}
      </SelectPrimitive.Viewport>
      <SelectScrollDownButton />
    </SelectPrimitive.Content>
  </SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName

const SelectLabel = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
  <SelectPrimitive.Label
    ref={ref}
    className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
    {...props}
  />
))
SelectLabel.displayName = SelectPrimitive.Label.displayName

const SelectItem = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
  <SelectPrimitive.Item
    ref={ref}
    className={cn(
      "relative flex w-full cursor-default select-none items-center rounded-xl py-2 pl-8 pr-2 text-sm outline-none focus:bg-warm-sand/20 focus:text-deep-pine data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className
    )}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <SelectPrimitive.ItemIndicator>
        <Check className="h-4 w-4" />
      </SelectPrimitive.ItemIndicator>
    </span>

    <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
  </SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName

const SelectSeparator = React.forwardRef<
  React.ElementRef<typeof SelectPrimitive.Separator>,
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
  <SelectPrimitive.Separator
    ref={ref}
    className={cn("-mx-1 my-1 h-px bg-warm-sand", className)}
    {...props}
  />
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName

export {
  Select,
  SelectGroup,
  SelectValue,
  SelectTrigger,
  SelectContent,
  SelectLabel,
  SelectItem,
  SelectSeparator,
  SelectScrollUpButton,
  SelectScrollDownButton,
}
Usage:
<FormField label="Species" required>
  <Select value={species} onValueChange={setSpecies}>
    <SelectTrigger>
      <SelectValue placeholder="Select species..." />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="ball-python">Ball Python (Python regius)</SelectItem>
      <SelectItem value="corn-snake">Corn Snake (Pantherophis guttatus)</SelectItem>
      <SelectItem value="leopard-gecko">Leopard Gecko (Eublepharis macularius)</SelectItem>
    </SelectContent>
  </Select>
</FormField>

Dialog Components


Specialized Components

Animal Components

// components/specialized/animal/animal-card.tsx
import * as React from "react"
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Edit, Eye } from "lucide-react"
import { cn } from "@/lib/utils"

interface AnimalCardProps {
  animal: {
    id: string
    name: string
    species: string
    sex: 'male' | 'female' | 'unknown'
    status: 'available' | 'sold' | 'reserved' | 'breeding'
    hatchDate: string
    weight?: number
    photos: string[]
    genetics?: string
  }
  onEdit?: () => void
  onView?: () => void
  className?: string
}

export function AnimalCard({
  animal,
  onEdit,
  onView,
  className
}: AnimalCardProps) {
  const statusColors = {
    available: 'bg-moss-green text-white',
    sold: 'bg-gray-500 text-white',
    reserved: 'bg-yellow-500 text-white',
    breeding: 'bg-soft-clay text-white'
  }

  return (
    <Card className={cn("hover:shadow-xl transition-shadow duration-300", className)}>
      <CardHeader className="space-y-4">
        <div className="flex items-start justify-between">
          <div>
            <h4 className="text-lg font-bold text-deep-pine font-heading">
              {animal.name}
            </h4>
            <p className="text-sm text-muted-foreground">
              {animal.species}{animal.sex}{animal.hatchDate}
            </p>
          </div>
          <Badge className={statusColors[animal.status]}>
            {animal.status}
          </Badge>
        </div>

        {animal.photos[0] && (
          <Avatar className="w-full h-48 rounded-2xl">
            <AvatarImage
              src={animal.photos[0]}
              alt={animal.name}
              className="object-cover"
            />
            <AvatarFallback className="rounded-2xl text-2xl">
              {animal.name.slice(0, 2).toUpperCase()}
            </AvatarFallback>
          </Avatar>
        )}
      </CardHeader>

      <CardContent>
        <div className="grid grid-cols-2 gap-4 text-sm">
          <div>
            <span className="text-muted-foreground">Hatch Date:</span>
            <span className="text-deep-pine ml-2">{animal.hatchDate}</span>
          </div>
          {animal.weight && (
            <div>
              <span className="text-muted-foreground">Weight:</span>
              <span className="text-deep-pine ml-2">{animal.weight}g</span>
            </div>
          )}
          {animal.genetics && (
            <div className="col-span-2">
              <span className="text-muted-foreground">Genetics:</span>
              <span className="text-deep-pine ml-2">{animal.genetics}</span>
            </div>
          )}
        </div>
      </CardContent>

      <CardFooter className="gap-3">
        {onView && (
          <Button variant="default" size="sm" onClick={onView} className="flex-1">
            <Eye className="w-4 h-4 mr-2" />
            View Details
          </Button>
        )}
        {onEdit && (
          <Button variant="secondary" size="sm" onClick={onEdit} className="flex-1">
            <Edit className="w-4 h-4 mr-2" />
            Edit
          </Button>
        )}
      </CardFooter>
    </Card>
  )
}

Pedigree Components

// components/specialized/pedigree/pedigree-tree.tsx
import * as React from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Download, Eye } from "lucide-react"
import { cn } from "@/lib/utils"

interface PedigreeAnimal {
  id: string
  name: string
  species: string
  genetics?: string
  verification: 'verified' | 'community' | 'self-reported' | 'purple-ribbon'
}

interface PedigreeTreeProps {
  centerAnimal: PedigreeAnimal
  sire?: PedigreeAnimal
  dam?: PedigreeAnimal
  generations?: number
  onViewFull?: () => void
  onGenerateCertificate?: () => void
  className?: string
}

export function PedigreeTree({
  centerAnimal,
  sire,
  dam,
  generations = 3,
  onViewFull,
  onGenerateCertificate,
  className
}: PedigreeTreeProps) {
  const verificationColors = {
    'verified': 'bg-moss-green text-white',
    'community': 'bg-yellow-500 text-white',
    'self-reported': 'bg-gray-400 text-white',
    'purple-ribbon': 'bg-purple-600 text-white'
  }

  const AnimalNode = ({ animal, className }: { animal: PedigreeAnimal, className?: string }) => (
    <div className={cn(
      "border rounded-2xl p-4 text-center min-w-[180px]",
      className
    )}>
      <h5 className="font-medium text-deep-pine font-heading text-sm mb-1">
        {animal.name}
      </h5>
      <p className="text-xs text-muted-foreground mb-2">
        {animal.genetics || 'Unknown genetics'}
      </p>
      <Badge
        size="sm"
        className={verificationColors[animal.verification]}
      >
        {animal.verification.replace('-', ' ')}
      </Badge>
    </div>
  )

  return (
    <Card className={cn("relative", className)}>
      <CardHeader>
        <CardTitle className="flex items-center justify-between">
          <span>{generations}-Generation Pedigree</span>
          <div className="flex gap-2">
            {onViewFull && (
              <Button variant="ghost" size="sm" onClick={onViewFull}>
                <Eye className="w-4 h-4 mr-2" />
                View Full
              </Button>
            )}
            {onGenerateCertificate && (
              <Button variant="default" size="sm" onClick={onGenerateCertificate}>
                <Download className="w-4 h-4 mr-2" />
                Certificate
              </Button>
            )}
          </div>
        </CardTitle>
      </CardHeader>

      <CardContent>
        <div className="pedigree-layout space-y-8">
          {/* Center Animal */}
          <div className="flex justify-center">
            <AnimalNode
              animal={centerAnimal}
              className="bg-moss-green/10 border-moss-green border-2"
            />
          </div>

          {/* Parents Generation */}
          {(sire || dam) && (
            <div className="grid grid-cols-2 gap-8">
              {sire ? (
                <div className="flex justify-center">
                  <AnimalNode
                    animal={sire}
                    className="bg-blue-50 border-blue-200"
                  />
                </div>
              ) : (
                <div className="flex justify-center">
                  <div className="border-dashed border-2 border-gray-300 rounded-2xl p-4 text-center min-w-[180px]">
                    <p className="text-sm text-muted-foreground">Unknown Sire</p>
                  </div>
                </div>
              )}

              {dam ? (
                <div className="flex justify-center">
                  <AnimalNode
                    animal={dam}
                    className="bg-pink-50 border-pink-200"
                  />
                </div>
              ) : (
                <div className="flex justify-center">
                  <div className="border-dashed border-2 border-gray-300 rounded-2xl p-4 text-center min-w-[180px]">
                    <p className="text-sm text-muted-foreground">Unknown Dam</p>
                  </div>
                </div>
              )}
            </div>
          )}

          {/* Connection Lines (simplified) */}
          <svg className="absolute inset-0 pointer-events-none" style={{zIndex: -1}}>
            <line x1="50%" y1="120px" x2="25%" y2="200px" stroke="#8A9A5B" strokeWidth="2" />
            <line x1="50%" y1="120px" x2="75%" y2="200px" stroke="#8A9A5B" strokeWidth="2" />
          </svg>
        </div>
      </CardContent>
    </Card>
  )
}

Toast Notifications

// components/ui/toast/toast.tsx
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"

const ToastProvider = ToastPrimitives.Provider

const ToastViewport = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Viewport>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Viewport
    ref={ref}
    className={cn(
      "fixed top-0 right-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:top-auto sm:right-0 sm:flex-col md:max-w-[420px]",
      className
    )}
    {...props}
  />
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName

const toastVariants = cva(
  "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-r-2xl border border-l-4 p-4 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
  {
    variants: {
      variant: {
        default: "border-l-moss-green bg-white text-deep-pine",
        destructive: "border-l-red-500 bg-white text-deep-pine",
        success: "border-l-green-500 bg-white text-deep-pine",
        warning: "border-l-yellow-500 bg-white text-deep-pine",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

const Toast = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Root>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
    VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
  return (
    <ToastPrimitives.Root
      ref={ref}
      className={cn(toastVariants({ variant }), className)}
      {...props}
    />
  )
})
Toast.displayName = ToastPrimitives.Root.displayName

const ToastAction = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Action>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Action
    ref={ref}
    className={cn(
      "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
      className
    )}
    {...props}
  />
))
ToastAction.displayName = ToastPrimitives.Action.displayName

const ToastClose = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Close>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Close
    ref={ref}
    className={cn(
      "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
      className
    )}
    toast-close=""
    {...props}
  >
    <X className="h-4 w-4" />
  </ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName

const ToastTitle = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Title>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Title
    ref={ref}
    className={cn("text-sm font-semibold", className)}
    {...props}
  />
))
ToastTitle.displayName = ToastPrimitives.Title.displayName

const ToastDescription = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Description>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Description
    ref={ref}
    className={cn("text-sm opacity-90", className)}
    {...props}
  />
))
ToastDescription.displayName = ToastPrimitives.Description.displayName

type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>

type ToastActionElement = React.ReactElement<typeof ToastAction>

export {
  type ToastProps,
  type ToastActionElement,
  ToastProvider,
  ToastViewport,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastClose,
  ToastAction,
}
Usage with Hook:
// hooks/use-toast.ts
import * as React from "react"
import { ToastActionElement, type ToastProps } from "@/components/ui/toast"

const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000

type ToasterToast = ToastProps & {
  id: string
  title?: React.ReactNode
  description?: React.ReactNode
  action?: ToastActionElement
}

const actionTypes = {
  ADD_TOAST: "ADD_TOAST",
  UPDATE_TOAST: "UPDATE_TOAST",
  DISMISS_TOAST: "DISMISS_TOAST",
  REMOVE_TOAST: "REMOVE_TOAST",
} as const

let count = 0

function genId() {
  count = (count + 1) % Number.MAX_SAFE_INTEGER
  return count.toString()
}

type ActionType = typeof actionTypes

type Action =
  | {
      type: ActionType["ADD_TOAST"]
      toast: ToasterToast
    }
  | {
      type: ActionType["UPDATE_TOAST"]
      toast: Partial<ToasterToast>
    }
  | {
      type: ActionType["DISMISS_TOAST"]
      toastId?: ToasterToast["id"]
    }
  | {
      type: ActionType["REMOVE_TOAST"]
      toastId?: ToasterToast["id"]
    }

interface State {
  toasts: ToasterToast[]
}

const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()

const addToRemoveQueue = (toastId: string) => {
  if (toastTimeouts.has(toastId)) {
    return
  }

  const timeout = setTimeout(() => {
    toastTimeouts.delete(toastId)
    dispatch({
      type: "REMOVE_TOAST",
      toastId: toastId,
    })
  }, TOAST_REMOVE_DELAY)

  toastTimeouts.set(toastId, timeout)
}

export const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case "ADD_TOAST":
      return {
        ...state,
        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
      }

    case "UPDATE_TOAST":
      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === action.toast.id ? { ...t, ...action.toast } : t
        ),
      }

    case "DISMISS_TOAST": {
      const { toastId } = action

      if (toastId) {
        addToRemoveQueue(toastId)
      } else {
        state.toasts.forEach((toast) => {
          addToRemoveQueue(toast.id)
        })
      }

      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === toastId || toastId === undefined
            ? {
                ...t,
                open: false,
              }
            : t
        ),
      }
    }
    case "REMOVE_TOAST":
      if (action.toastId === undefined) {
        return {
          ...state,
          toasts: [],
        }
      }
      return {
        ...state,
        toasts: state.toasts.filter((t) => t.id !== action.toastId),
      }
  }
}

const listeners: Array<(state: State) => void> = []

let memoryState: State = { toasts: [] }

function dispatch(action: Action) {
  memoryState = reducer(memoryState, action)
  listeners.forEach((listener) => {
    listener(memoryState)
  })
}

type Toast = Omit<ToasterToast, "id">

function toast({ ...props }: Toast) {
  const id = genId()

  const update = (props: ToasterToast) =>
    dispatch({
      type: "UPDATE_TOAST",
      toast: { ...props, id },
    })
  const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })

  dispatch({
    type: "ADD_TOAST",
    toast: {
      ...props,
      id,
      open: true,
      onOpenChange: (open) => {
        if (!open) dismiss()
      },
    },
  })

  return {
    id: id,
    dismiss,
    update,
  }
}

function useToast() {
  const [state, setState] = React.useState<State>(memoryState)

  React.useEffect(() => {
    listeners.push(setState)
    return () => {
      const index = listeners.indexOf(setState)
      if (index > -1) {
        listeners.splice(index, 1)
      }
    }
  }, [state])

  return {
    ...state,
    toast,
    dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
  }
}

export { useToast, toast }
Usage:
import { useToast } from "@/hooks/use-toast"

function MyComponent() {
  const { toast } = useToast()

  return (
    <Button
      onClick={() => {
        toast({
          variant: "success",
          title: "Animal Added Successfully",
          description: "Luna has been added to your registry",
        })
      }}
    >
      Add Animal
    </Button>
  )
}

Component Testing

Testing Setup

// components/ui/button/button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import { Button } from './button'

describe('Button', () => {
  it('renders with correct text', () => {
    render(<Button>Add Animal</Button>)
    expect(screen.getByRole('button', { name: 'Add Animal' })).toBeInTheDocument()
  })

  it('calls onClick handler when clicked', () => {
    const handleClick = vi.fn()
    render(<Button onClick={handleClick}>Click me</Button>)

    fireEvent.click(screen.getByRole('button'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })

  it('applies variant styles correctly', () => {
    render(<Button variant="accent">Upgrade</Button>)
    const button = screen.getByRole('button')
    expect(button).toHaveClass('bg-soft-clay')
  })

  it('applies size styles correctly', () => {
    render(<Button size="lg">Large Button</Button>)
    const button = screen.getByRole('button')
    expect(button).toHaveClass('h-13')
  })

  it('forwards ref correctly', () => {
    const ref = React.createRef<HTMLButtonElement>()
    render(<Button ref={ref}>Button</Button>)
    expect(ref.current).toBeInstanceOf(HTMLButtonElement)
  })

  it('works as a child component with asChild', () => {
    render(
      <Button asChild>
        <a href="/test">Link Button</a>
      </Button>
    )
    const link = screen.getByRole('link')
    expect(link).toHaveClass('bg-moss-green')
  })
})
// components/ui/button/button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './button'
import { Download, Edit, Heart, Plus } from 'lucide-react'

const meta: Meta<typeof Button> = {
  title: 'UI/Button',
  component: Button,
  parameters: {
    layout: 'centered',
    docs: {
      description: {
        component: 'A versatile button component built on Radix UI Slot with Forest & Stone design system styling.'
      }
    }
  },
  argTypes: {
    variant: {
      control: 'select',
      options: ['default', 'destructive', 'outline', 'secondary', 'accent', 'ghost', 'link']
    },
    size: {
      control: 'select',
      options: ['default', 'sm', 'lg', 'icon']
    },
    asChild: {
      control: 'boolean',
      description: 'Render as a different element using Radix UI Slot'
    }
  }
}

export default meta
type Story = StoryObj<typeof Button>

export const Default: Story = {
  args: {
    children: 'Add Animal',
    variant: 'default'
  }
}

export const Secondary: Story = {
  args: {
    children: 'Cancel',
    variant: 'secondary'
  }
}

export const Accent: Story = {
  args: {
    children: 'Upgrade Plan',
    variant: 'accent'
  }
}

export const WithIcon: Story = {
  args: {
    children: (
      <>
        <Plus className="w-4 h-4 mr-2" />
        Add Animal
      </>
    ),
    variant: 'default'
  }
}

export const IconOnly: Story = {
  args: {
    children: <Edit className="w-4 h-4" />,
    variant: 'ghost',
    size: 'icon'
  }
}

export const AllVariants: Story = {
  render: () => (
    <div className="flex flex-wrap gap-4">
      <Button variant="default">Default</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="accent">Accent</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="link">Link</Button>
    </div>
  )
}

export const AllSizes: Story = {
  render: () => (
    <div className="flex items-center gap-4">
      <Button size="sm">Small</Button>
      <Button size="default">Default</Button>
      <Button size="lg">Large</Button>
      <Button size="icon">
        <Heart className="w-4 h-4" />
      </Button>
    </div>
  )
}

export const AsLink: Story = {
  args: {
    asChild: true,
    children: <a href="#" onClick={(e) => e.preventDefault()}>I'm actually a link!</a>
  }
}

Integration with @reptidex/core

State Management

// Integration with @reptidex/core stores
import { useAnimalStore } from '@reptidex/core/stores/animal-store'
import { useAuthStore } from '@reptidex/core/stores/auth-store'
import { AnimalCard } from '@reptidex/ui/components/specialized/animal'

function AnimalRegistry() {
  const { animals, loading, addAnimal, updateAnimal } = useAnimalStore()
  const { user } = useAuthStore()

  if (loading) {
    return <AnimalRegistrySkeleton />
  }

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
      {animals.map((animal) => (
        <AnimalCard
          key={animal.id}
          animal={animal}
          onEdit={() => updateAnimal(animal.id)}
          onView={() => /* navigate to animal detail */}
        />
      ))}
    </div>
  )
}

API Integration

// Integration with @reptidex/core API clients
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { animalApi } from '@reptidex/core/api/animal-api'
import { useToast } from '@reptidex/ui/hooks/use-toast'
import { Button } from '@reptidex/ui/components/ui/button'

function useAnimalActions() {
  const queryClient = useQueryClient()
  const { toast } = useToast()

  const createAnimalMutation = useMutation({
    mutationFn: animalApi.create,
    onSuccess: () => {
      queryClient.invalidateQueries(['animals'])
      toast({
        variant: "success",
        title: "Animal Added Successfully",
        description: "The animal has been added to your registry",
      })
    },
    onError: (error) => {
      toast({
        variant: "destructive",
        title: "Error",
        description: error.message,
      })
    }
  })

  return {
    createAnimal: createAnimalMutation.mutate,
    isCreating: createAnimalMutation.isPending
  }
}

function AddAnimalButton() {
  const { createAnimal, isCreating } = useAnimalActions()

  return (
    <Button
      onClick={() => createAnimal(animalData)}
      disabled={isCreating}
    >
      {isCreating ? 'Adding...' : 'Add Animal'}
    </Button>
  )
}

Package Exports

Main Export File

// packages/ui/src/index.ts

// Base UI Components
export * from './components/ui/button'
export * from './components/ui/input'
export * from './components/ui/label'
export * from './components/ui/select'
export * from './components/ui/dialog'
export * from './components/ui/card'
export * from './components/ui/badge'
export * from './components/ui/avatar'
export * from './components/ui/separator'
export * from './components/ui/tabs'
export * from './components/ui/accordion'
export * from './components/ui/toast'
export * from './components/ui/progress'
export * from './components/ui/switch'
export * from './components/ui/checkbox'

// Form Components
export * from './components/forms/form-field'
export * from './components/forms/animal-form'
export * from './components/forms/breeding-form'

// Layout Components
export * from './components/layout/header'
export * from './components/layout/sidebar'
export * from './components/layout/breadcrumb'

// Specialized Components
export * from './components/specialized/animal/animal-card'
export * from './components/specialized/animal/animal-table'
export * from './components/specialized/pedigree/pedigree-tree'
export * from './components/specialized/pedigree/genetic-prediction'
export * from './components/specialized/marketplace/listing-card'

// Hooks
export * from './hooks/use-toast'

// Utilities
export * from './lib/utils'
export * from './lib/cn'

// Types
export type * from './types'

Development Workflow

Local Development

# In the @reptidex/ui package
npm run dev          # Start Storybook development server
npm run build        # Build package for production
npm run test         # Run component tests
npm run test:watch   # Run tests in watch mode
npm run lint         # Lint TypeScript and styles
npm run type-check   # TypeScript type checking

# In consuming applications
npm run dev          # Start Vite dev server with HMR
npm run build        # Build application
npm run preview      # Preview built application

CI/CD Integration

# .github/workflows/ui-package.yml
name: UI Package CI

on:
  push:
    paths:
      - 'packages/ui/**'
  pull_request:
    paths:
      - 'packages/ui/**'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci
        working-directory: packages/ui

      - name: Run type checking
        run: npm run type-check
        working-directory: packages/ui

      - name: Run tests
        run: npm run test:ci
        working-directory: packages/ui

      - name: Run linting
        run: npm run lint
        working-directory: packages/ui

      - name: Build package
        run: npm run build
        working-directory: packages/ui

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: packages/ui/coverage/lcov.info
Production Ready: This component library is architected for scale with Radix UI accessibility, Tailwind CSS performance, comprehensive testing, and seamless integration with the reptidex platform ecosystem.