@okinoxis/hero-scene 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +47 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +47 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -166,7 +166,7 @@ function HeroSceneRoot({
|
|
|
166
166
|
return () => clearInterval(id);
|
|
167
167
|
}, [initialIndex, interval, images.length, onIndexChange]);
|
|
168
168
|
const activeColor = images[index]?.color ?? "128, 128, 128";
|
|
169
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
170
170
|
HeroSceneContext.Provider,
|
|
171
171
|
{
|
|
172
172
|
value: {
|
|
@@ -177,55 +177,53 @@ function HeroSceneRoot({
|
|
|
177
177
|
isInViewport,
|
|
178
178
|
containerRef: rootRef
|
|
179
179
|
},
|
|
180
|
-
children:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
180
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
ref: rootRef,
|
|
184
|
+
className,
|
|
185
|
+
style: { position: "relative", overflow: "hidden" },
|
|
186
|
+
children: [
|
|
187
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: DARK_STYLES } }),
|
|
188
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
189
|
+
"div",
|
|
190
|
+
{
|
|
191
|
+
"data-hero-images": "",
|
|
192
|
+
"aria-hidden": "true",
|
|
193
|
+
style: {
|
|
194
|
+
position: "absolute",
|
|
195
|
+
inset: "-15%",
|
|
196
|
+
backgroundColor: `rgb(${activeColor})`,
|
|
197
|
+
transition: reducedMotion ? "none" : "background-color 1s ease"
|
|
198
|
+
},
|
|
199
|
+
children: images.map((img, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
200
|
+
import_image.default,
|
|
201
|
+
{
|
|
202
|
+
src: img.src,
|
|
203
|
+
alt: "",
|
|
204
|
+
width: 1920,
|
|
205
|
+
height: 1080,
|
|
206
|
+
priority: i === initialIndex,
|
|
207
|
+
sizes: "100vw",
|
|
208
|
+
"data-hs-img": "",
|
|
209
|
+
style: {
|
|
210
|
+
position: "absolute",
|
|
211
|
+
inset: 0,
|
|
212
|
+
width: "100%",
|
|
213
|
+
height: "100%",
|
|
214
|
+
objectFit: "cover",
|
|
215
|
+
opacity: i === index ? 1 : 0,
|
|
216
|
+
transition: reducedMotion ? "none" : `opacity ${transitionDuration}ms ease`
|
|
217
|
+
}
|
|
199
218
|
},
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
sizes: "100vw",
|
|
209
|
-
"data-hs-img": "",
|
|
210
|
-
style: {
|
|
211
|
-
position: "absolute",
|
|
212
|
-
inset: 0,
|
|
213
|
-
width: "100%",
|
|
214
|
-
height: "100%",
|
|
215
|
-
objectFit: "cover",
|
|
216
|
-
opacity: i === index ? 1 : 0,
|
|
217
|
-
transition: reducedMotion ? "none" : `opacity ${transitionDuration}ms ease`
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
img.src
|
|
221
|
-
))
|
|
222
|
-
}
|
|
223
|
-
),
|
|
224
|
-
children
|
|
225
|
-
]
|
|
226
|
-
}
|
|
227
|
-
)
|
|
228
|
-
]
|
|
219
|
+
img.src
|
|
220
|
+
))
|
|
221
|
+
}
|
|
222
|
+
),
|
|
223
|
+
children
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
)
|
|
229
227
|
}
|
|
230
228
|
);
|
|
231
229
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/hero-scene.tsx","../src/hero-scene-context.ts","../src/utils.ts","../src/use-in-viewport.ts","../src/use-reduced-motion.ts"],"sourcesContent":["export {\n HeroScene,\n HeroParallax,\n HeroVignette,\n HeroBlur,\n HeroPattern,\n HeroDarkOverlay,\n HeroContent,\n} from './hero-scene'\n\n/** Event name dispatched on `globalThis` when the active image changes. `event.detail` is the new index. */\nexport const HERO_SCENE_INDEX_EVENT = 'hero-scene-index-change'\nexport { buildVignetteGradient, buildBlurMask } from './utils'\nexport { useReducedMotion } from './use-reduced-motion'\n\nexport type {\n HeroSceneProps,\n HeroImage,\n ParallaxProps,\n VignetteProps,\n BlurProps,\n PatternProps,\n DarkOverlayProps,\n ContentProps,\n} from './types'\n","'use client'\n\nimport Image from 'next/image'\nimport { useEffect, useRef, useState } from 'react'\n\nimport { HeroSceneContext, useHeroScene } from './hero-scene-context'\nimport type {\n BlurProps,\n ContentProps,\n DarkOverlayProps,\n HeroSceneProps,\n ParallaxProps,\n PatternProps,\n VignetteProps,\n} from './types'\nimport { buildBlurMask, buildVignetteGradient } from './utils'\nimport { useInViewport } from './use-in-viewport'\nimport { useReducedMotion } from './use-reduced-motion'\n\n/**\n * Injected once — handles dark mode via `.dark` ancestor class.\n * Uses data-attributes so no Tailwind scanning is needed.\n */\nconst DARK_STYLES = `\n[data-hs-dark-only]{display:none}\n[data-hs-light-only]{display:block}\n[data-hs-img]{filter:none}\n.dark [data-hs-dark-only]{display:block}\n.dark [data-hs-light-only]{display:none}\n.dark [data-hs-img]{filter:grayscale(1)}\n`\n\n// ─── Root Component ──────────────────────────────────────────\n\nfunction HeroSceneRoot({\n images,\n initialIndex = 0,\n interval = 30_000,\n transitionDuration = 700,\n className,\n onIndexChange,\n children,\n}: HeroSceneProps) {\n const [index, setIndex] = useState(initialIndex)\n const rootRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const isInViewport = useInViewport(rootRef)\n\n // ── Image rotation ──\n useEffect(() => {\n if (interval <= 0 || images.length <= 1) return\n\n let current = initialIndex\n const id = setInterval(() => {\n const next = (current + 1) % images.length\n current = next\n setIndex(next)\n setTimeout(() => {\n onIndexChange?.(next)\n globalThis.dispatchEvent(\n new CustomEvent('hero-scene-index-change', { detail: next }),\n )\n }, 0)\n }, interval)\n return () => clearInterval(id)\n }, [initialIndex, interval, images.length, onIndexChange])\n\n const activeColor = images[index]?.color ?? '128, 128, 128'\n\n return (\n <HeroSceneContext.Provider\n value={{\n images,\n index,\n transitionDuration,\n reducedMotion,\n isInViewport,\n containerRef: rootRef,\n }}\n >\n <style dangerouslySetInnerHTML={{ __html: DARK_STYLES }} />\n <div\n ref={rootRef}\n className={className}\n style={{ position: 'relative', overflow: 'hidden' }}\n >\n {/* ── Background images ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: '-15%',\n backgroundColor: `rgb(${activeColor})`,\n transition: reducedMotion ? 'none' : 'background-color 1s ease',\n }}\n >\n {images.map((img, i) => (\n <Image\n key={img.src}\n src={img.src}\n alt=\"\"\n width={1920}\n height={1080}\n priority={i === initialIndex}\n sizes=\"100vw\"\n data-hs-img=\"\"\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n opacity: i === index ? 1 : 0,\n transition: reducedMotion\n ? 'none'\n : `opacity ${transitionDuration}ms ease`,\n }}\n />\n ))}\n </div>\n\n {children}\n </div>\n </HeroSceneContext.Provider>\n )\n}\n\n// ─── Parallax ────────────────────────────────────────────────\n\nfunction Parallax({\n speed = 0.4,\n mouseShiftX = 25,\n mouseShiftY = 15,\n mouseLerp = 0.04,\n}: ParallaxProps) {\n const { reducedMotion, isInViewport, containerRef } = useHeroScene()\n\n const scrollY = useRef(0)\n const targetMouseX = useRef(0)\n const targetMouseY = useRef(0)\n const currentMouseX = useRef(0)\n const currentMouseY = useRef(0)\n\n useEffect(() => {\n if (reducedMotion) return\n\n const root = containerRef.current\n if (!root) return\n\n const imagesEl = root.querySelector<HTMLDivElement>('[data-hero-images]')\n if (!imagesEl) return\n\n let rafId: number\n let running = true\n\n function loop() {\n if (!running) return\n\n currentMouseX.current +=\n (targetMouseX.current - currentMouseX.current) * mouseLerp\n currentMouseY.current +=\n (targetMouseY.current - currentMouseY.current) * mouseLerp\n\n const y = scrollY.current * speed\n const mx = currentMouseX.current * mouseShiftX\n const my = currentMouseY.current * mouseShiftY\n imagesEl!.style.transform = `translate3d(${mx}px, ${y + my}px, 0)`\n\n rafId = requestAnimationFrame(loop)\n }\n\n function onScroll() {\n if (!isInViewport) return\n scrollY.current = globalThis.scrollY\n }\n\n function onMouseMove(e: MouseEvent) {\n if (!isInViewport) return\n targetMouseX.current = (e.clientX / globalThis.innerWidth - 0.5) * 2\n targetMouseY.current = (e.clientY / globalThis.innerHeight - 0.5) * 2\n }\n\n globalThis.addEventListener('scroll', onScroll, { passive: true })\n globalThis.addEventListener('mousemove', onMouseMove, { passive: true })\n rafId = requestAnimationFrame(loop)\n\n return () => {\n running = false\n globalThis.removeEventListener('scroll', onScroll)\n globalThis.removeEventListener('mousemove', onMouseMove)\n cancelAnimationFrame(rafId)\n }\n }, [\n reducedMotion,\n isInViewport,\n containerRef,\n speed,\n mouseShiftX,\n mouseShiftY,\n mouseLerp,\n ])\n\n return null\n}\n\n// ─── Vignette ────────────────────────────────────────────────\n\nfunction Vignette({\n centerX = 50,\n centerY = 100,\n shape = 'circle',\n stops,\n transitionDuration = 1000,\n}: VignetteProps) {\n const { images, index, reducedMotion } = useHeroScene()\n const transitionMs = reducedMotion ? '0ms' : `${transitionDuration}ms`\n\n return (\n <>\n {/* Light mode — color-matched vignettes with crossfade */}\n {images.map((img, i) => (\n <div\n key={`vignette-light-${img.color}`}\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n opacity: i === index ? 1 : 0,\n transition: `opacity ${transitionMs} ease`,\n background: buildVignetteGradient(img.color, {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n ))}\n {/* Dark mode — black vignette */}\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n background: buildVignetteGradient('0, 0, 0', {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n </>\n )\n}\n\n// ─── Blur ────────────────────────────────────────────────────\n\nfunction Blur({\n amount = 24,\n centerX = 50,\n centerY = 65,\n innerRadius = 15,\n outerRadius = 55,\n}: BlurProps) {\n useHeroScene()\n\n const mask = buildBlurMask({ centerX, centerY, innerRadius, outerRadius })\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 15,\n backdropFilter: `blur(${amount}px)`,\n WebkitBackdropFilter: `blur(${amount}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n pointerEvents: 'none',\n }}\n />\n )\n}\n\n// ─── Pattern ─────────────────────────────────────────────────\n\nfunction Pattern({\n dotSize = 1,\n spacing = 20,\n lightColor = 'rgba(0 0 0 / 0.15)',\n darkColor = 'rgba(255 255 255 / 0.1)',\n}: PatternProps) {\n useHeroScene()\n\n const bgImage = (color: string) =>\n `radial-gradient(circle, ${color} ${dotSize}px, transparent ${dotSize}px)`\n const bgSize = `${spacing}px ${spacing}px`\n\n return (\n <>\n <div\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(lightColor),\n backgroundSize: bgSize,\n }}\n />\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(darkColor),\n backgroundSize: bgSize,\n }}\n />\n </>\n )\n}\n\n// ─── DarkOverlay ─────────────────────────────────────────────\n\nfunction DarkOverlay({ opacity = 0.4 }: DarkOverlayProps) {\n useHeroScene()\n\n return (\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n pointerEvents: 'none',\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene()\n\n return (\n <div\n className={className}\n style={{ position: 'relative', zIndex: 30 }}\n >\n {children}\n </div>\n )\n}\n\n// ─── Exports ────────────────────────────────────────────────\n\nexport {\n HeroSceneRoot as HeroScene,\n Parallax as HeroParallax,\n Vignette as HeroVignette,\n Blur as HeroBlur,\n Pattern as HeroPattern,\n DarkOverlay as HeroDarkOverlay,\n Content as HeroContent,\n}\n","'use client'\n\nimport { createContext, useContext } from 'react'\nimport type { HeroSceneContextValue } from './types'\n\nexport const HeroSceneContext = createContext<HeroSceneContextValue | null>(null)\n\nexport function useHeroScene(): HeroSceneContextValue {\n const ctx = useContext(HeroSceneContext)\n if (!ctx) {\n throw new Error(\n 'HeroScene compound components (Parallax, Vignette, Blur, Pattern, DarkOverlay, Content) ' +\n 'must be rendered inside a <HeroScene> parent.',\n )\n }\n return ctx\n}\n","import type { VignetteProps, BlurProps } from './types'\n\nconst DEFAULT_STOPS: [number, number][] = [\n [0, 0],\n [15, 0.08],\n [30, 0.15],\n [45, 0.25],\n [60, 0.35],\n [75, 0.45],\n [88, 0.55],\n [100, 0.6],\n]\n\nexport function buildVignetteGradient(\n color: string,\n config: Pick<VignetteProps, 'shape' | 'centerX' | 'centerY' | 'stops'>,\n): string {\n const shape = config.shape ?? 'circle'\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 100\n const stops = config.stops ?? DEFAULT_STOPS\n\n const gradientStops = stops\n .map(([pos, opacity]) =>\n opacity === 0\n ? `transparent ${pos}%`\n : `color-mix(in srgb, rgb(${color}) ${Math.round(opacity * 100)}%, transparent) ${pos}%`,\n )\n .join(', ')\n\n return `radial-gradient(${shape} at ${cx}% ${cy}%, ${gradientStops})`\n}\n\nexport function buildBlurMask(\n config: Pick<BlurProps, 'centerX' | 'centerY' | 'innerRadius' | 'outerRadius'>,\n): string {\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 65\n const inner = config.innerRadius ?? 15\n const outer = config.outerRadius ?? 55\n\n return `radial-gradient(circle at ${cx}% ${cy}%, transparent ${inner}%, black ${outer}%)`\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\nimport type { RefObject } from 'react'\n\n/**\n * Returns true when the referenced element is at least partially visible\n * in the viewport, using IntersectionObserver. SSR-safe — defaults to true\n * so effects run immediately on first paint before the observer fires.\n */\nexport function useInViewport(ref: RefObject<HTMLElement | null>): boolean {\n const [inViewport, setInViewport] = useState(true)\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n setInViewport(entry.isIntersecting)\n },\n { threshold: 0 },\n )\n\n observer.observe(el)\n return () => observer.disconnect()\n }, [ref])\n\n return inViewport\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\n\n/**\n * Returns true when the user has enabled \"prefers-reduced-motion: reduce\"\n * in their OS or browser settings. SSR-safe — defaults to false.\n */\nexport function useReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false)\n\n useEffect(() => {\n const mql = globalThis.matchMedia('(prefers-reduced-motion: reduce)')\n setReduced(mql.matches)\n\n function onChange(e: MediaQueryListEvent) {\n setReduced(e.matches)\n }\n\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [])\n\n return reduced\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,mBAAkB;AAClB,IAAAA,gBAA4C;;;ACD5C,mBAA0C;AAGnC,IAAM,uBAAmB,4BAA4C,IAAI;AAEzE,SAAS,eAAsC;AACpD,QAAM,UAAM,yBAAW,gBAAgB;AACvC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;;;ACdA,IAAM,gBAAoC;AAAA,EACxC,CAAC,GAAG,CAAC;AAAA,EACL,CAAC,IAAI,IAAI;AAAA,EACT,CAAC,IAAI,IAAI;AAAA,EACT,CAAC,IAAI,IAAI;AAAA,EACT,CAAC,IAAI,IAAI;AAAA,EACT,CAAC,IAAI,IAAI;AAAA,EACT,CAAC,IAAI,IAAI;AAAA,EACT,CAAC,KAAK,GAAG;AACX;AAEO,SAAS,sBACd,OACA,QACQ;AACR,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,KAAK,OAAO,WAAW;AAC7B,QAAM,KAAK,OAAO,WAAW;AAC7B,QAAM,QAAQ,OAAO,SAAS;AAE9B,QAAM,gBAAgB,MACnB;AAAA,IAAI,CAAC,CAAC,KAAK,OAAO,MACjB,YAAY,IACR,eAAe,GAAG,MAClB,0BAA0B,KAAK,KAAK,KAAK,MAAM,UAAU,GAAG,CAAC,mBAAmB,GAAG;AAAA,EACzF,EACC,KAAK,IAAI;AAEZ,SAAO,mBAAmB,KAAK,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa;AACpE;AAEO,SAAS,cACd,QACQ;AACR,QAAM,KAAK,OAAO,WAAW;AAC7B,QAAM,KAAK,OAAO,WAAW;AAC7B,QAAM,QAAQ,OAAO,eAAe;AACpC,QAAM,QAAQ,OAAO,eAAe;AAEpC,SAAO,6BAA6B,EAAE,KAAK,EAAE,kBAAkB,KAAK,YAAY,KAAK;AACvF;;;ACxCA,IAAAC,gBAAoC;AAQ7B,SAAS,cAAc,KAA6C;AACzE,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,IAAI;AAEjD,+BAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AAET,UAAM,WAAW,IAAI;AAAA,MACnB,CAAC,CAAC,KAAK,MAAM;AACX,sBAAc,MAAM,cAAc;AAAA,MACpC;AAAA,MACA,EAAE,WAAW,EAAE;AAAA,IACjB;AAEA,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,GAAG,CAAC;AAER,SAAO;AACT;;;AC3BA,IAAAC,gBAAoC;AAM7B,SAAS,mBAA4B;AAC1C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAE5C,+BAAU,MAAM;AACd,UAAM,MAAM,WAAW,WAAW,kCAAkC;AACpE,eAAW,IAAI,OAAO;AAEtB,aAAS,SAAS,GAAwB;AACxC,iBAAW,EAAE,OAAO;AAAA,IACtB;AAEA,QAAI,iBAAiB,UAAU,QAAQ;AACvC,WAAO,MAAM,IAAI,oBAAoB,UAAU,QAAQ;AAAA,EACzD,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;;;AJwDM;AAzDN,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWpB,SAAS,cAAc;AAAA,EACrB;AAAA,EACA,eAAe;AAAA,EACf,WAAW;AAAA,EACX,qBAAqB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,YAAY;AAC/C,QAAM,cAAU,sBAAuB,IAAI;AAC3C,QAAM,gBAAgB,iBAAiB;AACvC,QAAM,eAAe,cAAc,OAAO;AAG1C,+BAAU,MAAM;AACd,QAAI,YAAY,KAAK,OAAO,UAAU,EAAG;AAEzC,QAAI,UAAU;AACd,UAAM,KAAK,YAAY,MAAM;AAC3B,YAAM,QAAQ,UAAU,KAAK,OAAO;AACpC,gBAAU;AACV,eAAS,IAAI;AACb,iBAAW,MAAM;AACf,wBAAgB,IAAI;AACpB,mBAAW;AAAA,UACT,IAAI,YAAY,2BAA2B,EAAE,QAAQ,KAAK,CAAC;AAAA,QAC7D;AAAA,MACF,GAAG,CAAC;AAAA,IACN,GAAG,QAAQ;AACX,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,cAAc,UAAU,OAAO,QAAQ,aAAa,CAAC;AAEzD,QAAM,cAAc,OAAO,KAAK,GAAG,SAAS;AAE5C,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,MAChB;AAAA,MAEA;AAAA,oDAAC,WAAM,yBAAyB,EAAE,QAAQ,YAAY,GAAG;AAAA,QACzD;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL;AAAA,YACA,OAAO,EAAE,UAAU,YAAY,UAAU,SAAS;AAAA,YAGlD;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,oBAAiB;AAAA,kBACjB,eAAY;AAAA,kBACZ,OAAO;AAAA,oBACL,UAAU;AAAA,oBACV,OAAO;AAAA,oBACP,iBAAiB,OAAO,WAAW;AAAA,oBACnC,YAAY,gBAAgB,SAAS;AAAA,kBACvC;AAAA,kBAEC,iBAAO,IAAI,CAAC,KAAK,MAChB;AAAA,oBAAC,aAAAC;AAAA,oBAAA;AAAA,sBAEC,KAAK,IAAI;AAAA,sBACT,KAAI;AAAA,sBACJ,OAAO;AAAA,sBACP,QAAQ;AAAA,sBACR,UAAU,MAAM;AAAA,sBAChB,OAAM;AAAA,sBACN,eAAY;AAAA,sBACZ,OAAO;AAAA,wBACL,UAAU;AAAA,wBACV,OAAO;AAAA,wBACP,OAAO;AAAA,wBACP,QAAQ;AAAA,wBACR,WAAW;AAAA,wBACX,SAAS,MAAM,QAAQ,IAAI;AAAA,wBAC3B,YAAY,gBACR,SACA,WAAW,kBAAkB;AAAA,sBACnC;AAAA;AAAA,oBAlBK,IAAI;AAAA,kBAmBX,CACD;AAAA;AAAA,cACH;AAAA,cAEC;AAAA;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,SAAS;AAAA,EAChB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,cAAc;AAAA,EACd,YAAY;AACd,GAAkB;AAChB,QAAM,EAAE,eAAe,cAAc,aAAa,IAAI,aAAa;AAEnE,QAAM,cAAU,sBAAO,CAAC;AACxB,QAAM,mBAAe,sBAAO,CAAC;AAC7B,QAAM,mBAAe,sBAAO,CAAC;AAC7B,QAAM,oBAAgB,sBAAO,CAAC;AAC9B,QAAM,oBAAgB,sBAAO,CAAC;AAE9B,+BAAU,MAAM;AACd,QAAI,cAAe;AAEnB,UAAM,OAAO,aAAa;AAC1B,QAAI,CAAC,KAAM;AAEX,UAAM,WAAW,KAAK,cAA8B,oBAAoB;AACxE,QAAI,CAAC,SAAU;AAEf,QAAI;AACJ,QAAI,UAAU;AAEd,aAAS,OAAO;AACd,UAAI,CAAC,QAAS;AAEd,oBAAc,YACX,aAAa,UAAU,cAAc,WAAW;AACnD,oBAAc,YACX,aAAa,UAAU,cAAc,WAAW;AAEnD,YAAM,IAAI,QAAQ,UAAU;AAC5B,YAAM,KAAK,cAAc,UAAU;AACnC,YAAM,KAAK,cAAc,UAAU;AACnC,eAAU,MAAM,YAAY,eAAe,EAAE,OAAO,IAAI,EAAE;AAE1D,cAAQ,sBAAsB,IAAI;AAAA,IACpC;AAEA,aAAS,WAAW;AAClB,UAAI,CAAC,aAAc;AACnB,cAAQ,UAAU,WAAW;AAAA,IAC/B;AAEA,aAAS,YAAY,GAAe;AAClC,UAAI,CAAC,aAAc;AACnB,mBAAa,WAAW,EAAE,UAAU,WAAW,aAAa,OAAO;AACnE,mBAAa,WAAW,EAAE,UAAU,WAAW,cAAc,OAAO;AAAA,IACtE;AAEA,eAAW,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AACjE,eAAW,iBAAiB,aAAa,aAAa,EAAE,SAAS,KAAK,CAAC;AACvE,YAAQ,sBAAsB,IAAI;AAElC,WAAO,MAAM;AACX,gBAAU;AACV,iBAAW,oBAAoB,UAAU,QAAQ;AACjD,iBAAW,oBAAoB,aAAa,WAAW;AACvD,2BAAqB,KAAK;AAAA,IAC5B;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAIA,SAAS,SAAS;AAAA,EAChB,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ;AAAA,EACR;AAAA,EACA,qBAAqB;AACvB,GAAkB;AAChB,QAAM,EAAE,QAAQ,OAAO,cAAc,IAAI,aAAa;AACtD,QAAM,eAAe,gBAAgB,QAAQ,GAAG,kBAAkB;AAElE,SACE,4EAEG;AAAA,WAAO,IAAI,CAAC,KAAK,MAChB;AAAA,MAAC;AAAA;AAAA,QAEC,sBAAmB;AAAA,QACnB,eAAY;AAAA,QACZ,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC3B,YAAY,WAAW,YAAY;AAAA,UACnC,YAAY,sBAAsB,IAAI,OAAO;AAAA,YAC3C;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AAAA;AAAA,MAhBK,kBAAkB,IAAI,KAAK;AAAA,IAiBlC,CACD;AAAA,IAED;AAAA,MAAC;AAAA;AAAA,QACC,qBAAkB;AAAA,QAClB,eAAY;AAAA,QACZ,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,YAAY,sBAAsB,WAAW;AAAA,YAC3C;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAIA,SAAS,KAAK;AAAA,EACZ,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc;AAAA,EACd,cAAc;AAChB,GAAc;AACZ,eAAa;AAEb,QAAM,OAAO,cAAc,EAAE,SAAS,SAAS,aAAa,YAAY,CAAC;AAEzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,gBAAgB,QAAQ,MAAM;AAAA,QAC9B,sBAAsB,QAAQ,MAAM;AAAA,QACpC,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,eAAe;AAAA,MACjB;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,QAAQ;AAAA,EACf,UAAU;AAAA,EACV,UAAU;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AACd,GAAiB;AACf,eAAa;AAEb,QAAM,UAAU,CAAC,UACf,2BAA2B,KAAK,IAAI,OAAO,mBAAmB,OAAO;AACvE,QAAM,SAAS,GAAG,OAAO,MAAM,OAAO;AAEtC,SACE,4EACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,sBAAmB;AAAA,QACnB,eAAY;AAAA,QACZ,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,iBAAiB,QAAQ,UAAU;AAAA,UACnC,gBAAgB;AAAA,QAClB;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,qBAAkB;AAAA,QAClB,eAAY;AAAA,QACZ,OAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,iBAAiB,QAAQ,SAAS;AAAA,UAClC,gBAAgB;AAAA,QAClB;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAIA,SAAS,YAAY,EAAE,UAAU,IAAI,GAAqB;AACxD,eAAa;AAEb,SACE;AAAA,IAAC;AAAA;AAAA,MACC,qBAAkB;AAAA,MAClB,eAAY;AAAA,MACZ,OAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,iBAAiB,gBAAgB,OAAO;AAAA,MAC1C;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,QAAQ,EAAE,WAAW,SAAS,GAAiB;AACtD,eAAa;AAEb,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,OAAO,EAAE,UAAU,YAAY,QAAQ,GAAG;AAAA,MAEzC;AAAA;AAAA,EACH;AAEJ;;;ADvWO,IAAM,yBAAyB;","names":["import_react","import_react","import_react","Image"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/hero-scene.tsx","../src/hero-scene-context.ts","../src/utils.ts","../src/use-in-viewport.ts","../src/use-reduced-motion.ts"],"sourcesContent":["export {\n HeroScene,\n HeroParallax,\n HeroVignette,\n HeroBlur,\n HeroPattern,\n HeroDarkOverlay,\n HeroContent,\n} from './hero-scene'\n\n/** Event name dispatched on `globalThis` when the active image changes. `event.detail` is the new index. */\nexport const HERO_SCENE_INDEX_EVENT = 'hero-scene-index-change'\nexport { buildVignetteGradient, buildBlurMask } from './utils'\nexport { useReducedMotion } from './use-reduced-motion'\n\nexport type {\n HeroSceneProps,\n HeroImage,\n ParallaxProps,\n VignetteProps,\n BlurProps,\n PatternProps,\n DarkOverlayProps,\n ContentProps,\n} from './types'\n","'use client'\n\nimport Image from 'next/image'\nimport { useEffect, useRef, useState } from 'react'\n\nimport { HeroSceneContext, useHeroScene } from './hero-scene-context'\nimport type {\n BlurProps,\n ContentProps,\n DarkOverlayProps,\n HeroSceneProps,\n ParallaxProps,\n PatternProps,\n VignetteProps,\n} from './types'\nimport { buildBlurMask, buildVignetteGradient } from './utils'\nimport { useInViewport } from './use-in-viewport'\nimport { useReducedMotion } from './use-reduced-motion'\n\n/**\n * Injected once — handles dark mode via `.dark` ancestor class.\n * Uses data-attributes so no Tailwind scanning is needed.\n */\nconst DARK_STYLES = `\n[data-hs-dark-only]{display:none}\n[data-hs-light-only]{display:block}\n[data-hs-img]{filter:none}\n.dark [data-hs-dark-only]{display:block}\n.dark [data-hs-light-only]{display:none}\n.dark [data-hs-img]{filter:grayscale(1)}\n`\n\n// ─── Root Component ──────────────────────────────────────────\n\nfunction HeroSceneRoot({\n images,\n initialIndex = 0,\n interval = 30_000,\n transitionDuration = 700,\n className,\n onIndexChange,\n children,\n}: HeroSceneProps) {\n const [index, setIndex] = useState(initialIndex)\n const rootRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const isInViewport = useInViewport(rootRef)\n\n // ── Image rotation ──\n useEffect(() => {\n if (interval <= 0 || images.length <= 1) return\n\n let current = initialIndex\n const id = setInterval(() => {\n const next = (current + 1) % images.length\n current = next\n setIndex(next)\n setTimeout(() => {\n onIndexChange?.(next)\n globalThis.dispatchEvent(\n new CustomEvent('hero-scene-index-change', { detail: next }),\n )\n }, 0)\n }, interval)\n return () => clearInterval(id)\n }, [initialIndex, interval, images.length, onIndexChange])\n\n const activeColor = images[index]?.color ?? '128, 128, 128'\n\n return (\n <HeroSceneContext.Provider\n value={{\n images,\n index,\n transitionDuration,\n reducedMotion,\n isInViewport,\n containerRef: rootRef,\n }}\n >\n <div\n ref={rootRef}\n className={className}\n style={{ position: 'relative', overflow: 'hidden' }}\n >\n <style dangerouslySetInnerHTML={{ __html: DARK_STYLES }} />\n {/* ── Background images ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: '-15%',\n backgroundColor: `rgb(${activeColor})`,\n transition: reducedMotion ? 'none' : 'background-color 1s ease',\n }}\n >\n {images.map((img, i) => (\n <Image\n key={img.src}\n src={img.src}\n alt=\"\"\n width={1920}\n height={1080}\n priority={i === initialIndex}\n sizes=\"100vw\"\n data-hs-img=\"\"\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n opacity: i === index ? 1 : 0,\n transition: reducedMotion\n ? 'none'\n : `opacity ${transitionDuration}ms ease`,\n }}\n />\n ))}\n </div>\n\n {children}\n </div>\n </HeroSceneContext.Provider>\n )\n}\n\n// ─── Parallax ────────────────────────────────────────────────\n\nfunction Parallax({\n speed = 0.4,\n mouseShiftX = 25,\n mouseShiftY = 15,\n mouseLerp = 0.04,\n}: ParallaxProps) {\n const { reducedMotion, isInViewport, containerRef } = useHeroScene()\n\n const scrollY = useRef(0)\n const targetMouseX = useRef(0)\n const targetMouseY = useRef(0)\n const currentMouseX = useRef(0)\n const currentMouseY = useRef(0)\n\n useEffect(() => {\n if (reducedMotion) return\n\n const root = containerRef.current\n if (!root) return\n\n const imagesEl = root.querySelector<HTMLDivElement>('[data-hero-images]')\n if (!imagesEl) return\n\n let rafId: number\n let running = true\n\n function loop() {\n if (!running) return\n\n currentMouseX.current +=\n (targetMouseX.current - currentMouseX.current) * mouseLerp\n currentMouseY.current +=\n (targetMouseY.current - currentMouseY.current) * mouseLerp\n\n const y = scrollY.current * speed\n const mx = currentMouseX.current * mouseShiftX\n const my = currentMouseY.current * mouseShiftY\n imagesEl!.style.transform = `translate3d(${mx}px, ${y + my}px, 0)`\n\n rafId = requestAnimationFrame(loop)\n }\n\n function onScroll() {\n if (!isInViewport) return\n scrollY.current = globalThis.scrollY\n }\n\n function onMouseMove(e: MouseEvent) {\n if (!isInViewport) return\n targetMouseX.current = (e.clientX / globalThis.innerWidth - 0.5) * 2\n targetMouseY.current = (e.clientY / globalThis.innerHeight - 0.5) * 2\n }\n\n globalThis.addEventListener('scroll', onScroll, { passive: true })\n globalThis.addEventListener('mousemove', onMouseMove, { passive: true })\n rafId = requestAnimationFrame(loop)\n\n return () => {\n running = false\n globalThis.removeEventListener('scroll', onScroll)\n globalThis.removeEventListener('mousemove', onMouseMove)\n cancelAnimationFrame(rafId)\n }\n }, [\n reducedMotion,\n isInViewport,\n containerRef,\n speed,\n mouseShiftX,\n mouseShiftY,\n mouseLerp,\n ])\n\n return null\n}\n\n// ─── Vignette ────────────────────────────────────────────────\n\nfunction Vignette({\n centerX = 50,\n centerY = 100,\n shape = 'circle',\n stops,\n transitionDuration = 1000,\n}: VignetteProps) {\n const { images, index, reducedMotion } = useHeroScene()\n const transitionMs = reducedMotion ? '0ms' : `${transitionDuration}ms`\n\n return (\n <>\n {/* Light mode — color-matched vignettes with crossfade */}\n {images.map((img, i) => (\n <div\n key={`vignette-light-${img.color}`}\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n opacity: i === index ? 1 : 0,\n transition: `opacity ${transitionMs} ease`,\n background: buildVignetteGradient(img.color, {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n ))}\n {/* Dark mode — black vignette */}\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n background: buildVignetteGradient('0, 0, 0', {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n </>\n )\n}\n\n// ─── Blur ────────────────────────────────────────────────────\n\nfunction Blur({\n amount = 24,\n centerX = 50,\n centerY = 65,\n innerRadius = 15,\n outerRadius = 55,\n}: BlurProps) {\n useHeroScene()\n\n const mask = buildBlurMask({ centerX, centerY, innerRadius, outerRadius })\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 15,\n backdropFilter: `blur(${amount}px)`,\n WebkitBackdropFilter: `blur(${amount}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n pointerEvents: 'none',\n }}\n />\n )\n}\n\n// ─── Pattern ─────────────────────────────────────────────────\n\nfunction Pattern({\n dotSize = 1,\n spacing = 20,\n lightColor = 'rgba(0 0 0 / 0.15)',\n darkColor = 'rgba(255 255 255 / 0.1)',\n}: PatternProps) {\n useHeroScene()\n\n const bgImage = (color: string) =>\n `radial-gradient(circle, ${color} ${dotSize}px, transparent ${dotSize}px)`\n const bgSize = `${spacing}px ${spacing}px`\n\n return (\n <>\n <div\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(lightColor),\n backgroundSize: bgSize,\n }}\n />\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(darkColor),\n backgroundSize: bgSize,\n }}\n />\n </>\n )\n}\n\n// ─── DarkOverlay ─────────────────────────────────────────────\n\nfunction DarkOverlay({ opacity = 0.4 }: DarkOverlayProps) {\n useHeroScene()\n\n return (\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n pointerEvents: 'none',\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene()\n\n return (\n <div\n className={className}\n style={{ position: 'relative', zIndex: 30 }}\n >\n {children}\n </div>\n )\n}\n\n// ─── Exports ────────────────────────────────────────────────\n\nexport {\n HeroSceneRoot as HeroScene,\n Parallax as HeroParallax,\n Vignette as HeroVignette,\n Blur as HeroBlur,\n Pattern as HeroPattern,\n DarkOverlay as HeroDarkOverlay,\n Content as HeroContent,\n}\n","'use client'\n\nimport { createContext, useContext } from 'react'\nimport type { HeroSceneContextValue } from './types'\n\nexport const HeroSceneContext = createContext<HeroSceneContextValue | null>(null)\n\nexport function useHeroScene(): HeroSceneContextValue {\n const ctx = useContext(HeroSceneContext)\n if (!ctx) {\n throw new Error(\n 'HeroScene compound components (Parallax, Vignette, Blur, Pattern, DarkOverlay, Content) ' +\n 'must be rendered inside a <HeroScene> parent.',\n )\n }\n return ctx\n}\n","import type { VignetteProps, BlurProps } from './types'\n\nconst DEFAULT_STOPS: [number, number][] = [\n [0, 0],\n [15, 0.08],\n [30, 0.15],\n [45, 0.25],\n [60, 0.35],\n [75, 0.45],\n [88, 0.55],\n [100, 0.6],\n]\n\nexport function buildVignetteGradient(\n color: string,\n config: Pick<VignetteProps, 'shape' | 'centerX' | 'centerY' | 'stops'>,\n): string {\n const shape = config.shape ?? 'circle'\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 100\n const stops = config.stops ?? DEFAULT_STOPS\n\n const gradientStops = stops\n .map(([pos, opacity]) =>\n opacity === 0\n ? `transparent ${pos}%`\n : `color-mix(in srgb, rgb(${color}) ${Math.round(opacity * 100)}%, transparent) ${pos}%`,\n )\n .join(', ')\n\n return `radial-gradient(${shape} at ${cx}% ${cy}%, ${gradientStops})`\n}\n\nexport function buildBlurMask(\n config: Pick<BlurProps, 'centerX' | 'centerY' | 'innerRadius' | 'outerRadius'>,\n): string {\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 65\n const inner = config.innerRadius ?? 15\n const outer = config.outerRadius ?? 55\n\n return `radial-gradient(circle at ${cx}% ${cy}%, transparent ${inner}%, black ${outer}%)`\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\nimport type { RefObject } from 'react'\n\n/**\n * Returns true when the referenced element is at least partially visible\n * in the viewport, using IntersectionObserver. SSR-safe — defaults to true\n * so effects run immediately on first paint before the observer fires.\n */\nexport function useInViewport(ref: RefObject<HTMLElement | null>): boolean {\n const [inViewport, setInViewport] = useState(true)\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n setInViewport(entry.isIntersecting)\n },\n { threshold: 0 },\n )\n\n observer.observe(el)\n return () => observer.disconnect()\n }, [ref])\n\n return inViewport\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\n\n/**\n * Returns true when the user has enabled \"prefers-reduced-motion: reduce\"\n * in their OS or browser settings. SSR-safe — defaults to false.\n */\nexport function useReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false)\n\n useEffect(() => {\n const mql = globalThis.matchMedia('(prefers-reduced-motion: reduce)')\n setReduced(mql.matches)\n\n function onChange(e: MediaQueryListEvent) {\n setReduced(e.matches)\n }\n\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [])\n\n return reduced\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","Image"]}
|
package/dist/index.js
CHANGED
|
@@ -121,7 +121,7 @@ function HeroSceneRoot({
|
|
|
121
121
|
return () => clearInterval(id);
|
|
122
122
|
}, [initialIndex, interval, images.length, onIndexChange]);
|
|
123
123
|
const activeColor = images[index]?.color ?? "128, 128, 128";
|
|
124
|
-
return /* @__PURE__ */
|
|
124
|
+
return /* @__PURE__ */ jsx(
|
|
125
125
|
HeroSceneContext.Provider,
|
|
126
126
|
{
|
|
127
127
|
value: {
|
|
@@ -132,55 +132,53 @@ function HeroSceneRoot({
|
|
|
132
132
|
isInViewport,
|
|
133
133
|
containerRef: rootRef
|
|
134
134
|
},
|
|
135
|
-
children:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
135
|
+
children: /* @__PURE__ */ jsxs(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
ref: rootRef,
|
|
139
|
+
className,
|
|
140
|
+
style: { position: "relative", overflow: "hidden" },
|
|
141
|
+
children: [
|
|
142
|
+
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: DARK_STYLES } }),
|
|
143
|
+
/* @__PURE__ */ jsx(
|
|
144
|
+
"div",
|
|
145
|
+
{
|
|
146
|
+
"data-hero-images": "",
|
|
147
|
+
"aria-hidden": "true",
|
|
148
|
+
style: {
|
|
149
|
+
position: "absolute",
|
|
150
|
+
inset: "-15%",
|
|
151
|
+
backgroundColor: `rgb(${activeColor})`,
|
|
152
|
+
transition: reducedMotion ? "none" : "background-color 1s ease"
|
|
153
|
+
},
|
|
154
|
+
children: images.map((img, i) => /* @__PURE__ */ jsx(
|
|
155
|
+
Image,
|
|
156
|
+
{
|
|
157
|
+
src: img.src,
|
|
158
|
+
alt: "",
|
|
159
|
+
width: 1920,
|
|
160
|
+
height: 1080,
|
|
161
|
+
priority: i === initialIndex,
|
|
162
|
+
sizes: "100vw",
|
|
163
|
+
"data-hs-img": "",
|
|
164
|
+
style: {
|
|
165
|
+
position: "absolute",
|
|
166
|
+
inset: 0,
|
|
167
|
+
width: "100%",
|
|
168
|
+
height: "100%",
|
|
169
|
+
objectFit: "cover",
|
|
170
|
+
opacity: i === index ? 1 : 0,
|
|
171
|
+
transition: reducedMotion ? "none" : `opacity ${transitionDuration}ms ease`
|
|
172
|
+
}
|
|
154
173
|
},
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
sizes: "100vw",
|
|
164
|
-
"data-hs-img": "",
|
|
165
|
-
style: {
|
|
166
|
-
position: "absolute",
|
|
167
|
-
inset: 0,
|
|
168
|
-
width: "100%",
|
|
169
|
-
height: "100%",
|
|
170
|
-
objectFit: "cover",
|
|
171
|
-
opacity: i === index ? 1 : 0,
|
|
172
|
-
transition: reducedMotion ? "none" : `opacity ${transitionDuration}ms ease`
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
img.src
|
|
176
|
-
))
|
|
177
|
-
}
|
|
178
|
-
),
|
|
179
|
-
children
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
)
|
|
183
|
-
]
|
|
174
|
+
img.src
|
|
175
|
+
))
|
|
176
|
+
}
|
|
177
|
+
),
|
|
178
|
+
children
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
)
|
|
184
182
|
}
|
|
185
183
|
);
|
|
186
184
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hero-scene.tsx","../src/hero-scene-context.ts","../src/utils.ts","../src/use-in-viewport.ts","../src/use-reduced-motion.ts","../src/index.ts"],"sourcesContent":["'use client'\n\nimport Image from 'next/image'\nimport { useEffect, useRef, useState } from 'react'\n\nimport { HeroSceneContext, useHeroScene } from './hero-scene-context'\nimport type {\n BlurProps,\n ContentProps,\n DarkOverlayProps,\n HeroSceneProps,\n ParallaxProps,\n PatternProps,\n VignetteProps,\n} from './types'\nimport { buildBlurMask, buildVignetteGradient } from './utils'\nimport { useInViewport } from './use-in-viewport'\nimport { useReducedMotion } from './use-reduced-motion'\n\n/**\n * Injected once — handles dark mode via `.dark` ancestor class.\n * Uses data-attributes so no Tailwind scanning is needed.\n */\nconst DARK_STYLES = `\n[data-hs-dark-only]{display:none}\n[data-hs-light-only]{display:block}\n[data-hs-img]{filter:none}\n.dark [data-hs-dark-only]{display:block}\n.dark [data-hs-light-only]{display:none}\n.dark [data-hs-img]{filter:grayscale(1)}\n`\n\n// ─── Root Component ──────────────────────────────────────────\n\nfunction HeroSceneRoot({\n images,\n initialIndex = 0,\n interval = 30_000,\n transitionDuration = 700,\n className,\n onIndexChange,\n children,\n}: HeroSceneProps) {\n const [index, setIndex] = useState(initialIndex)\n const rootRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const isInViewport = useInViewport(rootRef)\n\n // ── Image rotation ──\n useEffect(() => {\n if (interval <= 0 || images.length <= 1) return\n\n let current = initialIndex\n const id = setInterval(() => {\n const next = (current + 1) % images.length\n current = next\n setIndex(next)\n setTimeout(() => {\n onIndexChange?.(next)\n globalThis.dispatchEvent(\n new CustomEvent('hero-scene-index-change', { detail: next }),\n )\n }, 0)\n }, interval)\n return () => clearInterval(id)\n }, [initialIndex, interval, images.length, onIndexChange])\n\n const activeColor = images[index]?.color ?? '128, 128, 128'\n\n return (\n <HeroSceneContext.Provider\n value={{\n images,\n index,\n transitionDuration,\n reducedMotion,\n isInViewport,\n containerRef: rootRef,\n }}\n >\n <style dangerouslySetInnerHTML={{ __html: DARK_STYLES }} />\n <div\n ref={rootRef}\n className={className}\n style={{ position: 'relative', overflow: 'hidden' }}\n >\n {/* ── Background images ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: '-15%',\n backgroundColor: `rgb(${activeColor})`,\n transition: reducedMotion ? 'none' : 'background-color 1s ease',\n }}\n >\n {images.map((img, i) => (\n <Image\n key={img.src}\n src={img.src}\n alt=\"\"\n width={1920}\n height={1080}\n priority={i === initialIndex}\n sizes=\"100vw\"\n data-hs-img=\"\"\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n opacity: i === index ? 1 : 0,\n transition: reducedMotion\n ? 'none'\n : `opacity ${transitionDuration}ms ease`,\n }}\n />\n ))}\n </div>\n\n {children}\n </div>\n </HeroSceneContext.Provider>\n )\n}\n\n// ─── Parallax ────────────────────────────────────────────────\n\nfunction Parallax({\n speed = 0.4,\n mouseShiftX = 25,\n mouseShiftY = 15,\n mouseLerp = 0.04,\n}: ParallaxProps) {\n const { reducedMotion, isInViewport, containerRef } = useHeroScene()\n\n const scrollY = useRef(0)\n const targetMouseX = useRef(0)\n const targetMouseY = useRef(0)\n const currentMouseX = useRef(0)\n const currentMouseY = useRef(0)\n\n useEffect(() => {\n if (reducedMotion) return\n\n const root = containerRef.current\n if (!root) return\n\n const imagesEl = root.querySelector<HTMLDivElement>('[data-hero-images]')\n if (!imagesEl) return\n\n let rafId: number\n let running = true\n\n function loop() {\n if (!running) return\n\n currentMouseX.current +=\n (targetMouseX.current - currentMouseX.current) * mouseLerp\n currentMouseY.current +=\n (targetMouseY.current - currentMouseY.current) * mouseLerp\n\n const y = scrollY.current * speed\n const mx = currentMouseX.current * mouseShiftX\n const my = currentMouseY.current * mouseShiftY\n imagesEl!.style.transform = `translate3d(${mx}px, ${y + my}px, 0)`\n\n rafId = requestAnimationFrame(loop)\n }\n\n function onScroll() {\n if (!isInViewport) return\n scrollY.current = globalThis.scrollY\n }\n\n function onMouseMove(e: MouseEvent) {\n if (!isInViewport) return\n targetMouseX.current = (e.clientX / globalThis.innerWidth - 0.5) * 2\n targetMouseY.current = (e.clientY / globalThis.innerHeight - 0.5) * 2\n }\n\n globalThis.addEventListener('scroll', onScroll, { passive: true })\n globalThis.addEventListener('mousemove', onMouseMove, { passive: true })\n rafId = requestAnimationFrame(loop)\n\n return () => {\n running = false\n globalThis.removeEventListener('scroll', onScroll)\n globalThis.removeEventListener('mousemove', onMouseMove)\n cancelAnimationFrame(rafId)\n }\n }, [\n reducedMotion,\n isInViewport,\n containerRef,\n speed,\n mouseShiftX,\n mouseShiftY,\n mouseLerp,\n ])\n\n return null\n}\n\n// ─── Vignette ────────────────────────────────────────────────\n\nfunction Vignette({\n centerX = 50,\n centerY = 100,\n shape = 'circle',\n stops,\n transitionDuration = 1000,\n}: VignetteProps) {\n const { images, index, reducedMotion } = useHeroScene()\n const transitionMs = reducedMotion ? '0ms' : `${transitionDuration}ms`\n\n return (\n <>\n {/* Light mode — color-matched vignettes with crossfade */}\n {images.map((img, i) => (\n <div\n key={`vignette-light-${img.color}`}\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n opacity: i === index ? 1 : 0,\n transition: `opacity ${transitionMs} ease`,\n background: buildVignetteGradient(img.color, {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n ))}\n {/* Dark mode — black vignette */}\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n background: buildVignetteGradient('0, 0, 0', {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n </>\n )\n}\n\n// ─── Blur ────────────────────────────────────────────────────\n\nfunction Blur({\n amount = 24,\n centerX = 50,\n centerY = 65,\n innerRadius = 15,\n outerRadius = 55,\n}: BlurProps) {\n useHeroScene()\n\n const mask = buildBlurMask({ centerX, centerY, innerRadius, outerRadius })\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 15,\n backdropFilter: `blur(${amount}px)`,\n WebkitBackdropFilter: `blur(${amount}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n pointerEvents: 'none',\n }}\n />\n )\n}\n\n// ─── Pattern ─────────────────────────────────────────────────\n\nfunction Pattern({\n dotSize = 1,\n spacing = 20,\n lightColor = 'rgba(0 0 0 / 0.15)',\n darkColor = 'rgba(255 255 255 / 0.1)',\n}: PatternProps) {\n useHeroScene()\n\n const bgImage = (color: string) =>\n `radial-gradient(circle, ${color} ${dotSize}px, transparent ${dotSize}px)`\n const bgSize = `${spacing}px ${spacing}px`\n\n return (\n <>\n <div\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(lightColor),\n backgroundSize: bgSize,\n }}\n />\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(darkColor),\n backgroundSize: bgSize,\n }}\n />\n </>\n )\n}\n\n// ─── DarkOverlay ─────────────────────────────────────────────\n\nfunction DarkOverlay({ opacity = 0.4 }: DarkOverlayProps) {\n useHeroScene()\n\n return (\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n pointerEvents: 'none',\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene()\n\n return (\n <div\n className={className}\n style={{ position: 'relative', zIndex: 30 }}\n >\n {children}\n </div>\n )\n}\n\n// ─── Exports ────────────────────────────────────────────────\n\nexport {\n HeroSceneRoot as HeroScene,\n Parallax as HeroParallax,\n Vignette as HeroVignette,\n Blur as HeroBlur,\n Pattern as HeroPattern,\n DarkOverlay as HeroDarkOverlay,\n Content as HeroContent,\n}\n","'use client'\n\nimport { createContext, useContext } from 'react'\nimport type { HeroSceneContextValue } from './types'\n\nexport const HeroSceneContext = createContext<HeroSceneContextValue | null>(null)\n\nexport function useHeroScene(): HeroSceneContextValue {\n const ctx = useContext(HeroSceneContext)\n if (!ctx) {\n throw new Error(\n 'HeroScene compound components (Parallax, Vignette, Blur, Pattern, DarkOverlay, Content) ' +\n 'must be rendered inside a <HeroScene> parent.',\n )\n }\n return ctx\n}\n","import type { VignetteProps, BlurProps } from './types'\n\nconst DEFAULT_STOPS: [number, number][] = [\n [0, 0],\n [15, 0.08],\n [30, 0.15],\n [45, 0.25],\n [60, 0.35],\n [75, 0.45],\n [88, 0.55],\n [100, 0.6],\n]\n\nexport function buildVignetteGradient(\n color: string,\n config: Pick<VignetteProps, 'shape' | 'centerX' | 'centerY' | 'stops'>,\n): string {\n const shape = config.shape ?? 'circle'\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 100\n const stops = config.stops ?? DEFAULT_STOPS\n\n const gradientStops = stops\n .map(([pos, opacity]) =>\n opacity === 0\n ? `transparent ${pos}%`\n : `color-mix(in srgb, rgb(${color}) ${Math.round(opacity * 100)}%, transparent) ${pos}%`,\n )\n .join(', ')\n\n return `radial-gradient(${shape} at ${cx}% ${cy}%, ${gradientStops})`\n}\n\nexport function buildBlurMask(\n config: Pick<BlurProps, 'centerX' | 'centerY' | 'innerRadius' | 'outerRadius'>,\n): string {\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 65\n const inner = config.innerRadius ?? 15\n const outer = config.outerRadius ?? 55\n\n return `radial-gradient(circle at ${cx}% ${cy}%, transparent ${inner}%, black ${outer}%)`\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\nimport type { RefObject } from 'react'\n\n/**\n * Returns true when the referenced element is at least partially visible\n * in the viewport, using IntersectionObserver. SSR-safe — defaults to true\n * so effects run immediately on first paint before the observer fires.\n */\nexport function useInViewport(ref: RefObject<HTMLElement | null>): boolean {\n const [inViewport, setInViewport] = useState(true)\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n setInViewport(entry.isIntersecting)\n },\n { threshold: 0 },\n )\n\n observer.observe(el)\n return () => observer.disconnect()\n }, [ref])\n\n return inViewport\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\n\n/**\n * Returns true when the user has enabled \"prefers-reduced-motion: reduce\"\n * in their OS or browser settings. SSR-safe — defaults to false.\n */\nexport function useReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false)\n\n useEffect(() => {\n const mql = globalThis.matchMedia('(prefers-reduced-motion: reduce)')\n setReduced(mql.matches)\n\n function onChange(e: MediaQueryListEvent) {\n setReduced(e.matches)\n }\n\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [])\n\n return reduced\n}\n","export {\n HeroScene,\n HeroParallax,\n HeroVignette,\n HeroBlur,\n HeroPattern,\n HeroDarkOverlay,\n HeroContent,\n} from './hero-scene'\n\n/** Event name dispatched on `globalThis` when the active image changes. `event.detail` is the new index. */\nexport const HERO_SCENE_INDEX_EVENT = 'hero-scene-index-change'\nexport { buildVignetteGradient, buildBlurMask } from './utils'\nexport { useReducedMotion } from './use-reduced-motion'\n\nexport type {\n HeroSceneProps,\n HeroImage,\n ParallaxProps,\n VignetteProps,\n BlurProps,\n PatternProps,\n DarkOverlayProps,\n ContentProps,\n} from './types'\n"],"mappings":"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","names":["useEffect","useState","useEffect","useState","useState","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/hero-scene.tsx","../src/hero-scene-context.ts","../src/utils.ts","../src/use-in-viewport.ts","../src/use-reduced-motion.ts","../src/index.ts"],"sourcesContent":["'use client'\n\nimport Image from 'next/image'\nimport { useEffect, useRef, useState } from 'react'\n\nimport { HeroSceneContext, useHeroScene } from './hero-scene-context'\nimport type {\n BlurProps,\n ContentProps,\n DarkOverlayProps,\n HeroSceneProps,\n ParallaxProps,\n PatternProps,\n VignetteProps,\n} from './types'\nimport { buildBlurMask, buildVignetteGradient } from './utils'\nimport { useInViewport } from './use-in-viewport'\nimport { useReducedMotion } from './use-reduced-motion'\n\n/**\n * Injected once — handles dark mode via `.dark` ancestor class.\n * Uses data-attributes so no Tailwind scanning is needed.\n */\nconst DARK_STYLES = `\n[data-hs-dark-only]{display:none}\n[data-hs-light-only]{display:block}\n[data-hs-img]{filter:none}\n.dark [data-hs-dark-only]{display:block}\n.dark [data-hs-light-only]{display:none}\n.dark [data-hs-img]{filter:grayscale(1)}\n`\n\n// ─── Root Component ──────────────────────────────────────────\n\nfunction HeroSceneRoot({\n images,\n initialIndex = 0,\n interval = 30_000,\n transitionDuration = 700,\n className,\n onIndexChange,\n children,\n}: HeroSceneProps) {\n const [index, setIndex] = useState(initialIndex)\n const rootRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const isInViewport = useInViewport(rootRef)\n\n // ── Image rotation ──\n useEffect(() => {\n if (interval <= 0 || images.length <= 1) return\n\n let current = initialIndex\n const id = setInterval(() => {\n const next = (current + 1) % images.length\n current = next\n setIndex(next)\n setTimeout(() => {\n onIndexChange?.(next)\n globalThis.dispatchEvent(\n new CustomEvent('hero-scene-index-change', { detail: next }),\n )\n }, 0)\n }, interval)\n return () => clearInterval(id)\n }, [initialIndex, interval, images.length, onIndexChange])\n\n const activeColor = images[index]?.color ?? '128, 128, 128'\n\n return (\n <HeroSceneContext.Provider\n value={{\n images,\n index,\n transitionDuration,\n reducedMotion,\n isInViewport,\n containerRef: rootRef,\n }}\n >\n <div\n ref={rootRef}\n className={className}\n style={{ position: 'relative', overflow: 'hidden' }}\n >\n <style dangerouslySetInnerHTML={{ __html: DARK_STYLES }} />\n {/* ── Background images ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: '-15%',\n backgroundColor: `rgb(${activeColor})`,\n transition: reducedMotion ? 'none' : 'background-color 1s ease',\n }}\n >\n {images.map((img, i) => (\n <Image\n key={img.src}\n src={img.src}\n alt=\"\"\n width={1920}\n height={1080}\n priority={i === initialIndex}\n sizes=\"100vw\"\n data-hs-img=\"\"\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n opacity: i === index ? 1 : 0,\n transition: reducedMotion\n ? 'none'\n : `opacity ${transitionDuration}ms ease`,\n }}\n />\n ))}\n </div>\n\n {children}\n </div>\n </HeroSceneContext.Provider>\n )\n}\n\n// ─── Parallax ────────────────────────────────────────────────\n\nfunction Parallax({\n speed = 0.4,\n mouseShiftX = 25,\n mouseShiftY = 15,\n mouseLerp = 0.04,\n}: ParallaxProps) {\n const { reducedMotion, isInViewport, containerRef } = useHeroScene()\n\n const scrollY = useRef(0)\n const targetMouseX = useRef(0)\n const targetMouseY = useRef(0)\n const currentMouseX = useRef(0)\n const currentMouseY = useRef(0)\n\n useEffect(() => {\n if (reducedMotion) return\n\n const root = containerRef.current\n if (!root) return\n\n const imagesEl = root.querySelector<HTMLDivElement>('[data-hero-images]')\n if (!imagesEl) return\n\n let rafId: number\n let running = true\n\n function loop() {\n if (!running) return\n\n currentMouseX.current +=\n (targetMouseX.current - currentMouseX.current) * mouseLerp\n currentMouseY.current +=\n (targetMouseY.current - currentMouseY.current) * mouseLerp\n\n const y = scrollY.current * speed\n const mx = currentMouseX.current * mouseShiftX\n const my = currentMouseY.current * mouseShiftY\n imagesEl!.style.transform = `translate3d(${mx}px, ${y + my}px, 0)`\n\n rafId = requestAnimationFrame(loop)\n }\n\n function onScroll() {\n if (!isInViewport) return\n scrollY.current = globalThis.scrollY\n }\n\n function onMouseMove(e: MouseEvent) {\n if (!isInViewport) return\n targetMouseX.current = (e.clientX / globalThis.innerWidth - 0.5) * 2\n targetMouseY.current = (e.clientY / globalThis.innerHeight - 0.5) * 2\n }\n\n globalThis.addEventListener('scroll', onScroll, { passive: true })\n globalThis.addEventListener('mousemove', onMouseMove, { passive: true })\n rafId = requestAnimationFrame(loop)\n\n return () => {\n running = false\n globalThis.removeEventListener('scroll', onScroll)\n globalThis.removeEventListener('mousemove', onMouseMove)\n cancelAnimationFrame(rafId)\n }\n }, [\n reducedMotion,\n isInViewport,\n containerRef,\n speed,\n mouseShiftX,\n mouseShiftY,\n mouseLerp,\n ])\n\n return null\n}\n\n// ─── Vignette ────────────────────────────────────────────────\n\nfunction Vignette({\n centerX = 50,\n centerY = 100,\n shape = 'circle',\n stops,\n transitionDuration = 1000,\n}: VignetteProps) {\n const { images, index, reducedMotion } = useHeroScene()\n const transitionMs = reducedMotion ? '0ms' : `${transitionDuration}ms`\n\n return (\n <>\n {/* Light mode — color-matched vignettes with crossfade */}\n {images.map((img, i) => (\n <div\n key={`vignette-light-${img.color}`}\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n opacity: i === index ? 1 : 0,\n transition: `opacity ${transitionMs} ease`,\n background: buildVignetteGradient(img.color, {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n ))}\n {/* Dark mode — black vignette */}\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n background: buildVignetteGradient('0, 0, 0', {\n centerX,\n centerY,\n shape,\n stops,\n }),\n }}\n />\n </>\n )\n}\n\n// ─── Blur ────────────────────────────────────────────────────\n\nfunction Blur({\n amount = 24,\n centerX = 50,\n centerY = 65,\n innerRadius = 15,\n outerRadius = 55,\n}: BlurProps) {\n useHeroScene()\n\n const mask = buildBlurMask({ centerX, centerY, innerRadius, outerRadius })\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 15,\n backdropFilter: `blur(${amount}px)`,\n WebkitBackdropFilter: `blur(${amount}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n pointerEvents: 'none',\n }}\n />\n )\n}\n\n// ─── Pattern ─────────────────────────────────────────────────\n\nfunction Pattern({\n dotSize = 1,\n spacing = 20,\n lightColor = 'rgba(0 0 0 / 0.15)',\n darkColor = 'rgba(255 255 255 / 0.1)',\n}: PatternProps) {\n useHeroScene()\n\n const bgImage = (color: string) =>\n `radial-gradient(circle, ${color} ${dotSize}px, transparent ${dotSize}px)`\n const bgSize = `${spacing}px ${spacing}px`\n\n return (\n <>\n <div\n data-hs-light-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(lightColor),\n backgroundSize: bgSize,\n }}\n />\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 20,\n pointerEvents: 'none',\n backgroundImage: bgImage(darkColor),\n backgroundSize: bgSize,\n }}\n />\n </>\n )\n}\n\n// ─── DarkOverlay ─────────────────────────────────────────────\n\nfunction DarkOverlay({ opacity = 0.4 }: DarkOverlayProps) {\n useHeroScene()\n\n return (\n <div\n data-hs-dark-only=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n pointerEvents: 'none',\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene()\n\n return (\n <div\n className={className}\n style={{ position: 'relative', zIndex: 30 }}\n >\n {children}\n </div>\n )\n}\n\n// ─── Exports ────────────────────────────────────────────────\n\nexport {\n HeroSceneRoot as HeroScene,\n Parallax as HeroParallax,\n Vignette as HeroVignette,\n Blur as HeroBlur,\n Pattern as HeroPattern,\n DarkOverlay as HeroDarkOverlay,\n Content as HeroContent,\n}\n","'use client'\n\nimport { createContext, useContext } from 'react'\nimport type { HeroSceneContextValue } from './types'\n\nexport const HeroSceneContext = createContext<HeroSceneContextValue | null>(null)\n\nexport function useHeroScene(): HeroSceneContextValue {\n const ctx = useContext(HeroSceneContext)\n if (!ctx) {\n throw new Error(\n 'HeroScene compound components (Parallax, Vignette, Blur, Pattern, DarkOverlay, Content) ' +\n 'must be rendered inside a <HeroScene> parent.',\n )\n }\n return ctx\n}\n","import type { VignetteProps, BlurProps } from './types'\n\nconst DEFAULT_STOPS: [number, number][] = [\n [0, 0],\n [15, 0.08],\n [30, 0.15],\n [45, 0.25],\n [60, 0.35],\n [75, 0.45],\n [88, 0.55],\n [100, 0.6],\n]\n\nexport function buildVignetteGradient(\n color: string,\n config: Pick<VignetteProps, 'shape' | 'centerX' | 'centerY' | 'stops'>,\n): string {\n const shape = config.shape ?? 'circle'\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 100\n const stops = config.stops ?? DEFAULT_STOPS\n\n const gradientStops = stops\n .map(([pos, opacity]) =>\n opacity === 0\n ? `transparent ${pos}%`\n : `color-mix(in srgb, rgb(${color}) ${Math.round(opacity * 100)}%, transparent) ${pos}%`,\n )\n .join(', ')\n\n return `radial-gradient(${shape} at ${cx}% ${cy}%, ${gradientStops})`\n}\n\nexport function buildBlurMask(\n config: Pick<BlurProps, 'centerX' | 'centerY' | 'innerRadius' | 'outerRadius'>,\n): string {\n const cx = config.centerX ?? 50\n const cy = config.centerY ?? 65\n const inner = config.innerRadius ?? 15\n const outer = config.outerRadius ?? 55\n\n return `radial-gradient(circle at ${cx}% ${cy}%, transparent ${inner}%, black ${outer}%)`\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\nimport type { RefObject } from 'react'\n\n/**\n * Returns true when the referenced element is at least partially visible\n * in the viewport, using IntersectionObserver. SSR-safe — defaults to true\n * so effects run immediately on first paint before the observer fires.\n */\nexport function useInViewport(ref: RefObject<HTMLElement | null>): boolean {\n const [inViewport, setInViewport] = useState(true)\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n setInViewport(entry.isIntersecting)\n },\n { threshold: 0 },\n )\n\n observer.observe(el)\n return () => observer.disconnect()\n }, [ref])\n\n return inViewport\n}\n","'use client'\n\nimport { useEffect, useState } from 'react'\n\n/**\n * Returns true when the user has enabled \"prefers-reduced-motion: reduce\"\n * in their OS or browser settings. SSR-safe — defaults to false.\n */\nexport function useReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false)\n\n useEffect(() => {\n const mql = globalThis.matchMedia('(prefers-reduced-motion: reduce)')\n setReduced(mql.matches)\n\n function onChange(e: MediaQueryListEvent) {\n setReduced(e.matches)\n }\n\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [])\n\n return reduced\n}\n","export {\n HeroScene,\n HeroParallax,\n HeroVignette,\n HeroBlur,\n HeroPattern,\n HeroDarkOverlay,\n HeroContent,\n} from './hero-scene'\n\n/** Event name dispatched on `globalThis` when the active image changes. `event.detail` is the new index. */\nexport const HERO_SCENE_INDEX_EVENT = 'hero-scene-index-change'\nexport { buildVignetteGradient, buildBlurMask } from './utils'\nexport { useReducedMotion } from './use-reduced-motion'\n\nexport type {\n HeroSceneProps,\n HeroImage,\n ParallaxProps,\n VignetteProps,\n BlurProps,\n PatternProps,\n DarkOverlayProps,\n ContentProps,\n} from './types'\n"],"mappings":"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","names":["useEffect","useState","useEffect","useState","useState","useEffect"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okinoxis/hero-scene",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Cinematic hero sections for Next.js — compound component API with image rotation, parallax, vignette overlays, blur masks, and dot patterns.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|