{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sticky-surface",
  "files": [
    {
      "path": "ui/sticky-surface/StickySurface.astro",
      "content": "---\n// Generated from bejamas-juno style registry. Do not edit packages/ui/src/components/sticky-surface/StickySurface.astro directly.\n/**\n * @component StickySurface\n * @title Sticky Surface\n * @description A sticky surface component with scroll-based effects (border, shadow, surface swap, shrink, glass).\n * @figmaUrl \n * \n * @preview\n * \n * <div class=\"overflow-y-auto max-h-[320px] w-full bg-border px-8 border-border\">\n *   <div class=\"h-[1000px] bg-muted\">\n *     <StickySurface effects={[\"line\", \"elevate\"]} threshold=\"1rem\" observeStuck class=\"bg-background p-4 flex justify-between items-center\">\n *       <p>Scroll inside!</p>\n *       <div class=\"flex items-center gap-2\">\n *         <Button size=\"sm\" variant=\"outline\" class=\"group-[.is-stuck]/surface:opacity-0 will-change-transform group-[.is-stuck]/surface:blur-sm transition-all duration-300 ease-out\">Log in</Button>\n *         <Button size=\"sm\" class=\"w-24 group-[.is-stuck]/surface:w-28 duration-500 ease-out\">\n *           <span class=\"group-[.is-stuck]/surface:blur-sm group-[.is-stuck]/surface:opacity-0 absolute group-[.is-stuck]/surface:hidden transition-discrete duration-500 ease-out\">Contact</span>\n *           <span class=\"group-[.is-stuck]/surface:blur-none group-[.is-stuck]/surface:opacity-100 group-[.is-stuck]/surface:delay-150 opacity-0 transition-all blur-sm duration-500 ease-out\">Get a demo</span>\n *         </Button>\n *       </div>\n *     </StickySurface>\n *   </div>\n * </div>\n *\n * @usage\n *\n * ```astro nocollapse\n * ---\n * import { StickySurface } from '@bejamas/ui/components/sticky-surface';\n * ---\n *\n * <StickySurface effects={[\"line\", \"elevate\"]} threshold=\"1rem\">\n *   <!-- Your content here -->\n * </StickySurface>\n * ```\n */\n\nimport type { HTMLTag } from \"astro/types\";\nimport { cva } from \"class-variance-authority\";\nimport { cn } from \"@/lib/utils\";\n\ninterface Props {\n  as?: HTMLTag;\n  threshold?: string | number;\n  effects?: Array<\"line\" | \"elevate\" | \"backdrop\">;\n  class?: string;\n  style?: string;\n  observeStuck?: boolean;\n}\n\nconst {\n  as = \"div\",\n  threshold = \"16px\",\n  effects = [],\n  class: className = \"\",\n  style = \"\",\n  ...props\n} = Astro.props as Props;\n\n// Compose effect booleans for easier use\nconst hasLine = effects.includes(\"line\");\nconst hasElevate = effects.includes(\"elevate\");\nconst hasBackdrop = effects.includes(\"backdrop\");\n\n// CSS variable tokens (can be overridden via Tailwind arbitrary props)\nconst defaultVars = {\n  '--stuck-shadow': '0 2px 8px 0 rgb(0 0 0 / 0.06)',\n  '--stuck-threshold': typeof threshold === \"number\" ? `${threshold}px` : threshold,\n};\n\n\nconst Tag = as;\n\n// cva for sticky surface effects\nconst stickySurfaceVariants = cva(\n  [\n    \"group/surface\", \"sticky\", \"top-0\", \"z-50\", \"w-full\", \"transition-all\", \"duration-300\", \"will-change-transform\"\n  ],\n  {\n    variants: {\n      line: {\n        true: \"after:content-[''] after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-px after:bg-black/15 after:opacity-0 supports-[animation-timeline:scroll()]:after:[animation:show-line_linear_both] supports-[animation-timeline:scroll()]:after:[animation-timeline:scroll(nearest_block)] supports-[animation-timeline:scroll()]:after:[animation-range:0_var(--stuck-threshold)]\"\n      },\n      elevate: {\n        true: \"shadow-none supports-[animation-timeline:scroll()]:[animation:stuck-shadow_linear_both] supports-[animation-timeline:scroll()]:[animation-timeline:scroll(nearest_block)] supports-[animation-timeline:scroll()]:[animation-range:0_var(--stuck-threshold)]\"\n      },\n      backdrop: {\n        true: \"backdrop-blur supports-[animation-timeline:scroll()]:[animation:stuck-backdrop_linear_both] supports-[animation-timeline:scroll()]:[animation-timeline:scroll(nearest_block)] supports-[animation-timeline:scroll()]:[animation-range:0_var(--stuck-threshold)]\"\n      }\n    }\n  }\n);\n\nconst effectClasses = cn(\n  \"\",\n  stickySurfaceVariants({\n    line: hasLine,\n    elevate: hasElevate,\n    backdrop: hasBackdrop\n  }),\n  className\n);\n\nconst styleVars = Object.entries(defaultVars)\n  ?.map(([k, v]) => `${k}:${v}`)\n  ?.join(\";\");\n---\n\n<Tag\n  class={effectClasses}\n  style={`${styleVars};${style}`}\n  data-sticky-surface\n  {...props}\n>\n  <slot />\n</Tag>\n\n<style>\n@supports (animation-timeline: scroll()) {\n  /* Line fade-in */\n  @keyframes show-line {\n    from { opacity: 0 }\n    to { opacity: 1 }\n  }\n  /* Shadow fade-in */\n  @keyframes stuck-shadow {\n    from { box-shadow: none }\n    to { box-shadow: var(--stuck-shadow) }\n  }\n}\n</style>\n\n\n{Astro.props.observeStuck && (\n  <script is:inline>\n    (() => {\n      const surfaces = document.querySelectorAll('[data-sticky-surface]');\n      surfaces.forEach(surface => {\n        // Insert a 1px sentinel right before the sticky element\n        const sentinel = document.createElement('div');\n        sentinel.style.cssText = 'position:relative;height:0;opacity:0;visibility:hidden;';\n        surface.parentNode?.insertBefore(sentinel, surface);\n    \n        const io = new IntersectionObserver(([e]) => {\n          surface.classList.toggle('is-stuck', !e.isIntersecting);\n        }, { threshold: [0, 1] });\n    \n        io.observe(sentinel);\n      });\n    })();\n  </script>\n)}\n",
      "type": "registry:ui"
    },
    {
      "path": "ui/sticky-surface/index.ts",
      "content": "// Generated from bejamas-juno style registry. Do not edit packages/ui/src/components/sticky-surface/index.ts directly.\nexport { default as StickySurface } from \"./StickySurface.astro\";\n\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}