# Expanding Hero: Event

> A conference hero with an aftermovie and a bolder headline. The date and city chips live inside the halves, so they fly off with the words.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/expanding-hero-event-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/expanding-hero-event-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/expanding-hero-event-demo-light-f62218da29235d9a.png
- Registry item: https://keyframeui.com/r/expanding-hero-event-demo.json
- Categories: hero, scroll, video
- Listed: 2026-10-14, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/expanding-hero-event-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/expanding-hero.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/expanding-hero#expanding-hero-event-demo

## Files

### `registry/demos/expanding-hero-event-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { ExpandingHero } from "@/components/ui/expanding-hero";

const film = "https://assets.mixkit.co/videos/13222/13222-720.mp4";
const poster = "https://assets.mixkit.co/videos/13222/13222-thumb-720-0.jpg";

const days = [
  {
    day: "Day 1",
    date: "Thu, Sept 24",
    sessions: ["Opening keynote", "The craft of small details", "Designing for motion", "Evening party"],
  },
  {
    day: "Day 2",
    date: "Fri, Sept 25",
    sessions: ["Tools for thinking", "Building design systems that last", "Live redesign", "Closing keynote"],
  },
];

export function ExpandingHeroEventDemo() {
  const scrollRef = React.useRef<HTMLDivElement>(null);
  const filmRef = React.useRef<HTMLVideoElement>(null);

  return (
    <div className="relative w-full overflow-hidden rounded-xl border border-zinc-200 bg-white text-zinc-950 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50">
      <div ref={scrollRef} className="relative h-[var(--demo-height,36rem)] overflow-y-auto overscroll-contain">
        <ExpandingHero
          containerRef={scrollRef}
          stageClassName="h-[var(--demo-height,36rem)]"
          headlineClassName="font-bold tracking-[-0.06em] text-[clamp(3rem,19cqw,7rem)] @3xl:text-[clamp(3rem,11cqw,11rem)]"
          windowClassName="@3xl:w-[1.8em]"
          radius="0.3em"
          // Anything inside a half travels with it, so the chips fly off with the words.
          before={
            <span className="relative">
              Config
              <Chip className="bottom-full left-[0.08em] mb-3">
                <span className="size-1.5 rounded-full bg-orange-500" />
                Sept 24–25
              </Chip>
            </span>
          }
          after={
            <span className="relative">
              2026
              <Chip className="top-full right-[0.08em] mt-3">San Francisco</Chip>
            </span>
          }
          media={<video ref={filmRef} src={film} poster={poster} autoPlay muted loop playsInline />}
          overlay={
            <button
              type="button"
              onClick={() => watch(filmRef.current)}
              className="flex items-center gap-3 rounded-full bg-white py-2 pr-5 pl-2 text-sm font-medium text-zinc-950 shadow-xl shadow-black/30 transition-transform duration-150 ease-out active:scale-[0.97]"
            >
              <span className="grid size-8 place-items-center rounded-full bg-orange-500 text-white">
                <svg viewBox="0 0 16 16" aria-hidden className="ml-0.5 size-3 fill-current">
                  <path d="M4 2.6v10.8a.6.6 0 0 0 .9.5l8.6-5.4a.6.6 0 0 0 0-1L4.9 2.1a.6.6 0 0 0-.9.5Z" />
                </svg>
              </span>
              Watch the 2025 aftermovie
            </button>
          }
        >
          <p className="mt-12 max-w-xs text-base text-balance text-zinc-600 @3xl:mt-6 sm:text-lg dark:text-zinc-400">
            Two days on design, craft and the tools behind them.
          </p>
          <a
            href="#tickets"
            className="rounded-full bg-orange-500 px-5 py-2.5 text-sm font-medium text-white transition-[background-color,scale] duration-150 ease-out hover:bg-orange-600 active:scale-[0.97]"
          >
            Get tickets
          </a>
        </ExpandingHero>

        <section className="mx-auto max-w-3xl px-6 py-16 sm:py-24">
          <h2 className="text-sm font-medium text-zinc-500">Schedule</h2>
          <div className="mt-6 grid gap-10 sm:grid-cols-2">
            {days.map((day) => (
              <div key={day.day}>
                <p className="flex items-baseline justify-between border-b border-zinc-950/10 pb-3 dark:border-white/10">
                  <span className="text-lg font-semibold tracking-tight">{day.day}</span>
                  <span className="text-sm text-zinc-500">{day.date}</span>
                </p>
                <ul className="mt-3 space-y-2.5 text-sm text-zinc-600 dark:text-zinc-400">
                  {day.sessions.map((session) => (
                    <li key={session}>{session}</li>
                  ))}
                </ul>
              </div>
            ))}
          </div>
        </section>
      </div>
    </div>
  );
}

function Chip({ className, children }: { className?: string; children: React.ReactNode }) {
  return (
    <span
      className={`absolute flex items-center gap-2 rounded-full px-3 py-1.5 text-[13px] leading-none font-medium tracking-normal whitespace-nowrap text-zinc-600 ring-1 ring-zinc-950/10 dark:text-zinc-300 dark:ring-white/15 ${className}`}
    >
      {children}
    </span>
  );
}

/** Plays the film from the top, full screen. */
function watch(video: HTMLVideoElement | null) {
  if (!video) return;
  video.currentTime = 0;
  void video.play();
  if (video.requestFullscreen) void video.requestFullscreen();
  else (video as HTMLVideoElement & { webkitEnterFullscreen?: () => void }).webkitEnterFullscreen?.();
}
```
