Building Beautiful Reusable Backgrounds and Buttons with React, TypeScript, and Tailwind CSS
Master the art of creating stunning, type-safe UI components with gradient backgrounds, animated blobs, grid patterns, and 13+ button variants. A comprehensive tutorial featuring real-world examples, complete setup guide, and production-ready code for building scalable design systems.
Building Beautiful Reusable Backgrounds and Buttons with React, TypeScript, and Tailwind CSS
Master the art of creating stunning, type-safe UI components that elevate your React applications with minimal effort.
Table of Contents
- Introduction
- Prerequisites
- Project Setup
- Part 1: Gradient Background Component
- Part 2: Enhanced Button Component
- Real-World Usage Examples
- Advanced Customization
- Best Practices
- Conclusion
Introduction
In modern web development, creating reusable, beautiful UI components is essential for building scalable applications. This tutorial will teach you how to build two powerful components:
- GradientBackground - A flexible background wrapper with animated gradient blobs and optional grid patterns
- Enhanced Button - A feature-rich button component with 13+ variants including gradient themes
Both components are:
- ✅ Fully type-safe with TypeScript
- ✅ Highly customizable with multiple variants
- ✅ Production-ready with accessibility features
- ✅ Easy to use with sensible defaults
Prerequisites
Before we begin, make sure you have:
- Node.js 16+ installed
- Basic knowledge of React and TypeScript
- Familiarity with Tailwind CSS
- A Next.js or React project set up
Required Dependencies
pnpm add class-variance-authority clsx tailwind-merge
npm install @radix-ui/react-slot
npm install lucide-react # For icons (optional)
Project Setup
1. Configure Tailwind CSS
First, ensure your tailwind.config.ts is properly configured:
import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
// Add custom colors if needed
},
},
plugins: [],
};
export default config;2. Add Custom CSS for Grid Pattern
Create or update your globals.css file:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.bg-grid-radial {
background: white;
background-image:
linear-gradient(to right, rgba(71, 85, 105, 0.1) 1px, transparent 1px),
linear-gradient(to bottom, rgba(71, 85, 105, 0.1) 1px, transparent 1px),
radial-gradient(
circle at 50% 50%,
rgba(99, 102, 241, 0.15) 0%,
rgba(139, 92, 246, 0.05) 40%,
transparent 80%
);
background-size:
32px 32px,
32px 32px,
100% 100%;
}
}3. Create Utility Function
Create lib/utils.ts for the cn helper:
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Part 1: Gradient Background Component
Understanding the Component Architecture
The GradientBackground component provides a flexible wrapper that adds beautiful gradient effects to any content. It features:
- 4 themed variants (default, ocean, sunset, forest)
- Animated gradient blobs with customizable blur
- Optional grid pattern overlay
- Fade overlay for smooth transitions
- Full TypeScript support
Component Code
Create components/ui/gradient-background.tsx:
"use client";
import { ReactNode } from "react";
import { cn } from "@/lib/utils";
type Variant = "default" | "ocean" | "sunset" | "forest";
type BlurIntensity =
| "blur-sm"
| "blur"
| "blur-md"
| "blur-lg"
| "blur-xl"
| "blur-2xl"
| "blur-3xl";
interface VariantStyles {
container: string;
blob1: string;
blob2: string;
fadeOverlay: string;
}
interface GradientBackgroundProps {
children: ReactNode;
variant?: Variant;
className?: string;
showFadeOverlay?: boolean;
grid?: boolean;
blurIntensity?: BlurIntensity;
}
const variants: Record<Variant, VariantStyles> = {
default: {
container: "bg-gradient-to-br from-slate-50 via-white to-slate-50",
blob1: "bg-indigo-200/60 -left-40 top-10",
blob2: "bg-purple-200/60 -right-40 top-60",
fadeOverlay: "from-transparent to-white",
},
ocean: {
container: "bg-gradient-to-br from-cyan-50 via-blue-50 to-teal-50",
blob1: "bg-cyan-300/50 -left-32 top-20",
blob2: "bg-blue-300/50 -right-32 bottom-20",
fadeOverlay: "from-transparent to-cyan-50",
},
sunset: {
container: "bg-gradient-to-br from-orange-50 via-pink-50 to-purple-50",
blob1: "bg-orange-300/40 -left-40 top-0",
blob2: "bg-pink-300/40 -right-40 top-40",
fadeOverlay: "from-transparent to-orange-50",
},
forest: {
container: "bg-gradient-to-br from-emerald-50 via-green-50 to-teal-50",
blob1: "bg-emerald-300/50 -left-36 bottom-10",
blob2: "bg-green-300/50 -right-36 top-10",
fadeOverlay: "from-transparent to-emerald-50",
},
};
export default function GradientBackground({
children,
variant = "default",
className = "",
showFadeOverlay = true,
blurIntensity = "blur-3xl",
grid = true,
}: GradientBackgroundProps) {
const variantStyles = variants[variant];
return (
<section
className={cn(
"relative overflow-hidden",
variantStyles.container,
className,
grid && "bg-grid-radial"
)}
>
{/* Fade overlay at bottom */}
{showFadeOverlay && (
<div
className={cn(
"pointer-events-none absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-b",
variantStyles.fadeOverlay
)}
></div>
)}
{/* Gradient blobs */}
<div
className={cn(
"pointer-events-none absolute h-96 w-96 rounded-full",
variantStyles.blob1,
blurIntensity
)}
></div>
<div
className={cn(
"pointer-events-none absolute h-96 w-96 rounded-full",
variantStyles.blob2,
blurIntensity
)}
></div>
{/* Content */}
<div className="relative">{children}</div>
</section>
);
}
// Export types for use in other components
export type { GradientBackgroundProps, Variant, BlurIntensity };How It Works
1. Type Definitions
type Variant = "default" | "ocean" | "sunset" | "forest";This creates a union type that restricts the variant prop to only these four values, providing compile-time safety.
2. Variant Configuration
const variants: Record<Variant, VariantStyles> = {
default: {
container: "bg-gradient-to-br from-slate-50 via-white to-slate-50",
blob1: "bg-indigo-200/60 -left-40 top-10",
blob2: "bg-purple-200/60 -right-40 top-60",
fadeOverlay: "from-transparent to-white",
},
// ... other variants
};Each variant defines its color scheme and positioning for a cohesive look.
3. Grid Pattern Feature
grid && "bg-grid-radial";When grid={true}, the component adds a subtle grid pattern overlay using the custom CSS class.
Basic Usage
import GradientBackground from "@/components/ui/gradient-background";
export default function Page() {
return (
<GradientBackground variant="default">
<div className="container mx-auto px-4 py-20">
<h1 className="text-4xl font-bold">Welcome</h1>
<p className="text-lg text-slate-600">Your content here</p>
</div>
</GradientBackground>
);
}Advanced Usage
Without Grid Pattern
<GradientBackground variant="ocean" grid={false}>
<YourContent />
</GradientBackground>Custom Blur Intensity
<GradientBackground variant="sunset" blurIntensity="blur-xl">
<YourContent />
</GradientBackground>No Fade Overlay
<GradientBackground variant="forest" showFadeOverlay={false}>
<YourContent />
</GradientBackground>Combined Options
<GradientBackground
variant="default"
grid={true}
blurIntensity="blur-2xl"
showFadeOverlay={false}
className="min-h-screen"
>
<YourContent />
</GradientBackground>Part 2: Enhanced Button Component
Understanding the Component Architecture
The enhanced Button component is built on:
- Radix UI Slot - For composition with
asChild - Class Variance Authority (CVA) - For variant management
- TypeScript - For type safety
It includes:
- 7 original variants (default, destructive, outline, etc.)
- 4 custom gradient variants (gradient, dark, outline-pill, light-pill)
- 3 themed variants (ocean, sunset, forest)
- Multiple sizes (sm, default, lg, icon)
Component Code
Create components/ui/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 gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90 shadow-[inset_0px_0px_0px_1px_rgba(0,0,0,0.2)] rounded-lg",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 rounded-lg",
outline:
"border bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 rounded-lg",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 rounded-lg",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 rounded-lg",
link: "text-primary underline-offset-4 hover:underline",
// Custom gradient variant
gradient:
"rounded-full bg-gradient-to-r from-indigo-500 to-fuchsia-500 px-6 py-2.5 font-semibold text-white shadow-lg shadow-indigo-500/40 hover:from-indigo-600 hover:to-fuchsia-500",
// Outline pill variant
"outline-pill":
"rounded-full border border-slate-200 bg-white px-5 py-2.5 text-slate-800 shadow-sm shadow-slate-200 hover:border-slate-300 hover:shadow-md",
// Dark variant
dark:
"rounded bg-slate-900 px-5 py-2 font-medium text-white shadow-lg shadow-slate-900/40 hover:bg-purple-800",
// Light pill variant
"light-pill":
"rounded-full border border-slate-200 bg-white px-4 py-2 text-slate-800 shadow-sm shadow-slate-200/60 hover:border-slate-300 hover:shadow-md",
// Themed gradient variants
ocean:
"rounded-full bg-gradient-to-r from-cyan-500 to-blue-500 px-6 py-2.5 font-semibold text-white shadow-lg shadow-cyan-500/40 hover:from-cyan-600 hover:to-blue-600",
sunset:
"rounded-full bg-gradient-to-r from-orange-500 to-pink-500 px-6 py-2.5 font-semibold text-white shadow-lg shadow-orange-500/40 hover:from-orange-600 hover:to-pink-600",
forest:
"rounded-full bg-gradient-to-r from-emerald-500 to-green-500 px-6 py-2.5 font-semibold text-white shadow-lg shadow-emerald-500/40 hover:from-emerald-600 hover:to-green-600",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-4",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "button";
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };How It Works
1. Class Variance Authority (CVA)
const buttonVariants = cva(
"base-classes", // Classes applied to all variants
{
variants: {
variant: {
/* variant styles */
},
size: {
/* size styles */
},
},
defaultVariants: {
/* defaults */
},
}
);CVA manages variant classes efficiently, ensuring only the necessary styles are applied.
2. Radix UI Slot
const Comp = asChild ? Slot : "button";The asChild prop allows the button to render as a different element (like a Link) while maintaining all button styles.
3. TypeScript Integration
VariantProps<typeof buttonVariants>;This extracts the variant types directly from the CVA definition, ensuring type safety.
Basic Usage
import { Button } from "@/components/ui/button";
export default function Example() {
return (
<>
<Button variant="gradient">Get Started</Button>
<Button variant="outline-pill">Learn More</Button>
<Button variant="dark">Start Trial</Button>
</>
);
}With Icons
import { Button } from "@/components/ui/button";
import { PlayCircle, ArrowRight } from "lucide-react";
export default function HeroButtons() {
return (
<>
<Button variant="gradient">
Start Now
<ArrowRight />
</Button>
<Button variant="outline-pill">
<PlayCircle className="h-5 w-5 text-slate-700" strokeWidth={1.5} />
Watch Demo
</Button>
</>
);
}As Links
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function Navigation() {
return (
<>
<Button variant="light-pill" asChild>
<Link href="/login">Log In</Link>
</Button>
<Button variant="gradient" asChild>
<Link href="/signup">Sign Up</Link>
</Button>
</>
);
}All Variants Showcase
export default function AllVariants() {
return (
<div className="flex flex-wrap gap-4">
{/* Original variants */}
<Button variant="default">Default</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
{/* Custom variants */}
<Button variant="gradient">Gradient</Button>
<Button variant="outline-pill">Outline Pill</Button>
<Button variant="dark">Dark</Button>
<Button variant="light-pill">Light Pill</Button>
{/* Themed variants */}
<Button variant="ocean">Ocean</Button>
<Button variant="sunset">Sunset</Button>
<Button variant="forest">Forest</Button>
</div>
);
}Real-World Usage Examples
Example 1: Complete Hero Section
import GradientBackground from "@/components/ui/gradient-background";
import { Button } from "@/components/ui/button";
import { PlayCircle, ArrowRight } from "lucide-react";
export default function HeroSection() {
return (
<GradientBackground variant="default" grid={true}>
<div className="container mx-auto px-4 py-20 lg:py-32">
<div className="mx-auto max-w-3xl text-center">
{/* Badge */}
<span className="inline-flex items-center gap-2 rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-slate-500 shadow-sm">
<span className="h-2 w-2 rounded-full bg-gradient-to-r from-indigo-500 to-fuchsia-500"></span>
#1 Product Suite
</span>
{/* Headline */}
<h1 className="mt-6 text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl lg:text-6xl">
Build Better Products
<span className="bg-gradient-to-r from-indigo-500 to-fuchsia-500 bg-clip-text text-transparent">
{" "}
Faster Than Ever
</span>
</h1>
{/* Subtitle */}
<p className="mt-6 text-lg text-slate-600">
The complete toolkit for modern product teams. Ship features faster,
collaborate better, and scale with confidence.
</p>
{/* CTA Buttons */}
<div className="mt-10 flex flex-wrap justify-center gap-4">
<Button variant="gradient" size="lg">
Get Started Free
<ArrowRight />
</Button>
<Button variant="outline-pill" size="lg">
<PlayCircle
className="h-5 w-5 text-slate-700"
strokeWidth={1.5}
/>
Watch Demo
</Button>
</div>
{/* Social Proof */}
<p className="mt-6 text-sm text-slate-500">
Trusted by over 10,000 teams worldwide
</p>
</div>
</div>
</GradientBackground>
);
}Example 2: Feature Section with Ocean Theme
import GradientBackground from "@/components/ui/gradient-background";
import { Button } from "@/components/ui/button";
import { Check, ArrowRight } from "lucide-react";
export default function FeatureSection() {
return (
<GradientBackground variant="ocean" grid={true}>
<div className="container mx-auto px-4 py-20">
<div className="grid gap-12 lg:grid-cols-2 lg:gap-16">
{/* Content */}
<div className="flex flex-col justify-center">
<span className="text-sm font-semibold text-cyan-600">
FEATURES
</span>
<h2 className="mt-2 text-3xl font-bold text-slate-900 sm:text-4xl">
Everything you need to succeed
</h2>
<p className="mt-4 text-lg text-slate-600">
Powerful features designed to help your team work smarter, not
harder.
</p>
<ul className="mt-8 space-y-4">
{[
"Real-time collaboration",
"Advanced analytics",
"Custom workflows",
"24/7 support",
].map((feature) => (
<li key={feature} className="flex items-center gap-3">
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-cyan-100">
<Check className="h-4 w-4 text-cyan-600" />
</div>
<span className="text-slate-700">{feature}</span>
</li>
))}
</ul>
<div className="mt-8">
<Button variant="ocean" size="lg">
Explore Features
<ArrowRight />
</Button>
</div>
</div>
{/* Image/Illustration Placeholder */}
<div className="rounded-2xl bg-white p-8 shadow-xl">
<div className="aspect-square rounded-lg bg-gradient-to-br from-cyan-100 to-blue-100"></div>
</div>
</div>
</div>
</GradientBackground>
);
}Example 3: Pricing Section
import GradientBackground from "@/components/ui/gradient-background";
import { Button } from "@/components/ui/button";
import { Check } from "lucide-react";
const plans = [
{
name: "Starter",
price: "$19",
features: ["Up to 5 team members", "Basic analytics", "Email support"],
variant: "outline-pill" as const,
},
{
name: "Pro",
price: "$49",
features: [
"Up to 20 team members",
"Advanced analytics",
"Priority support",
"Custom workflows",
],
variant: "gradient" as const,
popular: true,
},
{
name: "Enterprise",
price: "$99",
features: [
"Unlimited team members",
"Custom analytics",
"Dedicated support",
"Advanced security",
],
variant: "dark" as const,
},
];
export default function PricingSection() {
return (
<GradientBackground variant="sunset" grid={true}>
<div className="container mx-auto px-4 py-20">
<div className="mx-auto max-w-5xl text-center">
<h2 className="text-3xl font-bold text-slate-900 sm:text-4xl">
Choose your plan
</h2>
<p className="mt-4 text-lg text-slate-600">
Flexible pricing for teams of all sizes
</p>
<div className="mt-12 grid gap-8 lg:grid-cols-3">
{plans.map((plan) => (
<div
key={plan.name}
className={cn(
"relative rounded-2xl bg-white p-8 shadow-lg",
plan.popular && "ring-2 ring-orange-500"
)}
>
{plan.popular && (
<span className="absolute -top-4 left-1/2 -translate-x-1/2 rounded-full bg-gradient-to-r from-orange-500 to-pink-500 px-4 py-1 text-xs font-semibold text-white">
Most Popular
</span>
)}
<h3 className="text-xl font-bold text-slate-900">
{plan.name}
</h3>
<div className="mt-4 flex items-baseline">
<span className="text-4xl font-bold text-slate-900">
{plan.price}
</span>
<span className="ml-2 text-slate-600">/month</span>
</div>
<ul className="mt-8 space-y-4">
{plan.features.map((feature) => (
<li key={feature} className="flex items-start gap-3">
<Check className="mt-0.5 h-5 w-5 shrink-0 text-orange-500" />
<span className="text-sm text-slate-600">{feature}</span>
</li>
))}
</ul>
<Button variant={plan.variant} className="mt-8 w-full">
Get Started
</Button>
</div>
))}
</div>
</div>
</div>
</GradientBackground>
);
}Example 4: Call-to-Action Section
import GradientBackground from "@/components/ui/gradient-background";
import { Button } from "@/components/ui/button";
import { ArrowRight } from "lucide-react";
export default function CTASection() {
return (
<GradientBackground variant="forest" grid={false} showFadeOverlay={false}>
<div className="container mx-auto px-4 py-20">
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-3xl font-bold text-slate-900 sm:text-4xl lg:text-5xl">
Ready to transform your workflow?
</h2>
<p className="mt-6 text-lg text-slate-600">
Join thousands of teams already using our platform to build better
products.
</p>
<div className="mt-10 flex flex-wrap justify-center gap-4">
<Button variant="forest" size="lg">
Start Free Trial
<ArrowRight />
</Button>
<Button variant="outline-pill" size="lg">
Schedule Demo
</Button>
</div>
<p className="mt-6 text-sm text-slate-500">
No credit card required • 14-day free trial
</p>
</div>
</div>
</GradientBackground>
);
}Example 5: Complete Landing Page
import GradientBackground from "@/components/ui/gradient-background";
import { Button } from "@/components/ui/button";
import { PlayCircle, ArrowRight, Check, Star } from "lucide-react";
import Link from "next/link";
export default function LandingPage() {
return (
<div className="min-h-screen">
{/* Navigation */}
<nav className="border-b border-slate-200 bg-white/80 backdrop-blur-sm">
<div className="container mx-auto flex items-center justify-between px-4 py-4">
<div className="text-xl font-bold text-slate-900">YourBrand</div>
<div className="hidden items-center gap-8 md:flex">
<Link
href="#features"
className="text-sm text-slate-600 hover:text-slate-900"
>
Features
</Link>
<Link
href="#pricing"
className="text-sm text-slate-600 hover:text-slate-900"
>
Pricing
</Link>
<Link
href="#about"
className="text-sm text-slate-600 hover:text-slate-900"
>
About
</Link>
</div>
<div className="flex items-center gap-3">
<Button variant="ghost" size="sm" asChild>
<Link href="/login">Log In</Link>
</Button>
<Button variant="gradient" size="sm">
Sign Up
</Button>
</div>
</div>
</nav>
{/* Hero Section */}
<GradientBackground variant="default" grid={true}>
<div className="container mx-auto px-4 py-20 lg:py-32">
<div className="mx-auto max-w-4xl text-center">
<span className="inline-flex items-center gap-2 rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-slate-500 shadow-sm">
<span className="h-2 w-2 rounded-full bg-gradient-to-r from-indigo-500 to-fuchsia-500"></span>
Now in Public Beta
</span>
<h1 className="mt-6 text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl lg:text-6xl">
The Modern Way to
<span className="bg-gradient-to-r from-indigo-500 to-fuchsia-500 bg-clip-text text-transparent">
{" "}
Build Products
</span>
</h1>
<p className="mt-6 text-lg text-slate-600">
Streamline your workflow, collaborate in real-time, and ship
features faster with our all-in-one platform built for modern
teams.
</p>
<div className="mt-10 flex flex-wrap justify-center gap-4">
<Button variant="gradient" size="lg">
Get Started Free
<ArrowRight />
</Button>
<Button variant="outline-pill" size="lg">
<PlayCircle
className="h-5 w-5 text-slate-700"
strokeWidth={1.5}
/>
Watch Demo
</Button>
</div>
<div className="mt-8 flex items-center justify-center gap-6">
<div className="flex -space-x-2">
{[1, 2, 3, 4].map((i) => (
<div
key={i}
className="h-10 w-10 rounded-full border-2 border-white bg-gradient-to-br from-indigo-400 to-fuchsia-400"
></div>
))}
</div>
<div className="text-sm text-slate-600">
<div className="flex items-center gap-1">
{[1, 2, 3, 4, 5].map((i) => (
<Star
key={i}
className="h-4 w-4 fill-amber-400 text-amber-400"
/>
))}
</div>
<p className="mt-1">Loved by 10,000+ teams</p>
</div>
</div>
</div>
</div>
</GradientBackground>
{/* Features Section */}
<GradientBackground variant="ocean" grid={true}>
<div className="container mx-auto px-4 py-20">
<div className="mx-auto max-w-3xl text-center">
<span className="text-sm font-semibold text-cyan-600">
FEATURES
</span>
<h2 className="mt-2 text-3xl font-bold text-slate-900 sm:text-4xl">
Everything you need in one place
</h2>
</div>
<div className="mx-auto mt-16 grid max-w-5xl gap-8 sm:grid-cols-2 lg:grid-cols-3">
{[
{
title: "Real-time Collaboration",
desc: "Work together seamlessly",
},
{ title: "Advanced Analytics", desc: "Data-driven insights" },
{ title: "Custom Workflows", desc: "Adapt to your process" },
{ title: "Integrations", desc: "Connect your favorite tools" },
{ title: "Security First", desc: "Enterprise-grade protection" },
{ title: "24/7 Support", desc: "We're here to help" },
].map((feature) => (
<div
key={feature.title}
className="rounded-xl bg-white p-6 shadow-sm transition hover:shadow-md"
>
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-gradient-to-br from-cyan-500 to-blue-500">
<Check className="h-6 w-6 text-white" />
</div>
<h3 className="mt-4 font-semibold text-slate-900">
{feature.title}
</h3>
<p className="mt-2 text-sm text-slate-600">{feature.desc}</p>
</div>
))}
</div>
<div className="mt-12 text-center">
<Button variant="ocean" size="lg">
Explore All Features
<ArrowRight />
</Button>
</div>
</div>
</GradientBackground>
{/* CTA Section */}
<GradientBackground variant="forest" grid={false}>
<div className="container mx-auto px-4 py-20">
<div className="mx-auto max-w-3xl rounded-2xl bg-white/80 p-12 text-center shadow-xl backdrop-blur-sm">
<h2 className="text-3xl font-bold text-slate-900 sm:text-4xl">
Ready to get started?
</h2>
<p className="mt-4 text-lg text-slate-600">
Join thousands of teams already building better products.
</p>
<div className="mt-8 flex flex-wrap justify-center gap-4">
<Button variant="forest" size="lg">
Start Free Trial
<ArrowRight />
</Button>
<Button variant="outline-pill" size="lg">
Contact Sales
</Button>
</div>
<p className="mt-6 text-sm text-slate-500">
No credit card required • Free 14-day trial
</p>
</div>
</div>
</GradientBackground>
{/* Footer */}
<footer className="border-t border-slate-200 bg-white py-12">
<div className="container mx-auto px-4 text-center text-sm text-slate-600">
<p>© 2024 YourBrand. All rights reserved.</p>
</div>
</footer>
</div>
);
}Advanced Customization
Adding Custom Variants
For GradientBackground
Add a new variant to the variants object:
const variants: Record<Variant, VariantStyles> = {
// ... existing variants
midnight: {
container: "bg-gradient-to-br from-indigo-950 via-purple-900 to-slate-900",
blob1: "bg-indigo-500/30 -left-40 top-10",
blob2: "bg-purple-500/30 -right-40 bottom-10",
fadeOverlay: "from-transparent to-indigo-950",
},
};
// Update the type
type Variant = "default" | "ocean" | "sunset" | "forest" | "midnight";For Button
Add a new variant to the buttonVariants:
const buttonVariants = cva(
// ... base classes
{
variants: {
variant: {
// ... existing variants
neon: "rounded-full bg-gradient-to-r from-cyan-400 to-pink-400 px-6 py-2.5 font-semibold text-white shadow-lg shadow-cyan-400/40 hover:from-cyan-500 hover:to-pink-500",
},
// ... sizes
},
}
);Creating Animated Variants
Add animation to gradient blobs:
// In your globals.css
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-20px);
}
}
@layer utilities {
.animate-float {
animation: float 6s ease-in-out infinite;
}
}Then use it in the component:
<div
className={cn(
"pointer-events-none absolute h-96 w-96 rounded-full animate-float",
variantStyles.blob1,
blurIntensity
)}
></div>Responsive Variants
Create size-specific styling:
<GradientBackground
variant="default"
className="py-10 md:py-20 lg:py-32"
>
{children}
</GradientBackground>Dark Mode Support
Add dark mode variants:
ocean: {
container: "bg-gradient-to-br from-cyan-50 via-blue-50 to-teal-50 dark:from-cyan-950 dark:via-blue-950 dark:to-teal-950",
blob1: "bg-cyan-300/50 dark:bg-cyan-500/20 -left-32 top-20",
blob2: "bg-blue-300/50 dark:bg-blue-500/20 -right-32 bottom-20",
fadeOverlay: "from-transparent to-cyan-50 dark:to-cyan-950",
},Best Practices
1. Performance Optimization
// ✅ Good - Import only what you need
import { Button } from "@/components/ui/button";
// ❌ Bad - Don't import everything
import * as Components from "@/components/ui";2. Semantic HTML
// ✅ Good - Use semantic tags
<GradientBackground variant="ocean">
<section>
<h1>Heading</h1>
</section>
</GradientBackground>
// ❌ Bad - Don't nest sections incorrectly
<section>
<GradientBackground variant="ocean">
<section> {/* Avoid nested sections */}
<h1>Heading</h1>
</section>
</GradientBackground>
</section>3. Accessibility
// ✅ Good - Descriptive button text
<Button variant="gradient">
Start Free Trial
</Button>
// ✅ Good - Icon with text
<Button variant="outline-pill">
<PlayCircle aria-hidden="true" />
Watch Demo
</Button>
// ❌ Bad - Icon only without accessible text
<Button variant="gradient">
<ArrowRight /> {/* No text label */}
</Button>4. Type Safety
// ✅ Good - Use exported types
import type { Variant } from "@/components/ui/gradient-background";
interface MyComponentProps {
backgroundTheme: Variant;
}
// ❌ Bad - String type loses type safety
interface MyComponentProps {
backgroundTheme: string;
}5. Component Composition
// ✅ Good - Compose components logically
<GradientBackground variant="default">
<HeroSection>
<Button variant="gradient">CTA</Button>
</HeroSection>
</GradientBackground>
// ❌ Bad - Over-nesting
<GradientBackground>
<div>
<div>
<div>
<Button>CTA</Button>
</div>
</div>
</div>
</GradientBackground>6. Consistent Variant Usage
// ✅ Good - Consistent theme
<GradientBackground variant="ocean">
<Button variant="ocean">Learn More</Button>
</GradientBackground>
// ⚠️ Consider - Mixed themes (use intentionally)
<GradientBackground variant="ocean">
<Button variant="sunset">Learn More</Button>
</GradientBackground>7. Error Boundaries
import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ error }) {
return (
<div className="p-4 text-red-600">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
</div>
);
}
export default function MyPage() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<GradientBackground variant="default">
<YourContent />
</GradientBackground>
</ErrorBoundary>
);
}8. Testing
import { render, screen } from "@testing-library/react";
import { Button } from "@/components/ui/button";
describe("Button", () => {
it("renders with gradient variant", () => {
render(<Button variant="gradient">Click me</Button>);
const button = screen.getByRole("button", { name: /click me/i });
expect(button).toBeInTheDocument();
});
it("renders as link when asChild is true", () => {
render(
<Button variant="gradient" asChild>
<a href="/test">Link</a>
</Button>
);
const link = screen.getByRole("link", { name: /link/i });
expect(link).toHaveAttribute("href", "/test");
});
});Conclusion
You've now built two powerful, reusable components that will elevate your React applications:
What You've Learned
- ✅ Creating type-safe components with TypeScript
- ✅ Managing variants with Class Variance Authority
- ✅ Building flexible, composable UI components
- ✅ Implementing custom Tailwind CSS utilities
- ✅ Using Radix UI for advanced composition
- ✅ Following React best practices
Next Steps
- Extend the components - Add more variants for your brand
- Create documentation - Document your component library
- Build a Storybook - Showcase all variants visually
- Add animations - Enhance with Framer Motion or CSS animations
- Create themes - Build a full design system
Resources
Final Thoughts
These components are production-ready and highly customizable. They follow industry best practices and are built with scalability in mind. Feel free to extend them, add your own variants, and make them your own!
Happy coding! 🚀
Author: Your Name
Last Updated: November 2024
License: MIT

