@okinoxis/hero-scene 0.3.0 → 0.4.1

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 CHANGED
@@ -32,7 +32,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  HERO_SCENE_INDEX_EVENT: () => HERO_SCENE_INDEX_EVENT,
35
- HeroScene: () => HeroScene,
35
+ HeroBlur: () => Blur,
36
+ HeroContent: () => Content,
37
+ HeroDarkOverlay: () => DarkOverlay,
38
+ HeroParallax: () => Parallax,
39
+ HeroPattern: () => Pattern,
40
+ HeroScene: () => HeroSceneRoot,
41
+ HeroVignette: () => Vignette,
36
42
  buildBlurMask: () => buildBlurMask,
37
43
  buildVignetteGradient: () => buildVignetteGradient,
38
44
  useReducedMotion: () => useReducedMotion
@@ -190,6 +196,7 @@ function HeroSceneRoot({
190
196
  height: 1080,
191
197
  priority: i === initialIndex,
192
198
  sizes: "100vw",
199
+ className: "dark:grayscale",
193
200
  style: {
194
201
  position: "absolute",
195
202
  inset: 0,
@@ -410,21 +417,19 @@ function Content({ className, children }) {
410
417
  }
411
418
  );
412
419
  }
413
- var HeroScene = Object.assign(HeroSceneRoot, {
414
- Parallax,
415
- Vignette,
416
- Blur,
417
- Pattern,
418
- DarkOverlay,
419
- Content
420
- });
421
420
 
422
421
  // src/index.ts
423
422
  var HERO_SCENE_INDEX_EVENT = "hero-scene-index-change";
424
423
  // Annotate the CommonJS export names for ESM import in node:
425
424
  0 && (module.exports = {
426
425
  HERO_SCENE_INDEX_EVENT,
426
+ HeroBlur,
427
+ HeroContent,
428
+ HeroDarkOverlay,
429
+ HeroParallax,
430
+ HeroPattern,
427
431
  HeroScene,
432
+ HeroVignette,
428
433
  buildBlurMask,
429
434
  buildVignetteGradient,
430
435
  useReducedMotion
@@ -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 { HeroScene } 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// ─── 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 {/* ── Background images (no parallax — Parallax child wraps these) ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\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 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 // Expand the images container to allow parallax overflow\n imagesEl.style.inset = '-15%'\n imagesEl.style.willChange = 'transform'\n\n return () => {\n imagesEl.style.inset = '0'\n imagesEl.style.willChange = ''\n imagesEl.style.transform = ''\n }\n }, [reducedMotion, containerRef])\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 // Parallax is a behavior-only component — renders nothing\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 {images.map((img, i) => (\n <div\n key={`vignette-${img.color}`}\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 </>\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() // validate context\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() // validate context\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 aria-hidden=\"true\"\n className=\"dark:hidden\"\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 aria-hidden=\"true\"\n className=\"hidden dark:block\"\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() // validate context\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none hidden dark:block\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene() // validate context\n\n return (\n <div\n className={className}\n style={{ position: 'relative', zIndex: 30 }}\n >\n {children}\n </div>\n )\n}\n\n// ─── Compound export ─────────────────────────────────────────\n\nexport const HeroScene = Object.assign(HeroSceneRoot, {\n Parallax,\n Vignette,\n Blur,\n Pattern,\n DarkOverlay,\n Content,\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"]}
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// ─── 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 {/* ── Background images (no parallax — Parallax child wraps these) ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\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 className=\"dark:grayscale\"\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 // Expand the images container to allow parallax overflow\n imagesEl.style.inset = '-15%'\n imagesEl.style.willChange = 'transform'\n\n return () => {\n imagesEl.style.inset = '0'\n imagesEl.style.willChange = ''\n imagesEl.style.transform = ''\n }\n }, [reducedMotion, containerRef])\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 // Parallax is a behavior-only component — renders nothing\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 {images.map((img, i) => (\n <div\n key={`vignette-${img.color}`}\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 </>\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() // validate context\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() // validate context\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 aria-hidden=\"true\"\n className=\"dark:hidden\"\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 aria-hidden=\"true\"\n className=\"hidden dark:block\"\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() // validate context\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none hidden dark:block\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene() // validate context\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.d.cts CHANGED
@@ -85,14 +85,6 @@ declare function Blur({ amount, centerX, centerY, innerRadius, outerRadius, }: B
85
85
  declare function Pattern({ dotSize, spacing, lightColor, darkColor, }: PatternProps): react_jsx_runtime.JSX.Element;
86
86
  declare function DarkOverlay({ opacity }: DarkOverlayProps): react_jsx_runtime.JSX.Element;
87
87
  declare function Content({ className, children }: ContentProps): react_jsx_runtime.JSX.Element;
88
- declare const HeroScene: typeof HeroSceneRoot & {
89
- Parallax: typeof Parallax;
90
- Vignette: typeof Vignette;
91
- Blur: typeof Blur;
92
- Pattern: typeof Pattern;
93
- DarkOverlay: typeof DarkOverlay;
94
- Content: typeof Content;
95
- };
96
88
 
97
89
  declare function buildVignetteGradient(color: string, config: Pick<VignetteProps, 'shape' | 'centerX' | 'centerY' | 'stops'>): string;
98
90
  declare function buildBlurMask(config: Pick<BlurProps, 'centerX' | 'centerY' | 'innerRadius' | 'outerRadius'>): string;
@@ -106,4 +98,4 @@ declare function useReducedMotion(): boolean;
106
98
  /** Event name dispatched on `globalThis` when the active image changes. `event.detail` is the new index. */
107
99
  declare const HERO_SCENE_INDEX_EVENT = "hero-scene-index-change";
108
100
 
109
- export { type BlurProps, type ContentProps, type DarkOverlayProps, HERO_SCENE_INDEX_EVENT, type HeroImage, HeroScene, type HeroSceneProps, type ParallaxProps, type PatternProps, type VignetteProps, buildBlurMask, buildVignetteGradient, useReducedMotion };
101
+ export { type BlurProps, type ContentProps, type DarkOverlayProps, HERO_SCENE_INDEX_EVENT, Blur as HeroBlur, Content as HeroContent, DarkOverlay as HeroDarkOverlay, type HeroImage, Parallax as HeroParallax, Pattern as HeroPattern, HeroSceneRoot as HeroScene, type HeroSceneProps, Vignette as HeroVignette, type ParallaxProps, type PatternProps, type VignetteProps, buildBlurMask, buildVignetteGradient, useReducedMotion };
package/dist/index.d.ts CHANGED
@@ -85,14 +85,6 @@ declare function Blur({ amount, centerX, centerY, innerRadius, outerRadius, }: B
85
85
  declare function Pattern({ dotSize, spacing, lightColor, darkColor, }: PatternProps): react_jsx_runtime.JSX.Element;
86
86
  declare function DarkOverlay({ opacity }: DarkOverlayProps): react_jsx_runtime.JSX.Element;
87
87
  declare function Content({ className, children }: ContentProps): react_jsx_runtime.JSX.Element;
88
- declare const HeroScene: typeof HeroSceneRoot & {
89
- Parallax: typeof Parallax;
90
- Vignette: typeof Vignette;
91
- Blur: typeof Blur;
92
- Pattern: typeof Pattern;
93
- DarkOverlay: typeof DarkOverlay;
94
- Content: typeof Content;
95
- };
96
88
 
97
89
  declare function buildVignetteGradient(color: string, config: Pick<VignetteProps, 'shape' | 'centerX' | 'centerY' | 'stops'>): string;
98
90
  declare function buildBlurMask(config: Pick<BlurProps, 'centerX' | 'centerY' | 'innerRadius' | 'outerRadius'>): string;
@@ -106,4 +98,4 @@ declare function useReducedMotion(): boolean;
106
98
  /** Event name dispatched on `globalThis` when the active image changes. `event.detail` is the new index. */
107
99
  declare const HERO_SCENE_INDEX_EVENT = "hero-scene-index-change";
108
100
 
109
- export { type BlurProps, type ContentProps, type DarkOverlayProps, HERO_SCENE_INDEX_EVENT, type HeroImage, HeroScene, type HeroSceneProps, type ParallaxProps, type PatternProps, type VignetteProps, buildBlurMask, buildVignetteGradient, useReducedMotion };
101
+ export { type BlurProps, type ContentProps, type DarkOverlayProps, HERO_SCENE_INDEX_EVENT, Blur as HeroBlur, Content as HeroContent, DarkOverlay as HeroDarkOverlay, type HeroImage, Parallax as HeroParallax, Pattern as HeroPattern, HeroSceneRoot as HeroScene, type HeroSceneProps, Vignette as HeroVignette, type ParallaxProps, type PatternProps, type VignetteProps, buildBlurMask, buildVignetteGradient, useReducedMotion };
package/dist/index.js CHANGED
@@ -151,6 +151,7 @@ function HeroSceneRoot({
151
151
  height: 1080,
152
152
  priority: i === initialIndex,
153
153
  sizes: "100vw",
154
+ className: "dark:grayscale",
154
155
  style: {
155
156
  position: "absolute",
156
157
  inset: 0,
@@ -371,20 +372,18 @@ function Content({ className, children }) {
371
372
  }
372
373
  );
373
374
  }
374
- var HeroScene = Object.assign(HeroSceneRoot, {
375
- Parallax,
376
- Vignette,
377
- Blur,
378
- Pattern,
379
- DarkOverlay,
380
- Content
381
- });
382
375
 
383
376
  // src/index.ts
384
377
  var HERO_SCENE_INDEX_EVENT = "hero-scene-index-change";
385
378
  export {
386
379
  HERO_SCENE_INDEX_EVENT,
387
- HeroScene,
380
+ Blur as HeroBlur,
381
+ Content as HeroContent,
382
+ DarkOverlay as HeroDarkOverlay,
383
+ Parallax as HeroParallax,
384
+ Pattern as HeroPattern,
385
+ HeroSceneRoot as HeroScene,
386
+ Vignette as HeroVignette,
388
387
  buildBlurMask,
389
388
  buildVignetteGradient,
390
389
  useReducedMotion
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// ─── 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 {/* ── Background images (no parallax — Parallax child wraps these) ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\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 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 // Expand the images container to allow parallax overflow\n imagesEl.style.inset = '-15%'\n imagesEl.style.willChange = 'transform'\n\n return () => {\n imagesEl.style.inset = '0'\n imagesEl.style.willChange = ''\n imagesEl.style.transform = ''\n }\n }, [reducedMotion, containerRef])\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 // Parallax is a behavior-only component — renders nothing\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 {images.map((img, i) => (\n <div\n key={`vignette-${img.color}`}\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 </>\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() // validate context\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() // validate context\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 aria-hidden=\"true\"\n className=\"dark:hidden\"\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 aria-hidden=\"true\"\n className=\"hidden dark:block\"\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() // validate context\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none hidden dark:block\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene() // validate context\n\n return (\n <div\n className={className}\n style={{ position: 'relative', zIndex: 30 }}\n >\n {children}\n </div>\n )\n}\n\n// ─── Compound export ─────────────────────────────────────────\n\nexport const HeroScene = Object.assign(HeroSceneRoot, {\n Parallax,\n Vignette,\n Blur,\n Pattern,\n DarkOverlay,\n Content,\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 { HeroScene } 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// ─── 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 {/* ── Background images (no parallax — Parallax child wraps these) ── */}\n <div\n data-hero-images=\"\"\n aria-hidden=\"true\"\n style={{\n position: 'absolute',\n inset: 0,\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 className=\"dark:grayscale\"\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 // Expand the images container to allow parallax overflow\n imagesEl.style.inset = '-15%'\n imagesEl.style.willChange = 'transform'\n\n return () => {\n imagesEl.style.inset = '0'\n imagesEl.style.willChange = ''\n imagesEl.style.transform = ''\n }\n }, [reducedMotion, containerRef])\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 // Parallax is a behavior-only component — renders nothing\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 {images.map((img, i) => (\n <div\n key={`vignette-${img.color}`}\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 </>\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() // validate context\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() // validate context\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 aria-hidden=\"true\"\n className=\"dark:hidden\"\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 aria-hidden=\"true\"\n className=\"hidden dark:block\"\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() // validate context\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none hidden dark:block\"\n style={{\n position: 'absolute',\n inset: 0,\n zIndex: 10,\n backgroundColor: `rgba(0 0 0 / ${opacity})`,\n }}\n />\n )\n}\n\n// ─── Content ─────────────────────────────────────────────────\n\nfunction Content({ className, children }: ContentProps) {\n useHeroScene() // validate context\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.3.0",
3
+ "version": "0.4.1",
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",