This component no longer exists. It was in ns-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-07 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Hero Dipole Field
ns-ui/hero-dipole-fieldRemovedComponent
Full-bleed hero where the headline exists twice: crisp DOM type above, and beneath it a canvas field of iron-filing strokes solved from a two-pole dipole field, so the type visibly iron-files into existence as the cursor approaches and CTA hover bends the whole field toward the button.
Live preview
live · rendered by the registry
Rendered by ns-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://design.helpmarq.com/r/hero-dipole-field.jsonSource
1"use client";23import { useEffect, useRef } from "react";45// ---------------------------------------------------------------------------6// LodestoneHero — full-bleed hero where the headline exists twice: crisp DOM7// type above, and beneath it a canvas field of iron-filing strokes solved from8// a two-pole dipole field (cursor pole + fixed anchor pole behind the primary9// CTA). Filings pack densely only inside the headline's letter-mask, so the10// type visibly "iron-files" into existence as the cursor approaches; hovering11// the CTA doubles its pole strength and bends the whole field toward the12// button. Vector-field solver rendering: each filing is a short line segment13// whose angle chases atan2 of the summed field vector with critically-damped14// easing. Canvas 2D, refs-only hot path, breath-cycle ambient drift with real15// sleep windows between breaths.16// ---------------------------------------------------------------------------1718type Vec3 = [number, number, number];1920function parseColor(raw: string): Vec3 | null {21 const s = raw.trim();22 if (s.startsWith("#")) {23 const hex = s.slice(1);24 if (hex.length === 3) {25 const r = parseInt(hex.slice(0, 1) + hex.slice(0, 1), 16);26 const g = parseInt(hex.slice(1, 2) + hex.slice(1, 2), 16);27 const b = parseInt(hex.slice(2, 3) + hex.slice(2, 3), 16);28 return Number.isNaN(r + g + b) ? null : [r, g, b];29 }30 if (hex.length >= 6) {31 const r = parseInt(hex.slice(0, 2), 16);32 const g = parseInt(hex.slice(2, 4), 16);33 const b = parseInt(hex.slice(4, 6), 16);34 return Number.isNaN(r + g + b) ? null : [r, g, b];35 }36 return null;37 }38 const m = s.match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/);39 return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;40}4142function mix(a: Vec3, b: Vec3, t: number): Vec3 {43 return [44 Math.round(a[0] + (b[0] - a[0]) * t),45 Math.round(a[1] + (b[1] - a[1]) * t),46 Math.round(a[2] + (b[2] - a[2]) * t),47 ];48}4950// deterministic prng — filing placement is stable across re-renders51function mulberry32(a: number) {52 return () => {53 a |= 0;54 a = (a + 0x6d2b79f5) | 0;55 let t = Math.imul(a ^ (a >>> 15), 1 | a);56 t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;57 return ((t ^ (t >>> 14)) >>> 0) / 4294967296;58 };59}6061// 2-octave value noise — seeds each filing's ambient-drift character62function hash2(x: number, y: number) {63 const n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453123;64// … truncated
