{
  "schemaVersion": 1,
  "target": "react",
  "name": "popover",
  "version": "0.2.0",
  "category": "core",
  "title": "Popover",
  "description": "A floating panel anchored to a trigger, built on Base UI Popover, that grows from the trigger and has an optional hairline arrow.",
  "tags": [
    "overlay",
    "popover",
    "floating",
    "base-ui"
  ],
  "requires": {
    "projectTypes": [
      "next",
      "vite-react"
    ],
    "styling": "tailwind-v4"
  },
  "npmDependencies": {
    "@base-ui/react": "^1.9.0"
  },
  "registryDependencies": [],
  "docsPath": "/components/popover",
  "props": [
    {
      "name": "Popover open / defaultOpen / onOpenChange",
      "type": "boolean / boolean / (open, details) => void",
      "description": "Root state. Leave open unset for an uncontrolled popover."
    },
    {
      "name": "Popover modal",
      "type": "boolean | \"trap-focus\"",
      "default": "false",
      "description": "Traps focus and blocks the page while open. Off by default."
    },
    {
      "name": "PopoverTrigger",
      "type": "Base UI Popover.Trigger props",
      "description": "Unstyled button that toggles the popover. Pass className, or render={<Button />}. openOnHover is supported."
    },
    {
      "name": "PopoverContent side",
      "type": "\"top\" | \"bottom\" | \"left\" | \"right\" | \"inline-start\" | \"inline-end\"",
      "default": "\"bottom\"",
      "description": "Preferred side of the trigger. Flips when there is not enough room."
    },
    {
      "name": "PopoverContent align",
      "type": "\"start\" | \"center\" | \"end\"",
      "default": "\"center\"",
      "description": "Alignment against the trigger."
    },
    {
      "name": "PopoverContent sideOffset / alignOffset",
      "type": "number",
      "default": "8 / 0",
      "description": "Gap from the trigger and shift along its edge, in pixels."
    },
    {
      "name": "PopoverContent arrow",
      "type": "boolean",
      "default": "false",
      "description": "Shows a 16 by 9 notch drawn in the panel fill and border color, pointing at the trigger. Adds 2px to sideOffset so the tip clears the trigger."
    },
    {
      "name": "PopoverContent positionerClassName",
      "type": "string",
      "description": "Classes for the positioner, for example a different z-index."
    },
    {
      "name": "PopoverTitle / PopoverDescription",
      "type": "Base UI Popover.Title / Popover.Description props",
      "description": "Optional heading and text, wired to aria-labelledby and aria-describedby."
    },
    {
      "name": "PopoverClose",
      "type": "Base UI Popover.Close props",
      "description": "Unstyled button that closes the popover."
    }
  ],
  "accessibility": [
    "The trigger sets aria-expanded and aria-controls; the panel has role=\"dialog\", named by PopoverTitle when present.",
    "Opening moves focus into the panel and closing returns it to the trigger.",
    "Escape and a click outside close the popover.",
    "The arrow is decorative and hidden from assistive technology.",
    "The panel scales from the trigger side (origin-aware) in 160ms and fades out in 110ms; with reduced motion only the opacity changes, and transitions are removed entirely."
  ],
  "states": [
    "closed",
    "open"
  ],
  "files": [
    {
      "path": "ui/popover.tsx",
      "content": "\"use client\";\n\nimport { Popover as BasePopover } from \"@base-ui/react/popover\";\n\ntype ClassName<State> = string | ((state: State) => string | undefined) | undefined;\n\nfunction cx(...classes: (string | false | null | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\n\n/** Prepends base classes while keeping Base UI's state-function form of className working. */\nfunction withBase<State>(base: string, className: ClassName<State>) {\n  return typeof className === \"function\" ? (state: State) => cx(base, className(state)) : cx(base, className);\n}\n\n/** Groups the popover parts and holds its open state. Pass `open`/`onOpenChange` to control it. */\nexport const Popover = BasePopover.Root;\n\n/** A button that toggles the popover. Unstyled; pass `className`, or `render={<Button />}` to reuse your button. */\nexport const PopoverTrigger = BasePopover.Trigger;\n\n/** A button that closes the popover. Unstyled; pass `className` or `render`. */\nexport const PopoverClose = BasePopover.Close;\n\ntype PositionerProps = BasePopover.Positioner.Props;\n\nexport interface PopoverContentProps extends BasePopover.Popup.Props {\n  /** Side of the trigger to place the popover on. Flips when there is not enough room. */\n  side?: PositionerProps[\"side\"];\n  /** Alignment against the trigger on the chosen side. */\n  align?: PositionerProps[\"align\"];\n  /** Gap between the trigger and the popover, in pixels. */\n  sideOffset?: PositionerProps[\"sideOffset\"];\n  /** Shift along the trigger edge, in pixels. */\n  alignOffset?: PositionerProps[\"alignOffset\"];\n  /** Shows an arrow pointing at the trigger. */\n  arrow?: boolean;\n  /** Classes for the positioner wrapper, for example to change the z-index. */\n  positionerClassName?: string;\n  /** Element to portal into. Defaults to `document.body`. */\n  container?: BasePopover.Portal.Props[\"container\"];\n}\n\n/** The floating panel, rendered in a portal and positioned against the trigger. */\nexport function PopoverContent({\n  side = \"bottom\",\n  align = \"center\",\n  sideOffset = 8,\n  alignOffset = 0,\n  arrow = false,\n  positionerClassName,\n  container,\n  className,\n  children,\n  ...props\n}: PopoverContentProps) {\n  return (\n    <BasePopover.Portal container={container}>\n      <BasePopover.Positioner\n        side={side}\n        align={align}\n        sideOffset={arrow && typeof sideOffset === \"number\" ? sideOffset + 2 : sideOffset}\n        alignOffset={alignOffset}\n        className={cx(\"z-50\", positionerClassName)}\n      >\n        <BasePopover.Popup\n          className={withBase(\n            cx(\n              \"relative w-72 max-w-(--available-width) origin-(--transform-origin) rounded-xl p-4 text-sm\",\n              \"border border-zinc-950/10 bg-white text-zinc-950 shadow-[0_12px_32px_-8px_rgb(0_0_0/0.18)]\",\n              \"dark:border-white/15 dark:bg-black dark:text-white dark:shadow-[0_16px_48px_-12px_rgb(0_0_0/1),inset_0_1px_0_rgb(255_255_255/0.08)]\",\n              \"outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500\",\n              // Enter: 160ms from 96% at the trigger. Exit: 110ms, fades with a smaller scale.\n              \"transition-[opacity,scale] duration-160 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none\",\n              \"data-[starting-style]:opacity-0 motion-safe:data-[starting-style]:scale-96\",\n              \"data-[ending-style]:opacity-0 data-[ending-style]:duration-110 data-[ending-style]:ease-out motion-safe:data-[ending-style]:scale-98\",\n            ),\n            className,\n          )}\n          {...props}\n        >\n          {arrow ? <PopoverArrow /> : null}\n          {children}\n        </BasePopover.Popup>\n      </BasePopover.Positioner>\n    </BasePopover.Portal>\n  );\n}\n\n/** A 16 x 9 notch drawn in the panel's own fill and hairline, so it reads as part of the border. */\nfunction PopoverArrow() {\n  return (\n    <BasePopover.Arrow\n      className={cx(\n        \"flex\",\n        \"data-[side=bottom]:-top-[9px]\",\n        \"data-[side=top]:-bottom-[9px] data-[side=top]:rotate-180\",\n        \"data-[side=left]:-right-[12.5px] data-[side=left]:rotate-90\",\n        \"data-[side=right]:-left-[12.5px] data-[side=right]:-rotate-90\",\n      )}\n    >\n      <svg aria-hidden=\"true\" width=\"16\" height=\"9\" viewBox=\"0 0 16 9\" fill=\"none\">\n        <path d=\"M0 9.5L8 1.5L16 9.5Z\" className=\"fill-white dark:fill-black\" />\n        <path d=\"M0 9L8 1L16 9\" strokeWidth=\"1\" className=\"stroke-zinc-950/10 dark:stroke-white/15\" />\n      </svg>\n    </BasePopover.Arrow>\n  );\n}\n\n/** Optional heading. Labels the popover with aria-labelledby. */\nexport function PopoverTitle({ className, ...props }: BasePopover.Title.Props) {\n  return <BasePopover.Title className={withBase(\"text-sm leading-5 font-semibold\", className)} {...props} />;\n}\n\n/** Optional supporting text. Describes the popover with aria-describedby. */\nexport function PopoverDescription({ className, ...props }: BasePopover.Description.Props) {\n  return <BasePopover.Description className={withBase(\"text-sm leading-5 text-zinc-600 dark:text-zinc-400\", className)} {...props} />;\n}\n"
    }
  ],
  "example": {
    "content": "import { Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from \"@/components/seamless/ui/popover\";\n\n// Same recipe as the Button component's secondary variant, inlined so this example has no other dependency.\nconst button =\n  \"inline-flex h-9 items-center justify-center gap-2 rounded-lg border border-zinc-950/12 bg-white px-3.5 text-sm font-medium text-zinc-950 select-none \" +\n  \"transition-[background-color,border-color,scale] duration-150 ease-out active:duration-100 motion-safe:active:scale-[0.97] motion-reduce:transition-none \" +\n  \"hover:border-zinc-950/20 hover:bg-zinc-950/[0.03] data-[popup-open]:bg-zinc-950/[0.04] \" +\n  \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500 \" +\n  \"dark:border-white/15 dark:bg-black dark:text-white dark:hover:border-white/25 dark:hover:bg-white/[0.06] dark:data-[popup-open]:bg-white/[0.06]\";\n\nexport default function PopoverExample() {\n  return (\n    <Popover>\n      <PopoverTrigger className={button}>Share</PopoverTrigger>\n      <PopoverContent arrow className=\"grid w-80 gap-3\">\n        <div className=\"grid gap-0.5\">\n          <PopoverTitle>Share this page</PopoverTitle>\n          <PopoverDescription>Anyone with the link can view it.</PopoverDescription>\n        </div>\n        <input\n          readOnly\n          aria-label=\"Page link\"\n          value=\"https://acme.dev/docs/limits\"\n          className={\n            \"h-8 rounded-lg border border-zinc-950/[0.07] bg-zinc-950/[0.02] px-2.5 font-mono text-xs text-zinc-700 \" +\n            \"focus-visible:border-sky-500 focus-visible:ring-3 focus-visible:ring-sky-500/20 focus-visible:outline-none \" +\n            \"dark:border-white/[0.08] dark:bg-transparent dark:text-zinc-300\"\n          }\n        />\n        <PopoverClose className={`${button} h-8 justify-self-end px-3`}>Done</PopoverClose>\n      </PopoverContent>\n    </Popover>\n  );\n}\n"
  }
}
