{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "reveal2",
  "title": "Comparison Slider with Highlights",
  "description": "Split-layout image comparison with a sidebar featuring benefit highlights and icons alongside a draggable before/after slider. Perfect for demonstrating product improvements with detailed feature callouts.",
  "dependencies": [
    "clsx",
    "tailwind-merge",
    "lucide-react"
  ],
  "registryDependencies": [
    "badge"
  ],
  "files": [
    {
      "path": "registry/reveal2/reveal2.tsx",
      "content": "\"use client\";\n\nimport { Check, GripHorizontal, GripVertical, Sparkles, Zap } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\n\ninterface Highlight {\n  id: string;\n  icon?: React.ReactNode;\n  title: string;\n  description?: string;\n}\n\ninterface Reveal2Props {\n  badge?: {\n    label: string;\n    variant?: \"default\" | \"secondary\" | \"outline\";\n  };\n  heading?: string;\n  description?: string;\n  highlights?: Highlight[];\n  beforeImage?: {\n    src: string;\n    alt: string;\n  };\n  afterImage?: {\n    src: string;\n    alt: string;\n  };\n  beforeLabel?: string;\n  afterLabel?: string;\n  showLabels?: boolean;\n  orientation?: \"horizontal\" | \"vertical\";\n  initialPosition?: number;\n  dividerWidth?: number;\n  className?: string;\n}\n\nexport const reveal2Demo: Reveal2Props = {\n  badge: { label: \"Compare\", variant: \"secondary\" },\n  heading: \"See the difference\",\n  description:\n    \"Drag the slider to compare the before and after states. Our solution delivers measurable improvements.\",\n  highlights: [\n    {\n      id: \"highlight-1\",\n      icon: <Check className=\"size-5\" />,\n      title: \"Performance boost\",\n      description: \"Up to 3x faster load times with optimized rendering.\",\n    },\n    {\n      id: \"highlight-2\",\n      icon: <Sparkles className=\"size-5\" />,\n      title: \"Better clarity\",\n      description: \"Enhanced visuals with improved contrast and sharpness.\",\n    },\n    {\n      id: \"highlight-3\",\n      icon: <Zap className=\"size-5\" />,\n      title: \"Reduced noise\",\n      description: \"Cleaner output with advanced noise reduction algorithms.\",\n    },\n  ],\n  beforeImage: {\n    src: \"https://images.unsplash.com/photo-1558370781-d6196949e317?q=80&w=2958&auto=format&fit=crop&sat=-100\",\n    alt: \"Before\",\n  },\n  afterImage: {\n    src: \"https://images.unsplash.com/photo-1558370781-d6196949e317?q=80&w=2958&auto=format&fit=crop\",\n    alt: \"After\",\n  },\n  beforeLabel: \"Before\",\n  afterLabel: \"After\",\n  showLabels: true,\n  orientation: \"horizontal\",\n  initialPosition: 50,\n  dividerWidth: 4,\n};\n\nexport function Reveal2({\n  badge,\n  heading,\n  description,\n  highlights = [],\n  beforeImage,\n  afterImage,\n  beforeLabel = \"Before\",\n  afterLabel = \"After\",\n  showLabels = true,\n  orientation = \"horizontal\",\n  initialPosition = 50,\n  dividerWidth = 4,\n  className,\n}: Reveal2Props) {\n  const [position, setPosition] = useState(initialPosition);\n  const [isDragging, setIsDragging] = useState(false);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  const isHorizontal = orientation === \"horizontal\";\n\n  const handleMove = useCallback(\n    (clientX: number, clientY: number) => {\n      if (!containerRef.current) return;\n\n      const rect = containerRef.current.getBoundingClientRect();\n\n      if (isHorizontal) {\n        const x = clientX - rect.left;\n        const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100));\n        setPosition(percentage);\n      } else {\n        const y = clientY - rect.top;\n        const percentage = Math.max(0, Math.min(100, (y / rect.height) * 100));\n        setPosition(percentage);\n      }\n    },\n    [isHorizontal]\n  );\n\n  const handleMouseDown = useCallback(\n    (e: React.MouseEvent) => {\n      e.preventDefault();\n      setIsDragging(true);\n      handleMove(e.clientX, e.clientY);\n    },\n    [handleMove]\n  );\n\n  const handleTouchStart = useCallback(\n    (e: React.TouchEvent) => {\n      setIsDragging(true);\n      const touch = e.touches[0];\n      if (touch) {\n        handleMove(touch.clientX, touch.clientY);\n      }\n    },\n    [handleMove]\n  );\n\n  useEffect(() => {\n    const handleMouseMove = (e: MouseEvent) => {\n      if (isDragging) {\n        handleMove(e.clientX, e.clientY);\n      }\n    };\n\n    const handleTouchMove = (e: TouchEvent) => {\n      if (isDragging) {\n        const touch = e.touches[0];\n        if (touch) {\n          handleMove(touch.clientX, touch.clientY);\n        }\n      }\n    };\n\n    const handleEnd = () => {\n      setIsDragging(false);\n    };\n\n    if (isDragging) {\n      document.addEventListener(\"mousemove\", handleMouseMove);\n      document.addEventListener(\"touchmove\", handleTouchMove);\n      document.addEventListener(\"mouseup\", handleEnd);\n      document.addEventListener(\"touchend\", handleEnd);\n    }\n\n    return () => {\n      document.removeEventListener(\"mousemove\", handleMouseMove);\n      document.removeEventListener(\"touchmove\", handleTouchMove);\n      document.removeEventListener(\"mouseup\", handleEnd);\n      document.removeEventListener(\"touchend\", handleEnd);\n    };\n  }, [isDragging, handleMove]);\n\n  return (\n    <section className={cn(\"py-16 md:py-24 w-full\", className)}>\n      <div className=\"mx-auto max-w-6xl px-4 md:px-6\">\n        <div className=\"grid gap-6 lg:grid-cols-12 lg:items-center\">\n          {/* Content */}\n          <div className=\"flex flex-col gap-4 lg:col-span-5\">\n            {badge && (\n              <div>\n                <Badge variant={badge.variant ?? \"default\"}>{badge.label}</Badge>\n              </div>\n            )}\n\n            {heading && <h2 className=\"text-2xl font-semibold md:text-4xl\">{heading}</h2>}\n\n            {description && (\n              <p className=\"max-w-xl text-base md:text-lg text-muted-foreground\">{description}</p>\n            )}\n\n            {highlights.length > 0 && (\n              <div className=\"mt-2 grid gap-3\">\n                {highlights.slice(0, 6).map((h) => (\n                  <div key={h.id} className=\"rounded-lg bg-muted p-4\">\n                    <div className=\"flex items-start gap-3\">\n                      <span className=\"text-muted-foreground\">\n                        {h.icon || <Check className=\"size-5\" />}\n                      </span>\n                      <div className=\"min-w-0\">\n                        <p className=\"text-sm font-semibold\">{h.title}</p>\n                        {h.description && (\n                          <p className=\"mt-1 text-sm text-muted-foreground\">{h.description}</p>\n                        )}\n                      </div>\n                    </div>\n                  </div>\n                ))}\n              </div>\n            )}\n          </div>\n\n          {/* Slider */}\n          <div className=\"rounded-lg bg-muted lg:col-span-7 md:mt-4\">\n            <div\n              ref={containerRef}\n              role=\"slider\"\n              aria-label=\"Before/After comparison slider\"\n              aria-valuemin={0}\n              aria-valuemax={100}\n              aria-valuenow={position}\n              tabIndex={0}\n              className={cn(\n                \"relative rounded-lg overflow-hidden border select-none\",\n                \"aspect-[16/10] cursor-ew-resize\",\n                !isHorizontal && \"cursor-ns-resize\"\n              )}\n              onMouseDown={handleMouseDown}\n              onTouchStart={handleTouchStart}\n            >\n              {/* After Image (Bottom layer - full) */}\n              <div className=\"absolute inset-0\">\n                {afterImage?.src ? (\n                  <img\n                    className=\"absolute inset-0 size-full object-cover\"\n                    src={afterImage.src}\n                    alt={afterImage.alt}\n                  />\n                ) : (\n                  <div className=\"w-full h-full bg-muted/50 flex items-center justify-center\">\n                    <span className=\"text-muted-foreground text-sm\">{afterLabel} image</span>\n                  </div>\n                )}\n              </div>\n\n              {/* Before Image (Top layer - clipped) */}\n              <div\n                className=\"absolute inset-0\"\n                style={{\n                  clipPath: isHorizontal\n                    ? `inset(0 ${100 - position}% 0 0)`\n                    : `inset(0 0 ${100 - position}% 0)`,\n                }}\n              >\n                {beforeImage?.src ? (\n                  <img\n                    className=\"absolute inset-0 size-full object-cover\"\n                    src={beforeImage.src}\n                    alt={beforeImage.alt}\n                  />\n                ) : (\n                  <div className=\"w-full h-full bg-muted flex items-center justify-center\">\n                    <span className=\"text-muted-foreground text-sm\">{beforeLabel} image</span>\n                  </div>\n                )}\n              </div>\n\n              {/* Divider Line */}\n              <div\n                className={cn(\n                  \"absolute bg-white shadow-lg z-10\",\n                  isHorizontal\n                    ? \"top-0 bottom-0 transform -translate-x-1/2\"\n                    : \"left-0 right-0 transform -translate-y-1/2\"\n                )}\n                style={{\n                  [isHorizontal ? \"left\" : \"top\"]: `${position}%`,\n                  [isHorizontal ? \"width\" : \"height\"]: `${dividerWidth}px`,\n                }}\n              >\n                {/* Handle */}\n                <div\n                  className={cn(\n                    \"absolute bg-white rounded-full shadow-xl\",\n                    \"flex items-center justify-center\",\n                    \"w-10 h-10 border-3 border-primary\",\n                    \"left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2\",\n                    isDragging && \"scale-110\"\n                  )}\n                >\n                  {isHorizontal ? (\n                    <GripVertical className=\"w-5 h-5 text-primary\" />\n                  ) : (\n                    <GripHorizontal className=\"w-5 h-5 text-primary\" />\n                  )}\n                </div>\n              </div>\n\n              {/* Labels */}\n              {showLabels && (\n                <>\n                  <div\n                    className={cn(\n                      \"absolute z-20 px-3 py-1.5 rounded-full\",\n                      \"bg-black/70 text-white text-sm font-medium backdrop-blur-sm\",\n                      \"top-4 left-4\"\n                    )}\n                  >\n                    {beforeLabel}\n                  </div>\n                  <div\n                    className={cn(\n                      \"absolute z-20 px-3 py-1.5 rounded-full\",\n                      \"bg-black/70 text-white text-sm font-medium backdrop-blur-sm\",\n                      isHorizontal ? \"top-4 right-4\" : \"bottom-4 left-4\"\n                    )}\n                  >\n                    {afterLabel}\n                  </div>\n                </>\n              )}\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/beste/block/reveal2.tsx"
    }
  ],
  "type": "registry:block"
}