"use client"; /** * In-view reveal — the page's core entrance vocabulary. * - Default: lift + fade + a touch of blur(4px -> 0) so content resolves INTO * place instead of just sliding (Emil's blur-masked entrance). Never appears * from nothing. * - variant="clip": a premium clip-path inset() wipe (bottom -> full) for * section/media reveals, paired with a small lift. * - `stagger` cascades direct children via variants (30–80ms). * - Custom EASE_OUT curve everywhere; honors prefers-reduced-motion (keeps * opacity/color, drops all movement + blur). */ import { motion, type Variants, useReducedMotion } from "motion/react"; import { EASE_OUT } from "./motion"; type Props = { children: React.ReactNode; className?: string; delay?: number; y?: number; stagger?: number; variant?: "lift" | "clip"; as?: | "div" | "section" | "ul" | "ol" | "li" | "p" | "figure" | "header" | "span" | "h2"; }; const VIEWPORT = { once: true, margin: "0px 0px -12% 0px" } as const; export default function Reveal({ children, className, delay = 0, y = 28, stagger, variant = "lift", as = "div", }: Props) { const reduce = useReducedMotion(); const MotionTag = motion[as] as typeof motion.div; if (stagger) { const parent: Variants = { hidden: {}, show: { transition: { staggerChildren: reduce ? 0 : stagger, delayChildren: delay / 1000, }, }, }; return ( {children} ); } const hidden = variant === "clip" ? reduce ? { opacity: 1 } : { opacity: 0, y: y * 0.5, clipPath: "inset(0 0 100% 0)" } : reduce ? { opacity: 1 } : { opacity: 0, y, filter: "blur(4px)" }; const shown = variant === "clip" ? { opacity: 1, y: 0, clipPath: "inset(0 0 0% 0)" } : { opacity: 1, y: 0, filter: "blur(0px)" }; return ( {children} ); } /** A child item that participates in a parent . */ export function RevealItem({ children, className, y = 24, as = "div", }: { children: React.ReactNode; className?: string; y?: number; as?: "div" | "li" | "p"; }) { const reduce = useReducedMotion(); const MotionTag = motion[as] as typeof motion.div; const item: Variants = { hidden: reduce ? { opacity: 1 } : { opacity: 0, y, filter: "blur(4px)" }, show: { opacity: 1, y: 0, filter: "blur(0px)", transition: { duration: 0.75, ease: EASE_OUT }, }, }; return ( {children} ); }