Heroes

Large rounded cards inset from the screen edges: a photo hero for inner pages, a video hero for the home page.

Page hero

The header of inner pages. It leaves room at the top for the floating header, and its title takes the accent on its last words.

An inner page, inset from the edges

Darkened background photo, thin title whose last words take the accent.

Updated on October 24, 2026
TSDemo.tsx
<PageHero
  title="An inner page, inset from the edges"
  description="Darkened background photo, thin title whose last words take the accent."
  meta="Updated on October 24, 2026"
  image="/media/page.webp"
/>

Without a photo, a subtle glow

The default background when no image is given.

TSDemo.tsx
<PageHero title="Without a photo, a subtle glow" description="The default background when no image is given." />
PropTypeDefaultDescription
titlestring–Title; *asterisks* mark the accent, otherwise the last words take it.
accentLastnumber1 or 2Trailing words in the accent when there are no asterisks (2 for titles over three words).
descriptionReactNode–Lead under the title.
metaReactNode–Discreet line: date, breadcrumb…
imagestring | ReactNode–Background photo URL, or a ready element such as Next.js <Image fill />. Without it, a subtle accent glow.
imagePositionstring"50% 50%"object-position of the photo.
childrenReactNode–Pills under the text.

Video hero

For the home page: muted videos that take turns with a crossfade, a soft parallax and dark veils. No video is chosen in the server render, so server and client match; the video fades in after hydration. With reduced motion, only the still poster shows.

Interfaces in deep black.

Muted videos crossfade behind dark veils that keep the text readable.

TSDemo.tsx
<HeroVideo
  clips={[
    { webm: "/videos/hero.webm", mp4: "/videos/hero.mp4", poster: "/videos/hero.jpg" },
    { webm: "/videos/hero-2.webm", mp4: "/videos/hero-2.mp4", poster: "/videos/hero-2.jpg" },
  ]}
>
  <Heading as="h1" size="display" className="trc-enter">Interfaces in *deep black.*</Heading>
  <Text size="lg">Muted videos crossfade behind dark veils that keep the text readable.</Text>
  <PillLink href="/start" solid>Get started</PillLink>
</HeroVideo>

Prepare each video muted, 1280 px wide, 24–30 fps and under 25 seconds; aim for 1.5–2.5 MB per file:

ffmpeg -i src.mp4 -an -vf "scale=1280:-2,fps=30" -c:v libx264 -preset slow -crf 28 -pix_fmt yuv420p -movflags +faststart hero.mp4
ffmpeg -i src.mp4 -an -vf "scale=1280:-2,fps=30" -c:v libvpx-vp9 -b:v 0 -crf 40 -row-mt 1 hero.webm
ffmpeg -ss 1 -i src.mp4 -frames:v 1 -vf "scale=1280:-2" -q:v 5 hero.jpg
PropTypeDefaultDescription
clips{ webm?, mp4?, poster? }[]–Muted videos played in turn with a crossfade; the first one is picked at random.
childrenReactNode–Hero text, bottom left.
asideReactNode–Optional block, bottom right on large screens.
fadenumber1.2Crossfade duration in seconds.
parallaxbooleantrueThe video moves slower than the page.

Back to top

A floating accent button that appears once the first screen is scrolled past. This page has one: scroll down and look at the bottom right corner.

<BackToTop />
PropTypeDefaultDescription
labelstring"Back to top"Accessible name.
thresholdnumber0.8Scroll, in viewport heights, before it appears.