{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "in-view",
  "title": "In View",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A wrapper that knows when it is on screen: it sets a data attribute for CSS, offers a render function and callbacks, and can run a simple entrance.",
  "dependencies": [
    "motion@^12"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/in-view.tsx",
      "content": "// Ballmac UI: In View. https://ui.ballmac.com/components/in-view\n\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useInView, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype InViewEffect = \"none\" | \"fade\" | \"slide-up\" | \"slide-down\" | \"scale\" | \"blur\"\n\nconst FROM: Record<Exclude<InViewEffect, \"none\">, Record<string, number | string>> = {\n  fade: { opacity: 0 },\n  \"slide-up\": { opacity: 0, y: 24 },\n  \"slide-down\": { opacity: 0, y: -24 },\n  scale: { opacity: 0, scale: 0.92 },\n  blur: { opacity: 0, filter: \"blur(10px)\" },\n}\n\ntype InViewProps = Omit<React.ComponentProps<\"div\">, \"children\" | \"ref\"> & {\n  /** Content, or a function that receives whether the element is in view. */\n  children: React.ReactNode | ((inView: boolean) => React.ReactNode)\n  /** Built-in entrance. \"none\" only reports the state and leaves styling to you. */\n  effect?: InViewEffect\n  /** Fraction of the element that must be visible: 0 to 1, or \"some\" / \"all\". */\n  amount?: number | \"some\" | \"all\"\n  /** Shrinks or grows the viewport used for the check, like CSS margins: \"0px 0px -20% 0px\". */\n  margin?: string\n  /** Stay revealed after the first time. */\n  once?: boolean\n  /** Seconds before the entrance starts. */\n  delay?: number\n  /** Seconds the entrance takes. */\n  duration?: number\n  /** Called whenever the element enters or leaves the view. */\n  onInViewChange?: (inView: boolean) => void\n  /** Element to render. */\n  as?: \"div\" | \"section\" | \"article\" | \"li\" | \"span\"\n}\n\n/** Tells you when an element is on screen. Sets `data-in-view` for CSS, and can run a simple entrance. */\nfunction InView({ children, effect = \"fade\", amount = 0.25, margin, once = true, delay = 0, duration = 0.55, onInViewChange, as = \"div\", className, ...props }: InViewProps) {\n  const ref = React.useRef<HTMLElement>(null)\n  const reduce = useReducedMotion()\n  const inView = useInView(ref, { once, amount, margin: margin as `${number}px` | undefined })\n  const last = React.useRef<boolean | null>(null)\n  const callback = React.useRef(onInViewChange)\n  React.useEffect(() => {\n    callback.current = onInViewChange\n  })\n  React.useEffect(() => {\n    if (last.current !== inView) {\n      if (last.current !== null || inView) callback.current?.(inView)\n      last.current = inView\n    }\n  }, [inView])\n\n  const Tag = motion[as] as typeof motion.div\n  const animated = effect !== \"none\" && !reduce\n  return (\n    <Tag\n      ref={ref as React.Ref<HTMLDivElement>}\n      data-slot=\"in-view\"\n      data-in-view={inView}\n      initial={false}\n      animate={animated ? (inView ? { opacity: 1, x: 0, y: 0, scale: 1, filter: \"blur(0px)\" } : FROM[effect as Exclude<InViewEffect, \"none\">]) : undefined}\n      transition={{ duration, delay: inView ? delay : 0, ease: [0.22, 1, 0.36, 1] }}\n      className={cn(className)}\n      {...(props as object)}\n    >\n      {typeof children === \"function\" ? children(inView) : children}\n    </Tag>\n  )\n}\n\nexport { InView, type InViewProps, type InViewEffect }\n",
      "type": "registry:ui",
      "target": "@components/ballmac/in-view.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "viewport",
      "scroll",
      "observer",
      "reveal",
      "trigger"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "<InView effect='fade|slide-up|slide-down|scale|blur|none' amount margin once onInViewChange>{content or (inView)=>content}</InView>. data-in-view is true or false for Tailwind's data-[in-view=true] variants.",
      "whenToUse": [
        "Starting a video, counter or animation only when seen",
        "Custom reveal styling driven by data-in-view"
      ],
      "whenNotToUse": [
        "Staggered groups (blur-fade or stagger-list)"
      ],
      "composesWith": [
        "blur-fade",
        "number-ticker",
        "stagger-list"
      ],
      "a11y": [
        {
          "keys": "Screen readers",
          "action": "Content is always in the DOM; only presentation changes"
        },
        {
          "keys": "Reduced motion",
          "action": "Content is shown with no entrance"
        }
      ],
      "customization": [
        "effect",
        "amount and margin",
        "once",
        "onInViewChange",
        "as"
      ]
    },
    "examples": [
      "in-view-demo",
      "in-view-state"
    ],
    "url": "https://ui.ballmac.com/components/in-view"
  },
  "categories": [
    "motion"
  ],
  "type": "registry:ui"
}