# Hero Section v6

> Centered hero with headline, description, two CTA buttons, a product screenshot, and a brands marquee strip below.

A shadcn/ui block from the [@uiception](https://uiception.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiception/hero-section-v6).

- Page, with a live preview: https://shadcn.exchange/r/uiception/hero-section-v6
- Screenshot: https://shadcn.exchange/shot/uiception/hero-section-v6/light.png
- Registry item: https://uiception.com/r/hero-section-v6.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/uiception/hero-section-v6.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: `@paper-design/shaders-react`, `gsap`
- Registry items: `https://uiception.com/r/block-media.json`, `button`, `https://uiception.com/r/brands-section-v5.json`

## Files

### `registry/new-york/blocks/hero-section-v6/page.tsx`

```tsx
import HeroSectionV6 from "./components/hero-section-v6"

export default function Page() {
  return <HeroSectionV6 />
}
```

### `registry/new-york/blocks/hero-section-v6/components/hero-section-v6.tsx`

```tsx
import BrandsSectionV5 from "../../brands-section-v5/components/brands-section-v5"
import { createBlockImage } from "@/lib/block-media"

const blockImage = createBlockImage("hero-section-v6")
import { HeroV6Root } from "./hero-v6-root"

import "../styles/hero-section-v6.css"

const HERO_V6_IMAGE = blockImage("image.jpg")

export default function HeroSectionV6() {
  return (
    <>
      <section className="py-4 md:py-6 lg:py-8">
        <HeroV6Root imageSrc={HERO_V6_IMAGE} />
      </section>
      <BrandsSectionV5 animated />
    </>
  )
}
```

### `registry/new-york/blocks/hero-section-v6/components/hero-v6-root.tsx`

```tsx
"use client"

import { useLayoutEffect, useRef } from "react"
import gsap from "gsap"

import HeroContent from "./hero-content"
import { HeroV6Image } from "./hero-v6-image"
import ShaderImageBg from "./shader-image-bg"

type HeroV6RootProps = {
  imageSrc: string
}

export function HeroV6Root({ imageSrc }: HeroV6RootProps) {
  const rootRef = useRef<HTMLDivElement>(null)
  const mediaRef = useRef<HTMLDivElement>(null)
  const frameRef = useRef<HTMLDivElement>(null)

  useLayoutEffect(() => {
    const root = rootRef.current
    const media = mediaRef.current
    const frame = frameRef.current
    if (!root || !media || !frame) return

    const isDesktop = window.matchMedia("(min-width: 1024px)").matches
    const reduceMotion = window.matchMedia(
      "(prefers-reduced-motion: reduce)"
    ).matches

    const reveals = gsap.utils.toArray<HTMLElement>(
      "[data-hero-v6-reveal]",
      root
    )
    const words = gsap.utils.toArray<HTMLElement>("[data-hero-v6-word]", root)

    if (reduceMotion) {
      gsap.set([...words, ...reveals, media, frame], {
        clearProps: "all",
        autoAlpha: 1,
      })
      return
    }

    const ctx = gsap.context(() => {
      gsap.set(words, { autoAlpha: 0, y: 26, force3D: true })
      gsap.set(reveals, { autoAlpha: 0, y: 20, force3D: true })
      gsap.set(media, {
        autoAlpha: 0,
        y: 42,
        scale: isDesktop ? 0.985 : 0.995,
        transformOrigin: "50% 50%",
        force3D: true,
      })
      gsap.set(frame, {
        autoAlpha: 0,
        scale: 0.97,
        force3D: true,
      })

      const allEls = [...words, ...reveals, media, frame]

      const tl = gsap.timeline({
        defaults: { ease: "power3.out", duration: 0.48 },
        onComplete: () => {
          gsap.set(allEls, { clearProps: "willChange" })
        },
      })

      tl.to(words, {
        autoAlpha: 1,
        y: 0,
        ease: "power2.out",
        stagger: { each: 0.04, from: "start" },
        force3D: true,
        clearProps: "transform",
      })
        .to(
          reveals[0],
          {
            autoAlpha: 1,
            y: 0,
            duration: 0.42,
            ease: "power2.out",
            force3D: true,
            clearProps: "transform",
          },
          ">-0.06"
        )
        .to(
          reveals[1],
          {
            autoAlpha: 1,
            y: 0,
            duration: 0.4,
            ease: "power2.out",
            force3D: true,
            clearProps: "transform",
          },
          ">-0.04"
        )
        .to(
          media,
          {
            autoAlpha: 1,
            y: 0,
            scale: 1,
            duration: 0.62,
            ease: "power3.out",
            force3D: true,
            clearProps: "transform",
          },
          ">-0.02"
        )
        .to(
          frame,
          {
            autoAlpha: 1,
            scale: 1,
            duration: 0.56,
            ease: "power2.out",
            force3D: true,
            clearProps: "transform",
          },
          "<"
        )
    }, root)

    return () => ctx.revert()
  }, [])

  return (
    <div ref={rootRef}>
      <div className="mx-auto max-w-6xl px-4">
        <div className="flex flex-col items-center gap-6 text-center">
          <HeroContent />
        </div>
      </div>

      <div className="mx-auto max-w-5xl px-4 pt-4">
        <div
          ref={mediaRef}
          className="hero-v6-media-shell relative mt-4 aspect-video w-full max-sm:mt-3.5 sm:mt-6"
        >
          <ShaderImageBg ref={frameRef} />
          <div className="hero-v6-media-image absolute inset-4 sm:inset-5 md:inset-7">
            <div className="relative size-full overflow-hidden rounded-sm border border-slate-900/22 bg-[#f5f5f5]">
              <HeroV6Image src={imageSrc} />
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v6/components/hero-content.tsx`

```tsx
import CtaButtons from "./cta-buttons"

const TITLE_WORDS = ["Run", "payroll", "faster", "with", "fewer", "errors."]

export default function HeroContent() {
  return (
    <div className="flex flex-col items-center gap-6 text-center">
      <h1 className="max-w-[18ch] text-3xl tracking-tighter sm:text-4xl lg:text-5xl lg:leading-[1.15]">
        {TITLE_WORDS.map((word, i) => (
          <span key={i} data-hero-v6-word className="inline-block">
            {i < TITLE_WORDS.length - 1 ? word + " " : word}
          </span>
        ))}
      </h1>

      <div data-hero-v6-reveal>
        <p className="max-w-xl text-base leading-7 tracking-tight text-muted-foreground sm:text-lg sm:leading-8">
          Automate approvals, sync time tracking, and catch anomalies before
          payday with a payroll workspace built for finance teams.
        </p>
      </div>

      <div data-hero-v6-reveal>
        <CtaButtons />
      </div>
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v6/components/cta-buttons.tsx`

```tsx
import Link from "next/link"

import { Button } from "@/components/ui/button"

const PRIMARY_HREF = "#"
const SECONDARY_HREF = "#"

export default function CtaButtons() {
  return (
    <div className="flex flex-row flex-wrap justify-center gap-3">
      <Button size="lg" className="w-full sm:w-auto" asChild>
        <Link href={PRIMARY_HREF} className="text-center font-mono uppercase">
          Start payroll audit
        </Link>
      </Button>
      <Button
        variant="secondary"
        size="lg"
        className="w-full sm:w-auto"
        asChild
      >
        <Link href={SECONDARY_HREF} className="text-center font-mono uppercase">
          View platform tour
        </Link>
      </Button>
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v6/components/hero-v6-image.tsx`

```tsx
import Image from "next/image"

type HeroV6ImageProps = {
  src: string
}

export function HeroV6Image({ src }: HeroV6ImageProps) {
  return (
    <Image
      src={src}
      unoptimized
      alt="Payroll operations dashboard preview"
      width={1280}
      height={720}
      className="size-full object-contain object-center"
      sizes="(max-width: 640px) 100vw, (max-width: 1024px) 95vw, 1280px"
      preload={true}
    />
  )
}
```

### `registry/new-york/blocks/hero-section-v6/components/shader-image-bg.tsx`

```tsx
import { forwardRef } from "react"
import { PerlinNoise } from "@paper-design/shaders-react"

const ShaderImageBg = forwardRef<HTMLDivElement>(
  function ShaderImageBg(_, ref) {
    return (
      <div ref={ref} className="hero-v6-noise-frame" aria-hidden>
        <PerlinNoise
          width={1280}
          height={720}
          colorBack="#ffffff"
          colorFront="#262626"
          proportion={0.65}
          softness={0.35}
          octaveCount={6}
          persistence={1}
          lacunarity={2.55}
          speed={0.02}
          scale={4}
        />
      </div>
    )
  }
)

export default ShaderImageBg
```

### `registry/new-york/blocks/hero-section-v6/styles/hero-section-v6.css`

```css
.hero-v6-noise-frame {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  opacity: 0.9;
}

.hero-v6-media-image {
  position: absolute;
  z-index: 1;
}

.hero-v6-noise-frame canvas {
  width: 100% !important;
  height: 100% !important;
  display: block;
}

.hero-v6-noise-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(
      45deg,
      rgba(255, 255, 255, 0.07) 0 1px,
      transparent 1px 4px
    ),
    repeating-linear-gradient(
      -45deg,
      rgba(0, 0, 0, 0.12) 0 1px,
      transparent 1px 5px
    );
  pointer-events: none;
}

.hero-v6-noise-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.12),
    transparent 42%,
    rgba(15, 23, 42, 0.22)
  );
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  [data-hero-v6-reveal],
  [data-hero-v6-word],
  .hero-v6-media-shell,
  .hero-v6-noise-frame {
    opacity: 0;
    will-change: transform, opacity;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-hero-v6-reveal],
  [data-hero-v6-word],
  .hero-v6-media-shell,
  .hero-v6-noise-frame {
    opacity: 1;
    transform: none;
    filter: none;
  }
}
```

### `public/images/blocks/hero-section-v6/image.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```
