# Hero 08

> A split hero with a bold headline, avatar social proof, and two image cards with invertible overlay text and CTAs.

A shadcn/ui block from the [@flx](https://ui.flexnative.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/flx/hero-08).

- Page, with a live preview: https://shadcn.exchange/r/flx/hero-08
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/flx/hero-08-light-764f53f4f047cd05.png
- Registry item: https://ui.flexnative.com/r/hero-08.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.flexnative.com/r/hero-08.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: `motion`, `react-wrap-balancer`
- Registry items: `@flx/cta`, `avatar`

## Files

### `registry/blocks/hero/hero-08/hero-08.tsx`

```tsx
'use client'

import * as React from 'react'
import { motion, useReducedMotion, type Variants } from 'motion/react'
import Balancer from 'react-wrap-balancer'

import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { cn } from '@/lib/utils'

import { Cta, type CtaProps } from '../../shared/cta'

export interface Hero08Avatar {
  src: string
  fallback: string
}

export interface Hero08Card {
  title: string
  subtitle: string
  image: string
  imageAlt?: string
  invert?: boolean
  cta: CtaProps
}

export interface Hero08Props {
  title: string
  description: string
  socialProof?: string
  avatars?: Hero08Avatar[]
  cards: Hero08Card[]
  animation?: 'none' | 'subtle'
  variant?: 'standard' | 'compact'
}

const variantStyles = {
  standard: {
    section: 'py-20 sm:py-28',
    title: 'text-3xl sm:text-4xl md:text-5xl',
    description: 'text-sm sm:text-base',
    header: 'gap-10 lg:gap-16',
    content: 'gap-12 sm:gap-16',
    grid: 'gap-5 sm:gap-6',
    card: 'aspect-16/10',
    cardTitle: 'text-2xl sm:text-3xl',
    cardBody: 'p-6 sm:p-8',
  },
  compact: {
    section: 'py-14 sm:py-20',
    title: 'text-2xl sm:text-3xl md:text-4xl',
    description: 'text-sm',
    header: 'gap-8 lg:gap-12',
    content: 'gap-10 sm:gap-12',
    grid: 'gap-4 sm:gap-5',
    card: 'aspect-16/11',
    cardTitle: 'text-xl sm:text-2xl',
    cardBody: 'p-5 sm:p-6',
  },
} as const

const container: Variants = {
  hidden: {},
  visible: { transition: { staggerChildren: 0.1, delayChildren: 0.05 } },
}

const item: Variants = {
  hidden: { opacity: 0, y: 12, filter: 'blur(6px)' },
  visible: {
    opacity: 1,
    y: 0,
    filter: 'blur(0px)',
    transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },
  },
}

const mediaItem: Variants = {
  hidden: { opacity: 0, y: 24, filter: 'blur(8px)' },
  visible: {
    opacity: 1,
    y: 0,
    filter: 'blur(0px)',
    transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },
  },
}

function Reveal({
  active,
  variants,
  className,
  children,
}: Readonly<{
  active: boolean
  variants?: Variants
  className?: string
  children: React.ReactNode
}>) {
  if (!active) return <div className={className}>{children}</div>

  return (
    <motion.div variants={variants ?? item} className={className}>
      {children}
    </motion.div>
  )
}

function FeatureCard({
  card,
  vs,
}: Readonly<{ card: Hero08Card; vs: (typeof variantStyles)[keyof typeof variantStyles] }>) {
  const titleClass = card.invert ? 'text-white' : 'text-foreground'
  const subtitleClass = card.invert ? 'text-white/80' : 'text-muted-foreground'

  return (
    <div
      className={cn(
        'relative isolate w-full overflow-hidden rounded-md outline outline-black/10 dark:outline-white/10',
        vs.card,
      )}
    >
      {card.image && (
        <img
          src={card.image}
          alt={card.imageAlt ?? ''}
          decoding="async"
          className="absolute inset-0 -z-10 size-full object-cover"
        />
      )}

      {card.invert && (
        <div
          aria-hidden
          className="absolute inset-0 -z-10 bg-linear-to-br from-black/40 via-black/15 to-transparent"
        />
      )}

      <div className={cn('flex h-full flex-col items-start', vs.cardBody)}>
        <h3 className={cn('font-semibold tracking-tight', vs.cardTitle, titleClass)}>
          <Balancer>{card.title}</Balancer>
        </h3>
        <p className={cn('mt-1 text-sm', subtitleClass)}>{card.subtitle}</p>
        {card.cta?.ctaEnabled && (
          <div className="mt-4">
            <Cta cta={card.cta} invert={card.invert} />
          </div>
        )}
      </div>
    </div>
  )
}

export function Hero08({
  title,
  description,
  socialProof,
  avatars,
  cards,
  animation = 'none',
  variant = 'standard',
}: Readonly<Hero08Props>) {
  const reduce = useReducedMotion()
  const animate = animation === 'subtle' && !reduce
  const vs = variantStyles[variant]

  const titleElement = title && (
    <h1
      className={cn(
        'text-foreground font-serif font-normal tracking-tight text-balance',
        vs.title,
      )}
    >
      <Balancer>{title}</Balancer>
    </h1>
  )

  const descriptionElement = description && (
    <p className={cn('text-muted-foreground max-w-sm', vs.description)}>
      <Balancer>{description}</Balancer>
    </p>
  )

  const socialProofElement = (socialProof || avatars?.length) && (
    <div className="flex flex-col items-start gap-3">
      {socialProof && (
        <p className="text-foreground text-sm font-semibold">{socialProof}</p>
      )}
      {avatars?.length ? (
        <div className="flex -space-x-2.5">
          {avatars.map((a) => (
            <Avatar
              key={a.src}
              className="ring-background size-9 ring-2"
            >
              <AvatarImage src={a.src} alt="" />
              <AvatarFallback className="text-xs">{a.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </div>
      ) : null}
    </div>
  )

  const cardsElement = cards?.length ? (
    <div className={cn('grid grid-cols-1 md:grid-cols-2', vs.grid)}>
      {cards.map((card) => (
        <FeatureCard key={card.title} card={card} vs={vs} />
      ))}
    </div>
  ) : null

  return (
    <section className="bg-background relative isolate w-full overflow-hidden">
      <motion.div
        className={cn(
          'relative z-10 mx-auto flex max-w-6xl flex-col px-6',
          vs.section,
          vs.content,
        )}
        variants={animate ? container : undefined}
        initial={animate ? 'hidden' : false}
        whileInView={animate ? 'visible' : undefined}
        viewport={{ once: true, margin: '-80px' }}
      >
        <Reveal
          active={animate}
          className={cn(
            'grid grid-cols-1 items-end lg:grid-cols-2',
            vs.header,
          )}
        >
          {titleElement}
          <div className="flex flex-col items-start gap-5">
            {descriptionElement}
            {socialProofElement}
          </div>
        </Reveal>

        <Reveal active={animate} variants={mediaItem} className="w-full">
          {cardsElement}
        </Reveal>
      </motion.div>
    </section>
  )
}
```

### `registry/blocks/hero/hero-08/hero-08-example.tsx`

```tsx
import { Hero08, type Hero08Props } from './hero-08'

export const values = {
  title: 'Learn the craft behind every great design',
  description:
    'Hands-on courses taught by working designers, built to fit around your schedule.',
  socialProof: 'Join 40,000+ Makers Learning With Us',
  avatars: [
    {
      src: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=64&q=80',
      fallback: 'JD',
    },
    {
      src: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=64&q=80',
      fallback: 'SL',
    },
    {
      src: 'https://images.unsplash.com/photo-1633332755192-727a05c4013d?w=64&q=80',
      fallback: 'MV',
    },
  ],
  cards: [
    {
      title: 'Design Fundamentals',
      subtitle: 'Starting at $ 29 per month',
      image:
        'https://images.unsplash.com/photo-1746467364902-ab40952e33fe?q=80&w=1131&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
      imageAlt: 'Designers collaborating at a shared desk',
      invert: true,
      cta: {
        ctaEnabled: true,
        text: 'Get Started',
        link: '',
        size: 'default',
      },
    },
    {
      title: 'Advanced Motion',
      subtitle: 'Starting at $ 29 per month',
      image:
        'https://images.unsplash.com/photo-1578301978018-3005759f48f7?q=80&w=1144&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
      imageAlt: 'Designer working on a laptop in a studio',
      invert: true,
      cta: {
        ctaEnabled: true,
        text: 'Get Started',
        link: '',
        size: 'default',
      },
    },
  ],
  animation: 'subtle',
} satisfies Hero08Props

export function Hero08Example() {
  return <Hero08 {...values} />
}
```
