# Product Customizer Form

> A hero with a product customizer form for options like size and color. Lets shoppers configure their choice and preview it before checkout.

A shadcn/ui block from the [@shadcn-ui-blocks](https://www.shadcn-ui-blocks.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcn-ui-blocks/marketing-hero-forms-product-customizer-form).

- Page, with a live preview: https://shadcn.exchange/r/shadcn-ui-blocks/marketing-hero-forms-product-customizer-form
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcn-ui-blocks/marketing-hero-forms-product-customizer-form-light-fd3b376cdd363da2.png
- Registry item: https://www.shadcn-ui-blocks.com/r/marketing-hero-forms-product-customizer-form.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://www.shadcn-ui-blocks.com/r/marketing-hero-forms-product-customizer-form.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `class-variance-authority`, `lucide-react`, `radix-ui`

## Files

### `components/blocks/marketing/hero-forms/product-customizer-form.tsx`

```tsx
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Slider } from '@/components/ui/slider';
import { Switch } from '@/components/ui/switch';
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@/components/ui/card';
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '@/components/ui/tooltip';
import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator';
import {
  InfoIcon,
  CheckIcon,
  ShoppingCartIcon,
  Share2Icon,
  HeartIcon,
} from 'lucide-react';

export default function HeroFormProductCustomizer() {
  // Product state
  const [product, setProduct] = useState({
    name: 'Custom Ergonomic Chair',
    basePrice: 299,
    color: 'black',
    material: 'fabric',
    armrests: true,
    headrest: false,
    lumbarSupport: true,
    height: 5, // 1-10 scale
    recline: 3, // 1-5 scale
    quantity: 1,
  });

  // UI state
  const [totalPrice, setTotalPrice] = useState(0);
  const [orderComplete, setOrderComplete] = useState(false);
  const [isLoading, setIsLoading] = useState(false);

  // Calculate price based on options
  useEffect(() => {
    let price = product.basePrice;

    // Add costs for premium options
    if (product.material === 'leather') price += 100;
    else if (product.material === 'mesh') price += 50;

    if (product.headrest) price += 45;
    if (product.lumbarSupport) price += 35;

    // Multiply by quantity
    price *= product.quantity;

    setTotalPrice(price);
  }, [product]);

  // Get image URL based on product configuration
  const getProductImageUrl = () => {
    let baseImage = '';

    // Select base image based on color and material
    if (product.material === 'leather') {
      if (product.color === 'black') {
        baseImage =
          'https://images.unsplash.com/photo-1580480055273-228ff5388ef8?q=80&w=2000&auto=format&fit=crop';
      } else if (product.color === 'brown') {
        baseImage =
          'https://images.unsplash.com/photo-1592078615290-033ee584e267?q=80&w=1964&auto=format&fit=crop';
      } else {
        baseImage =
          'https://images.unsplash.com/photo-1505843490701-5c4b83790555?q=80&w=1918&auto=format&fit=crop';
      }
    } else if (product.material === 'mesh') {
      baseImage =
        'https://images.unsplash.com/photo-1596079890701-dd42edf0b7d4?q=80&w=2071&auto=format&fit=crop';
    } else {
      // Fabric chair
      if (product.color === 'black') {
        baseImage =
          'https://images.unsplash.com/photo-1505843513577-22bb7d21e455?q=80&w=2069&auto=format&fit=crop';
      } else if (product.color === 'blue') {
        baseImage =
          'https://images.unsplash.com/photo-1586023492125-27b2c045efd7?q=80&w=2158&auto=format&fit=crop';
      } else {
        baseImage =
          'https://images.unsplash.com/photo-1567538096630-e0c55bd6374c?q=80&w=1974&auto=format&fit=crop';
      }
    }

    return baseImage;
  };

  // Handle form submission
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    setIsLoading(true);

    // Simulate API call
    setTimeout(() => {
      setIsLoading(false);
      setOrderComplete(true);
    }, 1500);
  };

  return (
    <>
      {/* Hero */}
      <div className="relative overflow-hidden bg-gradient-to-b from-slate-50 to-white dark:from-slate-950 dark:to-slate-900">
        <div className="bg-grid-slate-200 dark:bg-grid-slate-800/20 absolute inset-0 bg-[size:24px_24px] opacity-10"></div>

        <div className="relative z-10 container mx-auto px-4 py-16 md:px-6 lg:py-24 2xl:max-w-[1400px]">
          <div className="mx-auto max-w-6xl">
            <div className="mb-12 text-center">
              <Badge
                variant="outline"
                className="mb-4 px-3 py-1 text-sm font-medium"
              >
                Customizable Product
              </Badge>
              <h1 className="mb-4 text-4xl font-bold tracking-tight sm:text-5xl">
                Design Your Perfect Chair
              </h1>
              <p className="text-muted-foreground mx-auto max-w-2xl text-xl">
                Customize every detail to create a chair that matches your exact
                needs, comfort preferences, and style.
              </p>
            </div>

            {!orderComplete ? (
              <div className="grid gap-8 lg:grid-cols-2 lg:gap-12">
                {/* Product Preview */}
                <div className="order-2 lg:order-1">
                  <Card className="flex h-full flex-col overflow-hidden p-0">
                    <div className="relative aspect-square w-full overflow-hidden bg-slate-100 dark:bg-slate-800">
                      <img
                        src={getProductImageUrl()}
                        alt={`${product.color} ${product.material} chair`}
                        className="object-cover"
                      />
                      <div className="absolute top-4 right-4 space-x-2">
                        <Button
                          variant="outline"
                          size="icon"
                          className="rounded-full bg-white/80 backdrop-blur-sm hover:bg-white dark:bg-slate-900/80 dark:hover:bg-slate-900"
                        >
                          <HeartIcon className="h-4 w-4" />
                          <span className="sr-only">Add to favorites</span>
                        </Button>
                        <Button
                          variant="outline"
                          size="icon"
                          className="rounded-full bg-white/80 backdrop-blur-sm hover:bg-white dark:bg-slate-900/80 dark:hover:bg-slate-900"
                        >
                          <Share2Icon className="h-4 w-4" />
                          <span className="sr-only">Share</span>
                        </Button>
                      </div>
                    </div>
                    <CardContent className="flex-grow p-6">
                      <div className="mb-4 flex items-start justify-between">
                        <div>
                          <h2 className="text-2xl font-bold">{product.name}</h2>
                          <p className="text-muted-foreground">
                            Your custom configuration
                          </p>
                        </div>
                        <div className="text-right">
                          <div className="text-2xl font-bold">
                            ${totalPrice}
                          </div>
                          <p className="text-muted-foreground text-xs">
                            Free shipping
                          </p>
                        </div>
                      </div>

                      <div className="space-y-4">
                        <div>
                          <h3 className="mb-1 text-sm font-medium">
                            Selected Options
                          </h3>
                          <div className="grid grid-cols-2 gap-x-6 gap-y-2 text-sm">
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Color:
                              </span>
                              <span className="capitalize">
                                {product.color}
                              </span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Material:
                              </span>
                              <span className="capitalize">
                                {product.material}
                              </span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Armrests:
                              </span>
                              <span>{product.armrests ? 'Yes' : 'No'}</span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Headrest:
                              </span>
                              <span>{product.headrest ? 'Yes' : 'No'}</span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Lumbar Support:
                              </span>
                              <span>
                                {product.lumbarSupport ? 'Yes' : 'No'}
                              </span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Height:
                              </span>
                              <span>{product.height}/10</span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Recline:
                              </span>
                              <span>{product.recline}/5</span>
                            </div>
                            <div className="flex justify-between">
                              <span className="text-muted-foreground">
                                Quantity:
                              </span>
                              <span>{product.quantity}</span>
                            </div>
                          </div>
                        </div>

                        <Separator />

                        <div>
                          <h3 className="mb-2 text-sm font-medium">
                            Product Features
                          </h3>
                          <ul className="text-muted-foreground space-y-1 text-sm">
                            <li className="flex items-start">
                              <CheckIcon className="mt-0.5 mr-2 h-4 w-4 text-green-500" />
                              <span>5-year warranty on all parts</span>
                            </li>
                            <li className="flex items-start">
                              <CheckIcon className="mt-0.5 mr-2 h-4 w-4 text-green-500" />
                              <span>Easy assembly with included tools</span>
                            </li>
                            <li className="flex items-start">
                              <CheckIcon className="mt-0.5 mr-2 h-4 w-4 text-green-500" />
                              <span>Certified eco-friendly materials</span>
                            </li>
                            <li className="flex items-start">
                              <CheckIcon className="mt-0.5 mr-2 h-4 w-4 text-green-500" />
                              <span>30-day risk-free trial</span>
                            </li>
                          </ul>
                        </div>
                      </div>
                    </CardContent>
                  </Card>
                </div>

                {/* Customization Options */}
                <div className="order-1 lg:order-2">
                  <Card className="h-full">
                    <CardHeader>
                      <CardTitle>Customize Your Chair</CardTitle>
                      <CardDescription>
                        Select your preferred options and features
                      </CardDescription>
                    </CardHeader>
                    <CardContent>
                      <form onSubmit={handleSubmit} className="space-y-6">
                        {/* Color Selection */}
                        <div className="space-y-3">
                          <Label>Color</Label>
                          <RadioGroup
                            value={product.color}
                            onValueChange={(value) =>
                              setProduct({ ...product, color: value })
                            }
                            className="flex gap-3"
                          >
                            <div>
                              <RadioGroupItem
                                value="black"
                                id="color-black"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="color-black"
                                className={`flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-gray-900 ${
                                  product.color === 'black'
                                    ? 'ring-primary ring-2 ring-offset-2'
                                    : ''
                                }`}
                              >
                                {product.color === 'black' && (
                                  <CheckIcon className="h-4 w-4 text-white" />
                                )}
                              </Label>
                            </div>
                            <div>
                              <RadioGroupItem
                                value="blue"
                                id="color-blue"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="color-blue"
                                className={`flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-blue-600 ${
                                  product.color === 'blue'
                                    ? 'ring-primary ring-2 ring-offset-2'
                                    : ''
                                }`}
                              >
                                {product.color === 'blue' && (
                                  <CheckIcon className="h-4 w-4 text-white" />
                                )}
                              </Label>
                            </div>
                            <div>
                              <RadioGroupItem
                                value="brown"
                                id="color-brown"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="color-brown"
                                className={`flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-amber-800 ${
                                  product.color === 'brown'
                                    ? 'ring-primary ring-2 ring-offset-2'
                                    : ''
                                }`}
                              >
                                {product.color === 'brown' && (
                                  <CheckIcon className="h-4 w-4 text-white" />
                                )}
                              </Label>
                            </div>
                            <div>
                              <RadioGroupItem
                                value="gray"
                                id="color-gray"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="color-gray"
                                className={`flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-gray-400 ${
                                  product.color === 'gray'
                                    ? 'ring-primary ring-2 ring-offset-2'
                                    : ''
                                }`}
                              >
                                {product.color === 'gray' && (
                                  <CheckIcon className="h-4 w-4 text-white" />
                                )}
                              </Label>
                            </div>
                          </RadioGroup>
                        </div>

                        {/* Material Selection */}
                        <div className="space-y-3">
                          <div className="flex items-center justify-between">
                            <Label>Material</Label>
                            <TooltipProvider>
                              <Tooltip>
                                <TooltipTrigger asChild>
                                  <InfoIcon className="text-muted-foreground h-4 w-4" />
                                </TooltipTrigger>
                                <TooltipContent className="w-80 p-4">
                                  <p className="mb-1 font-medium">
                                    Material Details:
                                  </p>
                                  <ul className="space-y-1 text-sm">
                                    <li>
                                      <span className="font-medium">
                                        Fabric:
                                      </span>{' '}
                                      Breathable and comfortable, standard
                                      option.
                                    </li>
                                    <li>
                                      <span className="font-medium">Mesh:</span>{' '}
                                      Better airflow, ideal for warmer
                                      environments (+$50).
                                    </li>
                                    <li>
                                      <span className="font-medium">
                                        Leather:
                                      </span>{' '}
                                      Premium look and feel, more durable
                                      (+$100).
                                    </li>
                                  </ul>
                                </TooltipContent>
                              </Tooltip>
                            </TooltipProvider>
                          </div>
                          <RadioGroup
                            value={product.material}
                            onValueChange={(value) =>
                              setProduct({ ...product, material: value })
                            }
                            className="grid grid-cols-3 gap-3"
                          >
                            <div>
                              <RadioGroupItem
                                value="fabric"
                                id="material-fabric"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="material-fabric"
                                className={`bg-popover hover:bg-accent hover:text-accent-foreground flex h-20 flex-col items-center justify-center rounded-md border-2 p-2 ${
                                  product.material === 'fabric'
                                    ? 'border-primary'
                                    : 'border-muted'
                                }`}
                              >
                                <span className="text-sm font-medium">
                                  Fabric
                                </span>
                                <span className="text-muted-foreground text-xs">
                                  Standard
                                </span>
                              </Label>
                            </div>
                            <div>
                              <RadioGroupItem
                                value="mesh"
                                id="material-mesh"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="material-mesh"
                                className={`bg-popover hover:bg-accent hover:text-accent-foreground flex h-20 flex-col items-center justify-center rounded-md border-2 p-2 ${
                                  product.material === 'mesh'
                                    ? 'border-primary'
                                    : 'border-muted'
                                }`}
                              >
                                <span className="text-sm font-medium">
                                  Mesh
                                </span>
                                <span className="text-muted-foreground text-xs">
                                  +$50
                                </span>
                              </Label>
                            </div>
                            <div>
                              <RadioGroupItem
                                value="leather"
                                id="material-leather"
                                className="sr-only"
                              />
                              <Label
                                htmlFor="material-leather"
                                className={`bg-popover hover:bg-accent hover:text-accent-foreground flex h-20 flex-col items-center justify-center rounded-md border-2 p-2 ${
                                  product.material === 'leather'
                                    ? 'border-primary'
                                    : 'border-muted'
                                }`}
                              >
                                <span className="text-sm font-medium">
                                  Leather
                                </span>
                                <span className="text-muted-foreground text-xs">
                                  +$100
                                </span>
                              </Label>
                            </div>
                          </RadioGroup>
                        </div>

                        <Separator />

                        {/* Optional Features */}
                        <div className="space-y-4">
                          <h3 className="text-sm font-medium">Features</h3>

                          <div className="flex items-center justify-between">
                            <div className="space-y-0.5">
                              <Label htmlFor="armrests">Armrests</Label>
                              <div className="text-muted-foreground text-xs">
                                Adjustable height and width
                              </div>
                            </div>
                            <Switch
                              id="armrests"
                              checked={product.armrests}
                              onCheckedChange={(checked) =>
                                setProduct({ ...product, armrests: checked })
                              }
                            />
                          </div>

                          <div className="flex items-center justify-between">
                            <div className="space-y-0.5">
                              <Label htmlFor="headrest">Headrest</Label>
                              <div className="text-muted-foreground text-xs">
                                Additional head support (+$45)
                              </div>
                            </div>
                            <Switch
                              id="headrest"
                              checked={product.headrest}
                              onCheckedChange={(checked) =>
                                setProduct({ ...product, headrest: checked })
                              }
                            />
                          </div>

                          <div className="flex items-center justify-between">
                            <div className="space-y-0.5">
                              <Label htmlFor="lumbarSupport">
                                Lumbar Support
                              </Label>
                              <div className="text-muted-foreground text-xs">
                                Adjustable back support (+$35)
                              </div>
                            </div>
                            <Switch
                              id="lumbarSupport"
                              checked={product.lumbarSupport}
                              onCheckedChange={(checked) =>
                                setProduct({
                                  ...product,
                                  lumbarSupport: checked,
                                })
                              }
                            />
                          </div>
                        </div>

                        <Separator />

                        {/* Adjustments */}
                        <div className="space-y-4">
                          <h3 className="text-sm font-medium">Adjustments</h3>

                          <div className="space-y-3">
                            <div className="flex justify-between">
                              <Label htmlFor="height">Chair Height</Label>
                              <span className="text-muted-foreground text-sm">
                                {product.height}/10
                              </span>
                            </div>
                            <Slider
                              id="height"
                              min={1}
                              max={10}
                              step={1}
                              value={[product.height]}
                              onValueChange={(values) =>
                                setProduct({ ...product, height: values[0]! })
                              }
                            />
                            <div className="text-muted-foreground flex justify-between text-xs">
                              <span>Lower</span>
                              <span>Higher</span>
                            </div>
                          </div>

                          <div className="space-y-3">
                            <div className="flex justify-between">
                              <Label htmlFor="recline">Recline Tension</Label>
                              <span className="text-muted-foreground text-sm">
                                {product.recline}/5
                              </span>
                            </div>
                            <Slider
                              id="recline"
                              min={1}
                              max={5}
                              step={1}
                              value={[product.recline]}
                              onValueChange={(values) =>
                                setProduct({ ...product, recline: values[0]! })
                              }
                            />
                            <div className="text-muted-foreground flex justify-between text-xs">
                              <span>Soft</span>
                              <span>Firm</span>
                            </div>
                          </div>
                        </div>

                        <Separator />

                        {/* Quantity */}
                        <div className="space-y-3">
                          <Label htmlFor="quantity">Quantity</Label>
                          <div className="flex items-center">
                            <Button
                              type="button"
                              variant="outline"
                              size="icon"
                              className="h-8 w-8 rounded-r-none"
                              onClick={() =>
                                product.quantity > 1 &&
                                setProduct({
                                  ...product,
                                  quantity: product.quantity - 1,
                                })
                              }
                              disabled={product.quantity <= 1}
                            >
                              -
                            </Button>
                            <Input
                              id="quantity"
                              type="number"
                              min={1}
                              max={10}
                              className="h-8 w-16 [appearance:textfield] rounded-none text-center [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
                              value={product.quantity}
                              onChange={(e) => {
                                const value = parseInt(e.target.value);
                                if (
                                  !isNaN(value) &&
                                  value >= 1 &&
                                  value <= 10
                                ) {
                                  setProduct({ ...product, quantity: value });
                                }
                              }}
                            />
                            <Button
                              type="button"
                              variant="outline"
                              size="icon"
                              className="h-8 w-8 rounded-l-none"
                              onClick={() =>
                                product.quantity < 10 &&
                                setProduct({
                                  ...product,
                                  quantity: product.quantity + 1,
                                })
                              }
                              disabled={product.quantity >= 10}
                            >
                              +
                            </Button>
                          </div>
                        </div>

                        <Separator />

                        <div className="pt-4">
                          <div className="mb-4 flex items-center justify-between">
                            <div className="text-lg font-medium">Total</div>
                            <div className="text-2xl font-bold">
                              ${totalPrice}
                            </div>
                          </div>
                          <Button
                            type="submit"
                            className="w-full"
                            disabled={isLoading}
                          >
                            {isLoading ? (
                              <span className="flex items-center">
                                <svg
                                  className="mr-2 -ml-1 h-4 w-4 animate-spin text-white"
                                  xmlns="http://www.w3.org/2000/svg"
                                  fill="none"
                                  viewBox="0 0 24 24"
                                >
                                  <circle
                                    className="opacity-25"
                                    cx="12"
                                    cy="12"
                                    r="10"
                                    stroke="currentColor"
                                    strokeWidth="4"
                                  ></circle>
                                  <path
                                    className="opacity-75"
                                    fill="currentColor"
                                    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
                                  ></path>
                                </svg>
                                Processing...
                              </span>
                            ) : (
                              <span className="flex items-center">
                                <ShoppingCartIcon className="mr-2 h-4 w-4" />
                                Add to Cart
                              </span>
                            )}
                          </Button>
                        </div>
                      </form>
                    </CardContent>
                  </Card>
                </div>
              </div>
            ) : (
              <Card className="mx-auto max-w-xl">
                <CardHeader className="text-center">
                  <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/30">
                    <CheckIcon className="h-8 w-8 text-green-600 dark:text-green-400" />
                  </div>
                  <CardTitle className="text-2xl">
                    Order Added to Cart!
                  </CardTitle>
                  <CardDescription>
                    Your custom chair has been added to your cart.
                  </CardDescription>
                </CardHeader>
                <CardContent>
                  <div className="mb-6 flex items-start gap-4">
                    <div className="relative h-24 w-24 flex-shrink-0 overflow-hidden rounded-md bg-slate-100">
                      <img
                        src={getProductImageUrl()}
                        alt={`${product.color} ${product.material} chair`}
                        className="object-cover"
                      />
                    </div>
                    <div>
                      <h3 className="font-medium">{product.name}</h3>
                      <p className="text-muted-foreground text-sm">
                        Custom configuration
                      </p>
                      <div className="mt-1 flex text-sm">
                        <p className="text-muted-foreground">
                          {product.color.charAt(0).toUpperCase() +
                            product.color.slice(1)}
                          ,{' '}
                          {product.material.charAt(0).toUpperCase() +
                            product.material.slice(1)}
                          {product.headrest ? ', with headrest' : ''}
                        </p>
                      </div>
                      <div className="text-muted-foreground mt-1 text-sm">
                        Qty {product.quantity}
                      </div>
                    </div>
                    <div className="flex-1 text-right">
                      <p className="font-medium">${totalPrice}</p>
                    </div>
                  </div>

                  <Separator className="my-6" />

                  <div className="space-y-1.5">
                    <div className="flex justify-between">
                      <span className="text-muted-foreground">Subtotal</span>
                      <span>${totalPrice}</span>
                    </div>
                    <div className="flex justify-between">
                      <span className="text-muted-foreground">Shipping</span>
                      <span>Free</span>
                    </div>
                    <div className="flex justify-between">
                      <span className="text-muted-foreground">
                        Tax (estimated)
                      </span>
                      <span>${Math.round(totalPrice * 0.0725)}</span>
                    </div>
                    <Separator className="my-2" />
                    <div className="flex justify-between font-medium">
                      <span>Total</span>
                      <span>
                        ${totalPrice + Math.round(totalPrice * 0.0725)}
                      </span>
                    </div>
                  </div>
                </CardContent>
                <CardFooter className="flex flex-col space-y-3">
                  <Button className="w-full">Proceed to Checkout</Button>
                  <Button
                    variant="outline"
                    className="w-full"
                    onClick={() => setOrderComplete(false)}
                  >
                    Continue Shopping
                  </Button>
                </CardFooter>
              </Card>
            )}
          </div>
        </div>
      </div>
      {/* End Hero */}
    </>
  );
}
```

### `components/ui/button.tsx`

```tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/80",
        outline:
          "border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
        secondary:
          "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
        ghost:
          "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
        destructive:
          "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default:
          "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
        xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
        sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
        lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
        icon: "size-9",
        "icon-xs":
          "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
        "icon-sm":
          "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
        "icon-lg": "size-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function Button({
  className,
  variant = "default",
  size = "default",
  asChild = false,
  ...props
}: React.ComponentProps<"button"> &
  VariantProps<typeof buttonVariants> & {
    asChild?: boolean
  }) {
  const Comp = asChild ? Slot.Root : "button"

  return (
    <Comp
      data-slot="button"
      data-variant={variant}
      data-size={size}
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  )
}

export { Button, buttonVariants }
```

### `components/ui/input.tsx`

```tsx
import * as React from "react"

import { cn } from "@/lib/utils"

function Input({ className, type, ...props }: React.ComponentProps<"input">) {
  return (
    <input
      type={type}
      data-slot="input"
      className={cn(
        "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
        className
      )}
      {...props}
    />
  )
}

export { Input }
```

### `components/ui/label.tsx`

```tsx
"use client"

import * as React from "react"
import { Label as LabelPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Label({
  className,
  ...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
  return (
    <LabelPrimitive.Root
      data-slot="label"
      className={cn(
        "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
        className
      )}
      {...props}
    />
  )
}

export { Label }
```

### `components/ui/radio-group.tsx`

```tsx
"use client"

import * as React from "react"
import { RadioGroup as RadioGroupPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function RadioGroup({
  className,
  ...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
  return (
    <RadioGroupPrimitive.Root
      data-slot="radio-group"
      className={cn("grid w-full gap-3", className)}
      {...props}
    />
  )
}

function RadioGroupItem({
  className,
  ...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
  return (
    <RadioGroupPrimitive.Item
      data-slot="radio-group-item"
      className={cn(
        "group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
        className
      )}
      {...props}
    >
      <RadioGroupPrimitive.Indicator
        data-slot="radio-group-indicator"
        className="flex size-4 items-center justify-center"
      >
        <span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
      </RadioGroupPrimitive.Indicator>
    </RadioGroupPrimitive.Item>
  )
}

export { RadioGroup, RadioGroupItem }
```

### `components/ui/slider.tsx`

```tsx
"use client"

import * as React from "react"
import { Slider as SliderPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Slider({
  className,
  defaultValue,
  value,
  min = 0,
  max = 100,
  ...props
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
  const _values = React.useMemo(
    () =>
      Array.isArray(value)
        ? value
        : Array.isArray(defaultValue)
          ? defaultValue
          : [min, max],
    [value, defaultValue, min, max]
  )

  return (
    <SliderPrimitive.Root
      data-slot="slider"
      defaultValue={defaultValue}
      value={value}
      min={min}
      max={max}
      className={cn(
        "relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col",
        className
      )}
      {...props}
    >
      <SliderPrimitive.Track
        data-slot="slider-track"
        className="relative grow overflow-hidden rounded-full bg-muted data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5"
      >
        <SliderPrimitive.Range
          data-slot="slider-range"
          className="absolute bg-primary select-none data-horizontal:h-full data-vertical:w-full"
        />
      </SliderPrimitive.Track>
      {Array.from({ length: _values.length }, (_, index) => (
        <SliderPrimitive.Thumb
          data-slot="slider-thumb"
          key={index}
          className="block size-4 shrink-0 rounded-full border border-primary bg-white shadow-sm ring-ring/50 transition-[color,box-shadow] select-none hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
        />
      ))}
    </SliderPrimitive.Root>
  )
}

export { Slider }
```

### `components/ui/switch.tsx`

```tsx
"use client"

import * as React from "react"
import { Switch as SwitchPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Switch({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
  size?: "sm" | "default"
}) {
  return (
    <SwitchPrimitive.Root
      data-slot="switch"
      data-size={size}
      className={cn(
        "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
        className
      )}
      {...props}
    >
      <SwitchPrimitive.Thumb
        data-slot="switch-thumb"
        className="pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
      />
    </SwitchPrimitive.Root>
  )
}

export { Switch }
```

### `components/ui/card.tsx`

```tsx
import * as React from "react"

import { cn } from "@/lib/utils"

function Card({
  className,
  size = "default",
  ...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
  return (
    <div
      data-slot="card"
      data-size={size}
      className={cn(
        "group/card flex flex-col gap-6 overflow-hidden rounded-xl bg-card py-6 text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
        className
      )}
      {...props}
    />
  )
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-header"
      className={cn(
        "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4",
        className
      )}
      {...props}
    />
  )
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-title"
      className={cn(
        "font-heading text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
        className
      )}
      {...props}
    />
  )
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-description"
      className={cn("text-sm text-muted-foreground", className)}
      {...props}
    />
  )
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-action"
      className={cn(
        "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
        className
      )}
      {...props}
    />
  )
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-content"
      className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
      {...props}
    />
  )
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-footer"
      className={cn(
        "flex items-center rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4",
        className
      )}
      {...props}
    />
  )
}

export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
}
```

### `components/ui/tooltip.tsx`

```tsx
"use client"

import * as React from "react"
import { Tooltip as TooltipPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function TooltipProvider({
  delayDuration = 0,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
  return (
    <TooltipPrimitive.Provider
      data-slot="tooltip-provider"
      delayDuration={delayDuration}
      {...props}
    />
  )
}

function Tooltip({
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
  return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
}

function TooltipTrigger({
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}

function TooltipContent({
  className,
  sideOffset = 0,
  arrow = true,
  children,
  ...props
}: React.ComponentProps<typeof TooltipPrimitive.Content> & {
  /** Hide the pointer arrow (custom-surfaced tooltips, e.g. the app's dark
   *  menu-styled ones, where the bg-foreground diamond clashes). */
  arrow?: boolean
}) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Content
        data-slot="tooltip-content"
        sideOffset={sideOffset}
        className={cn(
          "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
          className
        )}
        {...props}
      >
        {children}
        {arrow && (
          <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
        )}
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  )
}

export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
```

### `components/ui/badge.tsx`

```tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"

import { cn } from "@/lib/utils"

const badgeVariants = cva(
  "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
        secondary:
          "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
        destructive:
          "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
        outline:
          "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
        ghost:
          "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
        link: "text-primary underline-offset-4 hover:underline",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

function Badge({
  className,
  variant = "default",
  asChild = false,
  ...props
}: React.ComponentProps<"span"> &
  VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
  const Comp = asChild ? Slot.Root : "span"

  return (
    <Comp
      data-slot="badge"
      data-variant={variant}
      className={cn(badgeVariants({ variant }), className)}
      {...props}
    />
  )
}

export { Badge, badgeVariants }
```

### `components/ui/separator.tsx`

```tsx
"use client";

import * as React from "react";
import { Separator as SeparatorPrimitive } from "radix-ui";

import { cn } from "@/lib/utils";

function Separator({
  className,
  orientation = "horizontal",
  decorative = true,
  ...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
  return (
    <SeparatorPrimitive.Root
      data-slot="separator"
      decorative={decorative}
      orientation={orientation}
      className={cn(
        "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px ",
        className
      )}
      {...props}
    />
  );
}

export { Separator };
```
