# Hero Kinetic (Moonarc)

A first screen in type as large as it will go: the name or the claim in a few lines that rise once, one after another, from masks of their own (SplitText by line). A line of roles can lead them, its word turning once through the list and settling within five seconds (RotatingText, fluid, with cycles), as in "I design / build / write". Under the type, an optional reel: a muted loop that plays while it is on screen, opens from a rounded inset to the full width as the page scrolls (ScrollExpand), and has a visible play and pause button. Zero JavaScript without a reel.

- Pro block. Install: `npx shadcn@latest add @moonarc-pro/hero-kinetic` (license key required)
- Tier B · category section · trigger load, scroll
- Readout: `<HeroKinetic headline={"Design for\ncompanies that\nmake things."} video={{ … }}>`
- Browser support: limited (Chrome 115 · not Firefox · Safari 26); elsewhere: without scroll timelines (Firefox) the reel is full width from the start; the lines still rise and the word still turns
- Measured cost: 0 B JS of its own · uses Reveal + runtime (with dependencies 3.0 kB raw; CSS 22.7 kB raw)
- Page: https://moonarc.dev/components/hero-kinetic/

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `headline` | `string` | none | The big type, one line per \n; each line rises from its own mask, 110 ms after the one before. The headings' text is the whole string (SplitText keeps one copy for screen readers). |
| `roles` | `{ before?: string; words: string[]; after?: string; position?: 'start' | 'end' }` | none | A line of roles before the headline (start, default) or after it: before, then a word turning through words, then after. The word turns once and settles on the first, the round inside 4.5 s whatever the number of words (WCAG 2.2.2), in the theme's primary colour; a screen reader hears the words as a list. |
| `eyebrow` | `string` | none | A short line over the type, in mono. Slot "eyebrow" wins. |
| `copy` | `string` | none | A paragraph under the type. Slot "copy" wins. |
| `facts` | `string[]` | `[]` | Short facts in a mono row under the type: a city, a year, what the studio does. |
| `level` | `1 | 2` | `1` | Heading level: 1 on the page's own hero, 2 under another h1. |
| `video` | `{ src: string | string[]; poster: string; label: string; caption?: string }` | none | The reel under the type: webm first, then mp4, silent (it plays muted). label is what it shows, for a reader who cannot see it; caption a line beside the button. It plays while a quarter of it is on screen and pauses off screen; the button pauses it for good, until the reader plays it again. Without JavaScript the video's own controls are shown instead. |
| `playLabel` | `string` | `'Play the reel'` | Accessible name of the reel's button; aria-pressed says whether the reel plays. |
| `expand` | `number` | `0.72` | Starting width of the reel as a share of the full width, 0.2–1; it opens to the full width as the page scrolls. |
| `tone` | `'surface' | 'muted' | 'ink' | 'brand'` | `unset (paints nothing)` | The ground: the page surface, a muted band, an always-dark ink panel, or a deep brand panel. The turning word is the theme's primary on an unset, surface or muted ground, and the text colour on ink and brand. |
| `density` | `'compact' | 'cozy' | 'roomy'` | `'cozy'` | Block padding ×0.8 / ×1 / ×1.25 through --ma-density. |
| `align` | `'start' | 'center'` | `'start'` | The type on the inline start or centred. |

## Usage

```astro
---
import HeroKinetic from '@/components/moonarc/pro/HeroKinetic.astro';
---
<!-- a studio: three lines, and the reel under them -->
<HeroKinetic
  eyebrow="Northfold, a design studio in Rotterdam"
  headline={'Identity, web\nand motion for\nnew companies.'}
  facts={['Est. 2014', '14 people', 'Rotterdam and Jakarta']}
  video={{ src: ['/video/reel.webm', '/video/reel.mp4'], poster: '/video/reel.jpg', label: 'The studio\'s reel: posters, packaging and websites from four projects', caption: 'Reel 2026' }}
/>

<!-- one person: the role turns once and settles on the first -->
<HeroKinetic
  eyebrow="Ines Vallory"
  roles={{ before: 'I', words: ['design', 'build', 'write'], after: 'websites' }}
  headline={'for companies with\nsomething to show.'}
/>
```

## Reduced motion

The lines and the roles are in place from the start (they fade in, without travel), the word stays on the first, the reel is full width and nothing plays by itself: the button is the only way to start it.

## With ClientRouter

The reel's script binds through the shared runtime and pauses the loop before the swap; the lines and the word are CSS and play again on the next page's own hero.

## Craft

- Each line rises from a mask of its own and the next follows 110 ms later: the type assembles in about half a second, once, and then holds still, which is what lets it be this large.
- The turning word settles: once through the list inside 4.5 s, then the first word for good. A word that turns forever beside the page's main heading is motion no one asked for (WCAG 2.2.2), and a pointer over it pauses it before that.
- The reel plays only while it is on screen and never under reduced motion; the button says with aria-pressed whether it plays, and a pause the reader asked for holds when the reel comes back into view.
- The native controls are the fallback: a reader without JavaScript gets a video that plays on demand, and the script takes them off only when it can offer its own button.
- The type is sized by the viewport (up to 9.5 rem) and the reel runs edge to edge: a hero is the page's scale, not a container's.

## Replaces

- Studio heroes (Framer templates)
- TextReveal hero (Magic UI)
- Showreel heroes (Awwwards studio sites)

## Source

Pro block. The source is served by the license-gated registry: `npx shadcn@latest add @moonarc-pro/hero-kinetic` with a key in components.json (https://moonarc.dev/account/). Everything it composes is free: split-text, rotating-text, reveal, scroll-expand, animated-icons.
