Drifting Logos Hero
A centred headline over a field of partner marks drifting slowly at different speeds and directions, with a subtle parallax that follows the pointer.
Behind a centred headline, subtitle and CTAs sits a layer of fictional partner marks (circle + letter, triangle, wave or a plain wordmark). Each mark derives its position, drift amplitude (12–40px), period (18–36s) and rotation (±6°) from an index hash and drifts back and forth along a small ellipse; when the pointer moves inside the section the whole layer shifts up to ±12px, scaled by each mark's depth. The drift is pure CSS transform, and no mark is placed inside the centre 40% width reserved for 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 drifting-logos-hero.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<DriftingLogosHero/>
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/drifting-logos-hero.en.jsonOr, once registries are configured in components.json, install it as @webberui/drifting-logos-hero.
Usage
import { DriftingLogosHero } from "@/components/ui/drifting-logos-hero";
<DriftingLogosHero
eyebrow="已有 120+ 個團隊導入"
title="把出貨流程交給一張看板"
subtitle="訂單、倉儲、配送狀態集中在同一個畫面。"
primaryAction={{ label: "免費試用", href: "/signup" }}
secondaryAction={{ label: "看客戶案例", href: "/customers" }}
marks={[
{ label: "北辰物流" },
{ label: "Orbis", shape: "circle" },
{ label: "Peakline", shape: "triangle" },
{ label: "潮聲音樂", shape: "wave" },
]}
density={16}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | Main headline (required) |
eyebrow | ReactNode | — | Small label above the title, shown in the accent colour |
subtitle | ReactNode | — | Subtitle below the headline |
primaryAction | DriftingLogosHeroAction | — | Primary CTA with an accent background |
secondaryAction | DriftingLogosHeroAction | — | Secondary, outlined CTA |
as | "h1" | "h2" | "h1" | Heading level; use "h2" when the page already has an h1 |
minHeight | number | 560 | Minimum section height (px) |
marks | DriftingLogo[] | 12 fictional marks | Partner marks drifting in the background; reused cyclically when fewer than density |
density | number | 12 | Number of background marks, capped at 24 |
parallax | boolean | true | Shift the marks layer up to ±12px with the pointer, scaled by depth |
speed | number | — | Drift speed multiplier written to --wb-drifting-logos-hero-speed; higher is faster, falls back to the CSS variable (default 1) |
duration | number | — | Entrance duration (ms) written to --wb-drifting-logos-hero-duration; falls back to the CSS variable (default 600) |
announceMarks | boolean | false | Announce partner names to screen readers via an sr-only list |
marksLabel | string | "合作夥伴" (Partners) | Accessible name of the sr-only list |
className | string | — | Merged into the root section |
DriftingLogo
| Field | Type | Default | Description |
|---|---|---|---|
label | string | — | Mark text, also the name read out by the sr-only list |
shape | "circle" | "triangle" | "wave" | "text" | "text" | Mark shape; circle puts the first character of the label inside a ring |
DriftingLogosHeroAction
| Field | Type | Default | Description |
|---|---|---|---|
label | string | — | Button label |
href | string | — | Renders an <a> when set, otherwise a <button type="button"> |
onClick | (event: React.MouseEvent<HTMLElement>) => void | — | Click handler |
How it works
- Deterministic layout: an 8 × 5 grid minus the headline zone, picked in index-hash order and jittered ±30% within each cell, so SSR and client render the exact same composition with no hydration mismatch.
- The drift is an inline CSS
@keyframesalong an ellipse withalternate; negative delays start each mark at a different point on its path, and the period is divided by--wb-drifting-logos-hero-speed. - Parallax uses
useMotionValue+useSpring(stiffness 120, damping 20) and only responds to mouse and pen, so touch drags do not make the marks jump. - Hovering a single mark raises it to full opacity and switches it to the accent colour (200ms).
- Overridable CSS variables:
--wb-drifting-logos-hero-accent(default#0d9488,#2dd4bfin dark mode),--wb-drifting-logos-hero-speed,--wb-drifting-logos-hero-duration. - Entrance order: eyebrow, title, subtitle, CTAs rise 16px and fade in, 80ms apart, easing
cubic-bezier(0.22,1,0.36,1).
Accessibility
- The root is a
<section aria-labelledby>pointing at the title'suseId. - The marks layer is
aria-hiddenand purely decorative; turn onannounceMarksto expose partner names through an sr-only list. - CTAs are
<a>elements ortype="button"buttons with a focus-visible ring. - With reduce motion enabled, all drifting stops and parallax is disabled for a static composition; the entrance becomes a 150ms fade with no stagger.
Count-Up Stats Hero
A dark hero React component: a left-aligned headline over 2–4 key figures. On entering the viewport the dividers draw in, each figure counts up from 0 in sequence, and an accent underline draws on completion.
Film Strip Hero
A hero with a sprocketed film strip looping endlessly beneath the headline; the faster the page scrolls, the faster the film runs, and hovered frames enlarge.