header-3

PreviousNext

Premium header block with gradient logo and smooth animations

Docs
smoothuiui

Preview

Loading preview…
index.tsx
"use client";

import { Avatar, AvatarImage } from "@repo/shadcn-ui/components/ui/avatar";
import { getAllPeople, getAvatarUrl, getImageKitUrl } from "@smoothui/data";
import { ArrowDownRight, Star } from "lucide-react";
import { motion } from "motion/react";
import { AnimatedGroup, AnimatedText, Button, HeroHeader } from "../shared";

type HeroShowcaseProps = {
  heading?: string;
  description?: string;
  buttons?: {
    primary?: {
      text: string;
      url: string;
    };
    secondary?: {
      text: string;
      url: string;
    };
  };
  reviews?: {
    count: number;
    avatars: {
      src: string;
      alt: string;
    }[];
    rating?: number;
  };
};

export function HeroShowcase({
  heading = "Build beautiful UIs, effortlessly.",
  description = "Smoothui gives you the building blocks to create stunning, animated interfaces in minutes.",
  buttons = {
    primary: {
      text: "Get Started",
      url: "#link",
    },
    secondary: {
      text: "Watch demo",
      url: "#link",
    },
  },
  reviews = {
    count: 200,
    rating: 5.0,
    avatars: getAllPeople()
      .slice(0, 5)
      .map((person) => ({
        src: getAvatarUrl(person.avatar, 90),
        alt: `${person.name} avatar`,
      })),
  },
}: HeroShowcaseProps) {
  return (
    <>
      <HeroHeader />
      <main>
        <motion.section
          animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
          className="relative overflow-hidden bg-gradient-to-b from-background to-muted"
          initial={{ opacity: 0, scale: 1.04, filter: "blur(12px)" }}
          transition={{ type: "spring", bounce: 0.32, duration: 0.9 }}
        >
          <div className="mx-auto grid max-w-5xl items-center gap-10 px-6 py-24 lg:grid-cols-2 lg:gap-20">
            <AnimatedGroup
              className="mx-auto flex flex-col items-center text-center md:ml-auto lg:max-w-3xl lg:items-start lg:text-left"
              preset="blur-slide"
            >
              <AnimatedText
                as="h1"
                className="my-6 text-pretty font-bold text-4xl lg:text-6xl xl:text-7xl"
              >
                {heading}
              </AnimatedText>
              <AnimatedText
                as="p"
                className="mb-8 max-w-xl text-foreground/70 lg:text-xl"
                delay={0.12}
              >
                {description}
              </AnimatedText>
              <AnimatedGroup
                className="mb-12 flex w-fit flex-col items-center gap-4 sm:flex-row"
                preset="slide"
              >
                <span className="-space-x-4 inline-flex items-center">
                  {reviews.avatars.map((avatar, index) => (
                    <motion.div
                      key={`${avatar.src}-${index}`}
                      style={{ display: "inline-block" }}
                      transition={{
                        type: "spring",
                        stiffness: 300,
                        damping: 20,
                      }}
                      whileHover={{ y: -8 }}
                    >
                      <Avatar className="size-12 border">
                        <AvatarImage alt={avatar.alt} src={avatar.src} />
                      </Avatar>
                    </motion.div>
                  ))}
                </span>
                <div>
                  <div className="flex items-center gap-1">
                    {[1, 2, 3, 4, 5].map((starNumber) => (
                      <Star
                        className="size-5 fill-yellow-400 text-yellow-400"
                        key={`star-${starNumber}`}
                      />
                    ))}
                    <span className="mr-1 font-semibold">
                      {reviews.rating?.toFixed(1)}
                    </span>
                  </div>
                  <p className="text-left font-medium text-foreground/70">
                    from {reviews.count}+ reviews
                  </p>
                </div>
              </AnimatedGroup>
              <AnimatedGroup
                className="flex w-full flex-col justify-center gap-2 sm:flex-row lg:justify-start"
                preset="slide"
              >
                {buttons.primary && (
                  <Button asChild className="w-full sm:w-auto" variant="candy">
                    <a href={buttons.primary.url}>{buttons.primary.text}</a>
                  </Button>
                )}
                {buttons.secondary && (
                  <Button asChild variant="outline">
                    <a href={buttons.secondary.url}>
                      {buttons.secondary.text}
                      <ArrowDownRight className="size-4" />
                    </a>
                  </Button>
                )}
              </AnimatedGroup>
            </AnimatedGroup>
            {/* Imagen completamente estática para que el blend mode funcione perfecto */}
            <div className="flex">
              <img
                alt="app screen"
                className="h-full w-full rounded-md object-cover"
                height={1842}
                src={getImageKitUrl("/images/hero-example_xertaz.png", {
                  width: 1200,
                  quality: 85,
                  format: "auto",
                })}
                width={2880}
              />
            </div>
          </div>
        </motion.section>
      </main>
    </>
  );
}
export default HeroShowcase;

Installation

npx shadcn@latest add @smoothui/header-3

Usage

import { Header3 } from "@/components/ui/header-3"
<Header3 />