Page Sections

Magnetic Lattice Hero

A dark tech-grid hero whose lattice of nodes and lines bends towards the pointer like a magnet, sending out a ripple wave from wherever you click.

A full-bleed canvas draws a regular lattice of nodes joined by thin lines. As the pointer moves inside the section, nodes within the radius are pulled towards it, the lines bend with them, and nearby nodes and lines light up in a glowing neon accent. Click and a ripple spreads out from that point, pushing each node it passes 14px outward before it springs back with a brief flash. Every node returns to rest through spring integration, and a dark radial scrim behind the centred text keeps the headline readable however busy the lattice gets.

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 magnetic-lattice-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.

36
160
600
600
Active edited usageDefault props
<MagneticLatticeHero/>

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/magnetic-lattice-hero.en.json

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

Usage

import { MagneticLatticeHero } from "@/components/ui/magnetic-lattice-hero";

<MagneticLatticeHero
  eyebrow="格點雲 2.0 開發者預覽"
  title="讓每一次部署都看得見"
  subtitle="服務、佇列與資料庫的連線狀態畫成一張即時拓樸圖。"
  primaryAction={{ label: "建立免費專案", href: "/signup" }}
  secondaryAction={{ label: "閱讀文件", href: "/docs" }}
  gap={36}
  radius={160}
  diagonal
/>

Props

PropTypeDefaultDescription
titleReactNode—Main title (required)
eyebrowReactNode—Small label above the title, shown as an accent pill
subtitleReactNode—Subtitle below the title
primaryActionMagneticLatticeHeroAction—Primary CTA with an accent background and an arrow icon
secondaryActionMagneticLatticeHeroAction—Secondary CTA, outlined
as"h1" | "h2""h1"Heading level; use "h2" when the page already has an h1
minHeightnumber560Minimum section height (px)
gapnumber36Lattice spacing (px)
radiusnumber160Magnetic radius around the pointer (px); nodes inside it are pulled towards the pointer
strengthnumber24Maximum pull (px); the actual offset is (1 - d/r)² × strength
ripplebooleantrueWhether a press sends out a ripple from that point
diagonalbooleanfalseAlso draw diagonals, turning the grid into a triangle mesh
surface"dark" | "auto""dark"dark is always dark; auto follows the site light/dark theme
rippleSpeednumber—Ripple expansion speed (px/s), written to --wb-magnetic-lattice-hero-ripple-speed; falls back to the CSS variable (default 600)
durationnumber—Entrance duration (ms), written to --wb-magnetic-lattice-hero-duration; falls back to the CSS variable (default 600)
onRipple(point: MagneticLatticeHeroRipplePoint) => void—Called every time a ripple is spawned
classNamestring—Merged into the root section

MagneticLatticeHeroAction

FieldTypeDefaultDescription
labelstring—Button label
hrefstring—Renders an <a> when provided, otherwise a <button type="button">
onClick(event: React.MouseEvent<HTMLElement>) => void—Click handler

MagneticLatticeHeroRipplePoint

FieldTypeDefaultDescription
xnumber—Horizontal coordinate of the ripple origin (relative to the root section, px)
ynumber—Vertical coordinate of the ripple origin (relative to the root section, px)
source"pointer" | "focus"—Triggered by a pointer press or by keyboard focus on a CTA

How it works

  • Every node has a rest position and a current position, and chases its target each frame through spring integration (stiffness 0.08, damping 0.82). Once everything is back at rest the loop stops on its own and wakes again on the next pointer move.
  • Magnetism only responds to mouse and pen: nodes inside the radius move towards the pointer by (1 - d/r)² × strength, never further than their distance to it. Touch drags do not pull the lattice, but a tap still sends out a ripple.
  • The ripple ring is 40px wide and expands at --wb-magnetic-lattice-hero-ripple-speed (default 600px/s) until it reaches the corner farthest from its origin. Up to 4 run at once; beyond that the oldest is dropped.
  • The glow (shadowBlur) is only drawn on bright nodes and the whole lattice is stroked in one pass, keeping each frame cheap. Canvas DPR is capped at 2, a ResizeObserver rebuilds the lattice, an IntersectionObserver pauses it when scrolled out of view, and everything is cleaned up on unmount.
  • Overridable CSS variables: --wb-magnetic-lattice-hero-accent (dark defaults to #2dd4bf; auto uses #0d9488 in light and #2dd4bf in dark), --wb-magnetic-lattice-hero-ripple-speed and --wb-magnetic-lattice-hero-duration. Colours are read inside the effect and re-read on theme changes when surface="auto".
  • Entrance order: eyebrow, title, subtitle and 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's useId.
  • The canvas and the radial scrim are both aria-hidden and purely decorative; the scrim behind the text keeps the headline contrasted against the lattice.
  • CTAs are <a> elements or type="button" buttons with a focus-visible ring; focusing a CTA from the keyboard fires one decorative ripple at the button.
  • With prefers-reduced-motion (reduce motion) enabled, only a static lattice is drawn — no pointer follow and no ripples — and the entrance becomes a 150ms fade with no stagger.