Page Sections

Blur-In Headline Hero

A minimal centred React hero where the headline rises character by character out of a blur and the single call-to-action breathes with a slow halo while everything else stays still; Chinese splits per character, English keeps words intact, and every timing is a CSS variable.

A hero with generous whitespace, everything centred: eyebrow (accent dot + text) → large headline → subtitle → one pill-shaped primary button plus an optional plain-text secondary link. Only two things move. The headline rises grapheme by grapheme out of a blur, and 150ms after it finishes the subtitle and button fade in; from then on the primary button breathes with a slow scale and outer halo. Hovering stops the breathing and instead lifts the button slightly and nudges the arrow 2px to the right. The background is a flat colour with no decoration, with an optional 48px hairline above the headline.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add blur-in-headline-hero.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

30
2.4
Active edited usageDefault props
<BlurInHeadlineHero/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/blur-in-headline-hero.en.json

Or, once registries are configured in components.json, install it as @webberui/blur-in-headline-hero.

Usage

import { BlurInHeadlineHero } from "@/components/ui/blur-in-headline-hero";

<BlurInHeadlineHero
  eyebrow="Stillwater Notes | New release"
  title="Turn off the noise. Keep the one line you came to write."
  subtitle="A writing space that stays out of your way: no notifications, no sidebars."
  primaryAction={{ label: "Start writing free", href: "/signup" }}
  secondaryAction={{ label: "See how it works", href: "#how" }}
/>

To change the pacing or accent, override the CSS variables:

<div
  style={{
    "--wb-blur-in-headline-hero-accent": "#7c3aed",
    "--wb-blur-in-headline-hero-stagger": "45ms",
    "--wb-blur-in-headline-hero-breath": "3.2s",
  } as React.CSSProperties}
>
  <BlurInHeadlineHero title="Slower is fine, too" primaryAction={{ label: "Begin" }} />
</div>

Props

PropTypeDefaultDescription
titleReactNode—Headline (required); a string animates glyph by glyph, a ReactNode fades and rises as one block
eyebrowReactNode—Small eyebrow above the headline, preceded by an accent dot
subtitleReactNode—Subtitle
primaryActionBlurInHeadlineHeroAction—The single primary CTA (pill-shaped, with a breathing halo)
secondaryActionBlurInHeadlineHeroAction—Secondary plain-text link
as"h1" | "h2""h1"Heading level used for the headline
minHeightnumber560Minimum section height (px)
splitBy"char" | "word""char"Split unit: char (each character / letter) or word (each word; Chinese is grouped into phrases at punctuation)
breathingbooleantrueWhether the CTA keeps breathing (scale + outer halo)
showRulebooleanfalseShow a 48px hairline above the headline
onEntered() => void—Called once every block has entered (also fires under reduced motion)
classNamestring—Merged into the root section

BlurInHeadlineHeroAction

FieldTypeDefaultDescription
labelstring—Button text
hrefstring—Link target; renders an a when provided
onClick() => void—Click handler; without href a type="button" button is rendered

CSS variables

VariableDefaultDescription
--wb-blur-in-headline-hero-accent#0d9488 (dark #2dd4bf)Eyebrow dot, primary button fill and breathing halo
--wb-blur-in-headline-hero-stagger30msDelay between headline glyphs
--wb-blur-in-headline-hero-duration600msEntrance duration of each glyph and block
--wb-blur-in-headline-hero-breath2.4sLength of one CTA breath

How it works

  • Splitting: the headline is split into graphemes with Intl.Segmenter (falling back to Array.from), so combined characters and emoji stay whole. Word granularity is deliberately avoided — it depends on ICU dictionaries, and server and browser can split differently, breaking hydration.
  • Line breaking: each English word is wrapped in an inline-block + nowrap group so per-letter animation never splits a word mid-way; each Chinese character stands alone and wraps normally. Closing marks such as 「,。」」 stick to the previous character and opening marks such as 「「(」 to the next, so they never start or end a line.
  • Each glyph enters with y 0.4em → 0, opacity 0 → 1 and blur(6px) → 0, delayed by stagger × step. In word mode an English word or a Chinese phrase shares one step and rises together.
  • The subtitle and button delays are computed with CSS calc() from the headline's step count, --wb-blur-in-headline-hero-stagger and --wb-blur-in-headline-hero-duration, so they still enter 150ms after the headline when you change the variables.
  • Breathing starts only after the button has entered, and pauses on hover or keyboard focus so it never fights the focus ring's shadow.

Accessibility

  • The root is a section with aria-labelledby pointing at the headline (generated with useId)
  • The split headline carries an aria-label with the full text and every glyph span is aria-hidden, so screen readers announce the sentence once
  • The primary button is an a when it has href and a type="button" button otherwise, both with a focus-visible ring; the arrow icon is hidden from assistive tech
  • When the user's system asks to reduce motion, the breathing loop stops entirely and everything fades in over 150ms without movement or blur