{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "blur-in",
  "type": "registry:ui",
  "title": "Blur In",
  "description": "Reveals content with a fade, a small rise and a clearing blur, once.",
  "categories": [
    "motion"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://www.stealth.pm/r/tokens.json",
    "https://www.stealth.pm/r/motion.json"
  ],
  "files": [
    {
      "path": "components/ui/blur-in.tsx",
      "content": "\"use client\";\nimport { motion, useReducedMotion, type Transition, type Variants } from \"motion/react\";\nimport { createContext, useContext, useState } from \"react\";\nimport { ease } from \"@/lib/motion\";\n\ntype Tag = \"div\" | \"section\" | \"article\" | \"header\" | \"ul\" | \"ol\" | \"li\" | \"p\" | \"span\" | \"h1\" | \"h2\" | \"h3\";\n\n// Motion's own handlers share these names with different signatures.\ntype Native = Omit<\n  React.ComponentProps<\"div\">,\n  \"ref\" | \"onDrag\" | \"onDragStart\" | \"onDragEnd\" | \"onAnimationStart\" | \"onAnimationEnd\" | \"onAnimationIteration\"\n>;\n\nexport type BlurInProps = Native & {\n  /** The element to render. */\n  as?: Tag;\n  /** mount: as soon as it renders. inView: the first time it scrolls into view (at once if it already is). */\n  trigger?: \"mount\" | \"inView\";\n  /** With inView: play again each time it re-enters. Off by default; a reveal is for the first look. */\n  replay?: boolean;\n  /** With inView: how much of it must be visible, 0–1. */\n  amount?: number;\n  /** With inView: the scrolling element to watch instead of the window. */\n  root?: React.RefObject<Element | null>;\n  /** Seconds before it starts, e.g. to follow something else in. */\n  delay?: number;\n  /** Seconds the reveal takes. */\n  duration?: number;\n  /** How far it rises, in px. */\n  y?: number;\n  /** How much blur it clears, in px. Keep it small on large areas. */\n  blur?: number;\n  /**\n   * Reveal the BlurInItem children one after another instead of this element as a whole.\n   * true staggers them 50ms apart; a number sets the gap in seconds. Capped after the eighth.\n   */\n  stagger?: boolean | number;\n  /** Called once the reveal has finished. */\n  onRevealed?: () => void;\n  ref?: React.Ref<HTMLElement>;\n};\n\nexport type BlurInItemProps = Native & { as?: Tag; ref?: React.Ref<HTMLElement> };\n\ntype Shape = { y: number; blur: number; duration: number; reduce: boolean; settled: boolean };\nconst Ctx = createContext<Shape | null>(null);\n\n// Hidden and shown are the same shape everywhere, so server and client render the\n// same first frame whatever the motion setting. Reduced motion only changes the\n// transition: the fade stays, rise and blur snap. The filter is removed when done,\n// because even blur(0px) traps position: fixed descendants.\n// Items leave delay out entirely: any delay of their own, even 0, overrides the stagger.\nfunction reveal({ y, blur, duration, reduce }: Omit<Shape, \"settled\">, delay?: number): Variants {\n  const wait = delay ? { delay: reduce ? Math.min(delay, 0.1) : delay } : {};\n  const transition: Transition = reduce\n    ? { duration: 0.2, ease: ease.out, y: { duration: 0 }, filter: { duration: 0 }, ...wait }\n    : { duration, ease: ease.out, ...wait };\n  return {\n    hidden: { opacity: 0, y, filter: `blur(${blur}px)` },\n    shown: { opacity: 1, y: 0, filter: \"blur(0px)\", transition, transitionEnd: { filter: \"none\" } },\n  };\n}\n\n// Stagger by position, but stop adding delay after the eighth item, so the tail of a\n// long list never arrives after someone has started reading.\nconst capped = (gap: number, start: number) => (i: number) => start + Math.min(i, 7) * gap;\n\nexport function BlurIn({\n  as = \"div\",\n  trigger = \"inView\",\n  replay = false,\n  amount = 0.15,\n  root,\n  delay = 0,\n  duration = 0.5,\n  y = 8,\n  blur = 4,\n  stagger = false,\n  onRevealed,\n  className,\n  children,\n  ref,\n  ...rest\n}: BlurInProps) {\n  const reduce = !!useReducedMotion();\n  // Items that mount after the reveal (a list that grows) are simply there.\n  const [settled, setSettled] = useState(false);\n  const Comp = motion[as] as typeof motion.div;\n  const staggered = stagger !== false;\n  const gap = stagger === true ? 0.05 : typeof stagger === \"number\" ? stagger : 0;\n\n  const variants: Variants = staggered\n    ? { hidden: {}, shown: { transition: { delayChildren: reduce ? delay : capped(gap, delay) } } }\n    : reveal({ y, blur, duration, reduce }, delay);\n\n  const play =\n    trigger === \"mount\"\n      ? { animate: \"shown\" }\n      : { whileInView: \"shown\", viewport: { once: !replay, amount, root } };\n\n  return (\n    <Ctx.Provider value={{ y, blur, duration, reduce, settled }}>\n      <Comp\n        ref={ref as React.Ref<HTMLDivElement>}\n        data-blur-in=\"\"\n        data-trigger={trigger}\n        initial=\"hidden\"\n        {...play}\n        variants={variants}\n        onAnimationComplete={(name) => {\n          if (name !== \"shown\") return;\n          setSettled(true);\n          onRevealed?.();\n        }}\n        className={className}\n        {...rest}\n      >\n        {children}\n      </Comp>\n      {/* Without JavaScript nothing would ever reveal it, so it starts visible. */}\n      <noscript>\n        <style>{`[data-blur-in],[data-blur-in-item]{opacity:1!important;transform:none!important;filter:none!important}`}</style>\n      </noscript>\n    </Ctx.Provider>\n  );\n}\n\n/** One piece of a staggered BlurIn. Outside one it renders as a plain element. */\nexport function BlurInItem({ as = \"div\", className, children, ref, ...rest }: BlurInItemProps) {\n  const shape = useContext(Ctx);\n  const Comp = motion[as] as typeof motion.div;\n  if (!shape) {\n    const Plain = as as \"div\";\n    return (\n      <Plain ref={ref as React.Ref<HTMLDivElement>} className={className} {...(rest as React.ComponentProps<\"div\">)}>\n        {children}\n      </Plain>\n    );\n  }\n  return (\n    <Comp\n      ref={ref as React.Ref<HTMLDivElement>}\n      data-blur-in-item=\"\"\n      // No initial label of its own: that would detach it from the parent's variants.\n      // It inherits \"hidden\" and plays \"shown\" when the parent does, on the stagger.\n      initial={shape.settled ? false : undefined}\n      variants={reveal(shape)}\n      className={className}\n      {...rest}\n    >\n      {children}\n    </Comp>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "https://www.stealth.pm/ui/blur-in"
}
