{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tooltip",
  "title": "Tooltip",
  "description": "A tooltip component.",
  "registryDependencies": [
    "@cubby-ui/elevated"
  ],
  "files": [
    {
      "path": "registry/default/tooltip/tooltip.tsx",
      "content": "import * as React from \"react\";\nimport { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  CHROME_SURFACE,\n  solidSurface,\n  type SurfaceLevel,\n} from \"@/registry/default/lib/elevated\";\n\nfunction TooltipProvider({\n  // Base UI's own default is 600ms, which reads as sluggish for a label that\n  // mostly confirms what an icon already implies. Set here rather than on\n  // TooltipTrigger because the trigger's `delay` *wins* over the provider's\n  // (`delay ?? providerDelay ?? 600`), so a default there would make\n  // `<TooltipProvider delay>` impossible to honour.\n  delay = 200,\n  ...props\n}: React.ComponentProps<typeof BaseTooltip.Provider>) {\n  return (\n    <BaseTooltip.Provider\n      data-slot=\"tooltip-provider\"\n      delay={delay}\n      {...props}\n    />\n  );\n}\n\nfunction Tooltip<Payload = unknown>({\n  ...props\n}: BaseTooltip.Root.Props<Payload>) {\n  return <BaseTooltip.Root data-slot=\"tooltip\" {...props} />;\n}\n\nfunction TooltipTrigger({\n  ...props\n}: React.ComponentProps<typeof BaseTooltip.Trigger>) {\n  return <BaseTooltip.Trigger data-slot=\"tooltip-trigger\" {...props} />;\n}\n\nfunction TooltipPortal({\n  ...props\n}: React.ComponentProps<typeof BaseTooltip.Portal>) {\n  return <BaseTooltip.Portal data-slot=\"tooltip-portal\" {...props} />;\n}\n\nfunction TooltipPositioner({\n  ...props\n}: React.ComponentProps<typeof BaseTooltip.Positioner>) {\n  return <BaseTooltip.Positioner data-slot=\"tooltip-positioner\" {...props} />;\n}\n\nfunction TooltipArrow({\n  ...props\n}: React.ComponentProps<typeof BaseTooltip.Arrow>) {\n  return <BaseTooltip.Arrow data-slot=\"tooltip-arrow\" {...props} />;\n}\n\n/**\n * Surface treatment, as a union so the compiler rejects the impossible pair.\n * `chrome` is the near-black fill in both themes, for labels that read as\n * instrument rather than page. It sits off the elevation ladder, so `level` and\n * `shadowLevel` would have nothing to select and are refused rather than\n * silently dropped.\n */\ntype TooltipSurfaceProps =\n  | {\n      variant?: \"default\";\n      /** Surface elevation level for the tooltip bg (1-8). Defaults to 2, the lightest \"lifted off the page\" tier. */\n      level?: SurfaceLevel;\n      /** Shadow weight (1-8). Pinned to 2 by default so tooltips read as quiet/subtle. */\n      shadowLevel?: SurfaceLevel;\n    }\n  | { variant: \"chrome\"; level?: never; shadowLevel?: never };\n\nfunction TooltipContent({\n  children,\n  className,\n  side = \"top\",\n  align = \"center\",\n  sideOffset = 8,\n  alignOffset = 0,\n  collisionBoundary,\n  collisionPadding = 10,\n  sticky = false,\n  positionMethod = \"absolute\",\n  arrow = false,\n  arrowPadding,\n  container,\n  variant = \"default\",\n  level = 2,\n  shadowLevel = 2,\n  ...props\n}: React.ComponentProps<typeof BaseTooltip.Popup> & {\n  side?: BaseTooltip.Positioner.Props[\"side\"];\n  align?: BaseTooltip.Positioner.Props[\"align\"];\n  sideOffset?: BaseTooltip.Positioner.Props[\"sideOffset\"];\n  alignOffset?: BaseTooltip.Positioner.Props[\"alignOffset\"];\n  collisionBoundary?: BaseTooltip.Positioner.Props[\"collisionBoundary\"];\n  collisionPadding?: BaseTooltip.Positioner.Props[\"collisionPadding\"];\n  sticky?: BaseTooltip.Positioner.Props[\"sticky\"];\n  positionMethod?: BaseTooltip.Positioner.Props[\"positionMethod\"];\n  arrow?: boolean;\n  arrowPadding?: number;\n  container?: HTMLElement | undefined;\n} & TooltipSurfaceProps) {\n  const isChrome = variant === \"chrome\";\n  return (\n    <TooltipPortal container={container}>\n      <BaseTooltip.Positioner\n        data-slot=\"tooltip-positioner\"\n        side={side}\n        align={align}\n        sideOffset={sideOffset}\n        alignOffset={alignOffset}\n        collisionBoundary={collisionBoundary}\n        collisionPadding={collisionPadding}\n        sticky={sticky}\n        positionMethod={positionMethod}\n        arrowPadding={arrowPadding}\n        className=\"z-50 h-(--positioner-height) max-h-(--available-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none motion-reduce:transition-none\"\n      >\n        <BaseTooltip.Popup\n          data-slot=\"tooltip-content\"\n          data-variant={variant}\n          // No level to report off the ladder. The popup carries the attribute\n          // itself because portalled content can't inherit the page's.\n          data-level={isChrome ? undefined : level}\n          data-surface={isChrome ? \"chrome\" : undefined}\n          className={cn(\n            // `relative` matches Popover and pins the arrow's containing block.\n            // Without it the popup is a containing block only while `scale` is\n            // mid-animation (a non-none scale creates one) and not at rest, so\n            // an absolutely positioned arrow would change reference frame\n            // partway through the open.\n            \"relative h-(--popup-height,auto) max-h-(--available-height) w-(--popup-width,auto) max-w-(--available-width) origin-(--transform-origin) rounded-sm text-xs outline-none\",\n            // The chrome rule re-points --foreground but deliberately leaves\n            // the popover/card pair alone, so chrome reads the former.\n            isChrome ? \"text-foreground\" : \"text-popover-foreground\",\n            // Fill only — `shadow-(--chrome-shadow)` is the opt-in edge, which a\n            // header bar needs and a two-line label does not.\n            isChrome ? CHROME_SURFACE : solidSurface(level, shadowLevel),\n            // No directional translate: the popup scales from\n            // --transform-origin, which Base UI aims back at the trigger, so\n            // the growth already reads as coming from the anchor. A per-side\n            // nudge on top of that is a second cue for the same thing.\n            \"transition-[width,height,scale,opacity] duration-[150ms,150ms,100ms,100ms] ease-[cubic-bezier(0.22,1,0.36,1),cubic-bezier(0.22,1,0.36,1),var(--ease-out-expo),var(--ease-out-expo)]\",\n            // Own compositor layer while mounted. Without it the 0.95 -> 1\n            // scale re-lays-out and re-rasterises the label every frame, and at\n            // 12px the hinting snaps stems between pixels, which reads as the\n            // text crawling. Promoted, the glyphs raster once and the layer is\n            // scaled instead. Scoped to the popup, which only exists while the\n            // tooltip is shown, so nothing carries the hint at rest.\n            \"will-change-transform\",\n            \"data-starting-style:scale-95 data-starting-style:opacity-0\",\n            \"data-ending-style:scale-95 data-ending-style:opacity-0\",\n            // Only 'delay', the swap inside a Provider group. 'dismiss' and 'focus'\n            // are closes: Base UI waits on this transition before unmounting, so\n            // suppressing them means no exit at all.\n            \"data-[instant=delay]:transition-none\",\n            \"motion-reduce:transition-none motion-reduce:will-change-auto\",\n            className,\n          )}\n          {...props}\n        >\n          <BaseTooltip.Viewport\n            data-slot=\"tooltip-viewport\"\n            className={cn(\n              // Base viewport styles\n              // `h-full` and the cap do different jobs; both are load-bearing. At\n              // rest --popup-height is `auto`, so the percentage is indefinite and\n              // only the cap bounds the viewport, which is what engages the\n              // overflow-y below. Mid-swap it is a definite px that transitions,\n              // and the percentage tracks it. Without that the viewport snaps to\n              // the incoming content's height on frame one, because Base UI takes\n              // the outgoing content out of flow with position:absolute, and\n              // overflow-clip cuts its extra rows instead of letting them fade.\n              // No `max-w`: `w-full` resolves against the popup's already-capped\n              // content box, so the width bound is inherited for free.\n              \"relative h-full max-h-(--available-height) w-full overflow-clip px-2 py-1.5 [--viewport-padding:0.5rem]\",\n              \"not-data-transitioning:overflow-y-auto\",\n              // Content width and transitions\n              \"**:data-current:w-[calc(var(--popup-width)-2*var(--viewport-padding))]\",\n              \"**:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-padding))]\",\n              // Straight crossfade, no scale: tooltip content is a line of\n              // text, and scaling it fights the popup's own scale rather than\n              // reading as a separate layer. The width/height morph carries\n              // the swap.\n              \"**:data-current:opacity-100 **:data-previous:opacity-100\",\n              \"**:data-current:transition-opacity **:data-current:duration-150 **:data-current:ease-[cubic-bezier(0.22,1,0.36,1)]\",\n              \"**:data-previous:transition-opacity **:data-previous:duration-150 **:data-previous:ease-[cubic-bezier(0.22,1,0.36,1)]\",\n              \"**:data-current:data-starting-style:opacity-0\",\n              \"**:data-previous:data-ending-style:opacity-0\",\n              // Truncate outgoing content as popup shrinks.\n              \"**:data-previous:truncate\",\n              // Disable transitions when instant or motion-reduce\n              // Value-matched to the popup's guard above so one policy governs the\n              // subtree. Targets the content: the viewport has no transitions of\n              // its own and transition-property does not inherit.\n              \"[[data-instant=delay]_&_[data-current]]:transition-none [[data-instant=delay]_&_[data-previous]]:transition-none\",\n              \"motion-reduce:**:data-current:transition-none motion-reduce:**:data-previous:transition-none\",\n            )}\n          >\n            {children}\n          </BaseTooltip.Viewport>\n          {arrow && (\n            <TooltipArrow className=\"outline-0 transition-[left,right,top,bottom] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:top-[-9px] data-[side=left]:right-[-13.5px] data-[side=left]:rotate-90 data-[side=right]:left-[-13.5px] data-[side=right]:-rotate-90 data-[side=top]:bottom-[-9px] data-[side=top]:rotate-180 motion-reduce:transition-none\">\n              <svg width=\"20\" height=\"10\" viewBox=\"0 0 20 10\" fill=\"none\">\n                <path\n                  d=\"M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V9H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z\"\n                  className=\"fill-(--popup-surface,var(--card))\"\n                />\n                <path\n                  d=\"M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z\"\n                  className=\"fill-border/80 dark:fill-border/60\"\n                />\n              </svg>\n            </TooltipArrow>\n          )}\n        </BaseTooltip.Popup>\n      </BaseTooltip.Positioner>\n    </TooltipPortal>\n  );\n}\n\nconst createTooltipHandle = BaseTooltip.createHandle;\n\nexport {\n  Tooltip,\n  TooltipTrigger,\n  TooltipContent,\n  TooltipPortal,\n  TooltipPositioner,\n  TooltipArrow,\n  TooltipProvider,\n  createTooltipHandle,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/tooltip.tsx"
    }
  ],
  "type": "registry:ui"
}