Skip to content
Facade UI
Colour mode
GitHub (opens in a new tab)
blockmotion

Hero — with media (motion)

Media hero with a staggered entrance played on mount.

Wraps the static Hero — with media, which renders identically with JavaScript disabled.

Preview

Open full width (opens in a new tab)
Preview width

Installation

Package manager
pnpm dlx shadcn@latest add https://facadeui.dev/r/hero-with-media-motion.json

Pulls in hero-with-media, motion-primitives. The CLI installs them for you.

Usage

The exact source of the preview above.

demos/hero-with-media-motion.tsx
"use client"

/**
 * The `hero-with-media` demo, with the motion variant swapped in.
 *
 * Identical props: the motion variant only changes which slot components
 * the static section renders through.
 *
 * `"use client"` is required here, not incidental: the motion variant is a
 * client component, and icon components cannot cross a server-to-client
 * boundary as props. See the note on `IconComponent`.
 */

import { HeroWithMediaMotion } from "@registry/sections/hero-with-media-motion"

import { ACTIONS } from "./content"

export function Demo() {
  return (
    <HeroWithMediaMotion
      eyebrow="Overlay"
      title="Copy over media, with contrast guaranteed"
      description="The scrim is always painted, because text over an arbitrary image has no predictable contrast ratio."
      actions={ACTIONS}
      headingLevel={1}
      spacing="md"
      // Decorative: it carries nothing the copy does not already say.
      media={
        <div
          role="presentation"
          className="size-full bg-[radial-gradient(circle_at_20%_20%,#3b4a6b,transparent_55%),radial-gradient(circle_at_80%_30%,#6b3b5a,transparent_50%),linear-gradient(140deg,#11151f,#2a2140)]"
        />
      }
    />
  )
}

Source

What the CLI copies into your project, byte for byte.

components/sections/hero-with-media-motion.tsx
"use client"

/**
 * HeroWithMediaMotion — the media hero with a staggered entrance.
 *
 * A genuine wrapper: it renders the static `HeroWithMedia` and only swaps the stack
 * slots. Nothing visual and no accessibility decision is duplicated, so the two
 * variants cannot drift apart.
 *
 * The stagger plays on mount rather than on scroll — a hero is above the fold by
 * definition, and waiting for an intersection that already happened would just
 * leave it invisible for a frame.
 *
 * Dependencies: react, @/components/motion/slots,
 * @/components/sections/hero-with-media.
 */

import { StaggerBlock, StaggerStack } from "@/components/motion/slots"
import {
  HeroWithMedia,
  type HeroWithMediaProps,
} from "@/components/sections/hero-with-media"

export type HeroWithMediaMotionProps = Omit<HeroWithMediaProps, "stackAs" | "blockAs">

export function HeroWithMediaMotion(props: HeroWithMediaMotionProps) {
  return <HeroWithMedia {...props} stackAs={StaggerStack} blockAs={StaggerBlock} />
}

Props

HeroWithMediaMotionProps

Extends Omit<HeroWithMediaProps, "stackAs" | "blockAs">.