Innovations
heroes /

Split Hero with Gradient

Two-column hero with animated gradient blob. Left: headline + CTAs. Right: animated visual.

Preview

Source

tsx
"use client";

import { motion } from "framer-motion";
import { ArrowRight, Sparkles } from "lucide-react";
import { Button } from "@/components/ui/button";

export default function SplitGradientHero() {
  return (
    <section className="relative min-h-screen flex items-center overflow-hidden bg-background">
      <div className="container mx-auto px-6 py-20 lg:py-0 grid lg:grid-cols-2 gap-12 lg:gap-20 items-center">
        {/* Left: Text content */}
        <motion.div
          initial={{ opacity: 0, x: -40 }}
          animate={{ opacity: 1, x: 0 }}
          transition={{ duration: 0.7, ease: "easeOut" }}
          className="flex flex-col gap-6"
        >
          <span className="inline-flex items-center gap-2 text-sm font-medium text-primary bg-primary/10 rounded-full px-4 py-1.5 w-fit">
            <Sparkles className="w-4 h-4" />
            Now in public beta
          </span>

          <h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold tracking-tight text-foreground leading-[1.1]">
            Build faster.
            <br />
            <span className="bg-gradient-to-r from-primary to-violet-500 bg-clip-text text-transparent">
              Ship smarter.
            </span>
          </h1>

          <p className="text-lg text-muted-foreground max-w-md leading-relaxed">
            The modern platform for teams who demand more from their workflow.
            Beautiful by default, infinitely flexible, and built to scale.
          </p>

          <div className="flex flex-col sm:flex-row gap-3 pt-2">
            <Button size="lg" className="gap-2 text-base">
              Get started free
              <ArrowRight className="w-4 h-4" />
            </Button>
            <Button size="lg" variant="outline" className="text-base">
              View demo
            </Button>
          </div>

          <p className="text-sm text-muted-foreground">
            No credit card required · Free forever plan available
          </p>
        </motion.div>

        {/* Right: Animated gradient blob */}
        <motion.div
          initial={{ opacity: 0, scale: 0.8 }}
          animate={{ opacity: 1, scale: 1 }}
          transition={{ duration: 0.9, ease: "easeOut", delay: 0.2 }}
          className="relative flex items-center justify-center h-[400px] lg:h-[560px]"
        >
          {/* Main blob */}
          <motion.div
            animate={{
              borderRadius: [
                "60% 40% 30% 70% / 60% 30% 70% 40%",
                "30% 60% 70% 40% / 50% 60% 30% 60%",
                "50% 50% 40% 60% / 40% 70% 60% 50%",
                "60% 40% 30% 70% / 60% 30% 70% 40%",
              ],
              rotate: [0, 5, -5, 0],
            }}
            transition={{
              duration: 8,
              repeat: Infinity,
              ease: "easeInOut",
            }}
            className="absolute inset-8 bg-gradient-to-br from-primary via-violet-500 to-fuchsia-500 opacity-80 blur-[2px]"
          />

          {/* Inner glow */}
          <motion.div
            animate={{
              borderRadius: [
                "40% 60% 70% 30% / 40% 60% 30% 70%",
                "60% 40% 30% 70% / 60% 30% 70% 40%",
                "30% 60% 70% 40% / 50% 40% 60% 50%",
                "40% 60% 70% 30% / 40% 60% 30% 70%",
              ],
            }}
            transition={{
              duration: 6,
              repeat: Infinity,
              ease: "easeInOut",
              delay: 1,
            }}
            className="absolute inset-16 bg-gradient-to-tl from-sky-400 via-primary to-indigo-600 opacity-60"
          />

          {/* Floating card overlay */}
          <motion.div
            initial={{ y: 20, opacity: 0 }}
            animate={{ y: 0, opacity: 1 }}
            transition={{ delay: 0.8, duration: 0.6 }}
            className="relative z-10 bg-background/90 backdrop-blur-md border border-border rounded-2xl p-6 shadow-2xl w-64"
          >
            <div className="flex items-center gap-3 mb-4">
              <div className="w-10 h-10 rounded-full bg-gradient-to-br from-primary to-violet-500 flex items-center justify-center">
                <Sparkles className="w-5 h-5 text-white" />
              </div>
              <div>
                <p className="text-sm font-semibold text-foreground">Weekly report</p>
                <p className="text-xs text-muted-foreground">Just generated</p>
              </div>
            </div>
            <div className="space-y-2">
              <div className="flex justify-between text-xs">
                <span className="text-muted-foreground">Conversion rate</span>
                <span className="font-semibold text-foreground">+24.8%</span>
              </div>
              <div className="w-full bg-muted rounded-full h-1.5">
                <div className="bg-primary h-1.5 rounded-full" style={{ width: "72%" }} />
              </div>
              <div className="flex justify-between text-xs">
                <span className="text-muted-foreground">Revenue</span>
                <span className="font-semibold text-green-500">$48,230</span>
              </div>
              <div className="w-full bg-muted rounded-full h-1.5">
                <div className="bg-green-500 h-1.5 rounded-full" style={{ width: "85%" }} />
              </div>
            </div>
          </motion.div>
        </motion.div>
      </div>
    </section>
  );
}
Claude Code Instructions

CLI Install

npx innovations add split-gradient

Where to use it

Place this at the top of your landing page, as the first section after the navbar. In Astro (src/pages/index.astro or similar): import SplitGradientHero from '../components/innovations/heroes/split-gradient'; <SplitGradientHero client:load /> In Next.js (app/page.tsx): import SplitGradientHero from '@/components/innovations/heroes/split-gradient'; // Add at the top of the page return, before any other sections Customize the headline, subtext, and CTA button text/href via props or by editing the default values directly in the component.