{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "dependencies": [],
  "description": "Anchored tooltip: CSS anchor positioning + [popover=manual] top layer (not popover=hint — not cross-browser). Thin JS opens on hover/focus and closes on leave/blur/Escape. role=tooltip + aria-describedby. WCAG 1.4.13 dismissible, hoverable (opt out with hoverable={false}) and persistent. Physical sides stay physical in RTL; logical sides are asked for by name. No light-dismiss (manual), no portals, no JS positioning.",
  "files": [
    {
      "content": "\"use client\";\n\n/**\n * heidi-ui Tooltip — Slice G first expansion primitive\n * (docs/HEIDI-UI-HEADLESS.md § P4). Platform shell matches Popover\n * (CSS anchors + top layer) but uses [popover=manual] — not popover=hint\n * (not cross-browser). Open/close is thin JS: hover/focus open with delay,\n * leave/blur/Escape close. role=tooltip + aria-describedby when open.\n *\n * WCAG 2.2 SC 1.4.13 (Content on Hover or Focus) is satisfied on all three\n * counts: Dismissible — Escape closes without moving pointer or focus, via\n * the layered escape stack; Hoverable — the tip keeps pointer events and the\n * trigger's leave grace covers the sideOffset gap (`hoverable={false}` opts\n * out); Persistent — nothing auto-hides the tip, only leave/blur/Escape do.\n *\n * Controlled/uncontrolled: `open` + `onOpenChange`, or `defaultOpen`.\n * Composition: className / style / render / ref via renderHeidiElement.\n *\n * RSC rule: named exports from Server Components; Tooltip.X is client sugar.\n */\n\nimport {\n  type ComponentPropsWithoutRef,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ReactNode,\n  type Ref\n} from \"react\";\nimport { addToOpenStack, isTopmost } from \"../_internal/escape-layer\";\nimport {\n  composeHeidiEventHandlers,\n  mergeHeidiRefs,\n  renderHeidiElement,\n  type HeidiIntrinsicHostProps\n} from \"../_internal/render-element\";\nimport { safeId } from \"../_internal/safe-id\";\nimport { useHeidiLayoutEffect } from \"../_internal/use-layout-effect\";\nimport { type TooltipAlign, type TooltipSide } from \"./tooltip.anatomy.generated\";\nimport { TOOLTIP_CLASSES } from \"./tooltip.classes.generated\";\n\nexport type { TooltipAlign, TooltipSide };\n\ntype TooltipContextValue = {\n  anchorName: string;\n  cancelClose: () => void;\n  close: () => void;\n  contentId: string;\n  contentRef: { current: HTMLDivElement | null };\n  hoverable: boolean;\n  open: boolean;\n  scheduleClose: () => void;\n  schedulePointerClose: () => void;\n  scheduleOpen: () => void;\n};\n\nconst TooltipContext = createContext<TooltipContextValue | null>(null);\nconst POINTER_BRIDGE_GRACE_MS = 120;\n\n/**\n * `data-open` / `data-closed` / `data-state` — the styling contract Base UI\n * and Radix consumers write against, on the trigger as well as the content\n * (`:popover-open` is not addressable from the trigger).\n */\nfunction tooltipStateAttributes(\n  open: boolean\n): Record<string, string | true | undefined> {\n  return open\n    ? { \"data-closed\": undefined, \"data-open\": true, \"data-state\": \"open\" }\n    : { \"data-closed\": true, \"data-open\": undefined, \"data-state\": \"closed\" };\n}\n\ntype TooltipPhysicalSide = Exclude<TooltipSide, \"inline-end\" | \"inline-start\">;\n\n/**\n * ponytail: `side` is PHYSICAL — `left` stays the physical left under\n * `dir=\"rtl\"`, matching Radix and heidi-ui's own Menu/HoverCard. The rejected\n * alternative was leaving the CSS on `position-area: inline-start/inline-end`,\n * which mirrors a physically-named side because CSS Anchor Positioning L1\n * §position-area resolves those keywords against writing mode + direction.\n * Consumers who want mirroring ask for it by name. Duplicated from\n * hover-card.tsx / popover.tsx because `_internal/` is a shared registry\n * surface pinned by contract 20c; a shared `_internal/physical-side.ts` would\n * collapse four copies in a follow-up slice.\n */\nfunction resolveTooltipPhysicalSide(\n  side: TooltipSide,\n  direction: \"ltr\" | \"rtl\"\n): TooltipPhysicalSide {\n  if (side === \"inline-start\") {\n    return direction === \"rtl\" ? \"right\" : \"left\";\n  }\n  if (side === \"inline-end\") {\n    return direction === \"rtl\" ? \"left\" : \"right\";\n  }\n  return side;\n}\n\nfunction useTooltipContext(part: string): TooltipContextValue {\n  const context = useContext(TooltipContext);\n  if (!context) {\n    throw new Error(`Tooltip.${part} must be rendered inside Tooltip.Root.`);\n  }\n  return context;\n}\n\nexport type TooltipRootProps = {\n  children?: ReactNode;\n  /** ms before close after leave/blur (default 0). */\n  closeDelay?: number;\n  defaultOpen?: boolean;\n  /**\n   * Whether the pointer can rest on the tooltip itself without dismissing it\n   * (WCAG 2.2 SC 1.4.13 Hoverable). Default true — the inverse of Radix's\n   * `disableHoverableContent` and Base UI's `disableHoverablePopup`, which\n   * also default to hoverable.\n   */\n  hoverable?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  open?: boolean;\n  /** ms before open after hover/focus (default 400). */\n  openDelay?: number;\n};\n\nexport function TooltipRoot({\n  children,\n  closeDelay = 0,\n  defaultOpen = false,\n  hoverable = true,\n  onOpenChange,\n  open: openProp,\n  openDelay = 400\n}: TooltipRootProps) {\n  const id = useId();\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const controlled = openProp !== undefined;\n  const open = openProp ?? internalOpen;\n  const openRef = useRef(open);\n  openRef.current = open;\n  const contentRef = useRef<HTMLDivElement | null>(null);\n  const openTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  const clearTimers = useCallback(() => {\n    if (openTimer.current !== null) {\n      clearTimeout(openTimer.current);\n      openTimer.current = null;\n    }\n    if (closeTimer.current !== null) {\n      clearTimeout(closeTimer.current);\n      closeTimer.current = null;\n    }\n  }, []);\n\n  const cancelClose = useCallback(() => {\n    if (closeTimer.current !== null) {\n      clearTimeout(closeTimer.current);\n      closeTimer.current = null;\n    }\n  }, []);\n\n  const setOpen = useCallback(\n    (next: boolean) => {\n      if (next === openRef.current) {\n        return;\n      }\n      if (!controlled) {\n        openRef.current = next;\n        setInternalOpen(next);\n      }\n      onOpenChange?.(next);\n    },\n    [controlled, onOpenChange]\n  );\n\n  const close = useCallback(() => {\n    clearTimers();\n    setOpen(false);\n  }, [clearTimers, setOpen]);\n\n  const scheduleOpen = useCallback(() => {\n    clearTimers();\n    if (!Number.isFinite(openDelay) || openDelay <= 0) {\n      setOpen(true);\n      return;\n    }\n    openTimer.current = setTimeout(() => {\n      openTimer.current = null;\n      setOpen(true);\n    }, openDelay);\n  }, [clearTimers, openDelay, setOpen]);\n\n  const scheduleClose = useCallback(() => {\n    clearTimers();\n    if (!Number.isFinite(closeDelay) || closeDelay <= 0) {\n      setOpen(false);\n      return;\n    }\n    closeTimer.current = setTimeout(() => {\n      closeTimer.current = null;\n      setOpen(false);\n    }, closeDelay);\n  }, [clearTimers, closeDelay, setOpen]);\n\n  // A zero consumer close delay must not make the tooltip disappear in the\n  // physical trigger/content gap (the sideOffset margin) while the pointer is\n  // travelling onto it — that gap is what WCAG 1.4.13 Hoverable is about.\n  // This small transport grace is only for pointer leave; blur and explicit\n  // close retain the authored closeDelay semantics.\n  const schedulePointerClose = useCallback(() => {\n    clearTimers();\n    closeTimer.current = setTimeout(\n      () => {\n        closeTimer.current = null;\n        setOpen(false);\n      },\n      Math.max(closeDelay, POINTER_BRIDGE_GRACE_MS)\n    );\n  }, [clearTimers, closeDelay, setOpen]);\n\n  useEffect(() => () => clearTimers(), [clearTimers]);\n\n  // Sync native popover API with React open state (manual — no invoker).\n  useEffect(() => {\n    const el = contentRef.current;\n    if (!el || typeof el.showPopover !== \"function\") {\n      return;\n    }\n    const isOpen = el.matches(\":popover-open\");\n    if (open && !isOpen) {\n      try {\n        el.showPopover();\n      } catch {\n        // Already open / not connected — ignore.\n      }\n    } else if (!open && isOpen) {\n      try {\n        el.hidePopover();\n      } catch {\n        // Already closed — ignore.\n      }\n    }\n  }, [open]);\n\n  // Escape closes even though popover=manual does not light-dismiss. Layered:\n  // only the topmost transient surface consumes, and consuming cancels the\n  // keydown so the dialog/popover underneath survives the same keystroke.\n  useEffect(() => {\n    if (!open) {\n      return;\n    }\n    const removeFromStack = addToOpenStack(id, () => contentRef.current);\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (event.defaultPrevented || event.key !== \"Escape\" || !isTopmost(id)) {\n        return;\n      }\n      close();\n      event.preventDefault();\n      event.stopPropagation();\n    };\n    document.addEventListener(\"keydown\", onKeyDown);\n    return () => {\n      removeFromStack();\n      document.removeEventListener(\"keydown\", onKeyDown);\n    };\n  }, [close, id, open]);\n\n  const value = useMemo(() => {\n    const safe = safeId(id);\n    return {\n      anchorName: `--hui-tooltip-anchor-${safe}`,\n      cancelClose,\n      close,\n      contentId: `hui-tooltip-${safe}`,\n      contentRef,\n      hoverable,\n      open,\n      scheduleClose,\n      schedulePointerClose,\n      scheduleOpen\n    };\n  }, [\n    cancelClose,\n    close,\n    hoverable,\n    id,\n    open,\n    scheduleClose,\n    scheduleOpen,\n    schedulePointerClose\n  ]);\n\n  return <TooltipContext value={value}>{children}</TooltipContext>;\n}\n\nexport type TooltipTriggerState = { open: boolean };\n\ntype TooltipTriggerNativeProps = Omit<\n  ComponentPropsWithoutRef<\"button\">,\n  | \"aria-describedby\"\n  | \"children\"\n  | \"className\"\n  | \"onBlur\"\n  | \"onFocus\"\n  | \"onPointerEnter\"\n  | \"onPointerLeave\"\n  | \"ref\"\n  | \"style\"\n  | \"type\"\n>;\n\nexport type TooltipTriggerProps = HeidiIntrinsicHostProps<\n  TooltipTriggerState,\n  \"button\"\n> &\n  TooltipTriggerNativeProps & {\n    children?: ReactNode;\n    onBlur?: ComponentPropsWithoutRef<\"button\">[\"onBlur\"];\n    onFocus?: ComponentPropsWithoutRef<\"button\">[\"onFocus\"];\n    onPointerEnter?: ComponentPropsWithoutRef<\"button\">[\"onPointerEnter\"];\n    onPointerLeave?: ComponentPropsWithoutRef<\"button\">[\"onPointerLeave\"];\n    ref?: Ref<HTMLButtonElement>;\n  };\n\nexport function TooltipTrigger({\n  children,\n  className,\n  onBlur,\n  onFocus,\n  onPointerEnter,\n  onPointerLeave,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: TooltipTriggerProps) {\n  const {\n    anchorName,\n    contentId,\n    open,\n    scheduleClose,\n    scheduleOpen,\n    schedulePointerClose\n  } = useTooltipContext(\"Trigger\");\n  return renderHeidiElement({\n    className: TOOLTIP_CLASSES.trigger,\n    dataPart: \"tooltip-trigger\",\n    element: \"button\",\n    props: {\n      ...nativeProps,\n      \"aria-describedby\": open ? contentId : undefined,\n      children,\n      ...tooltipStateAttributes(open),\n      onBlur: composeHeidiEventHandlers(onBlur, scheduleClose),\n      onFocus: composeHeidiEventHandlers(onFocus, scheduleOpen),\n      onPointerEnter: composeHeidiEventHandlers(\n        onPointerEnter,\n        scheduleOpen\n      ),\n      onPointerLeave: composeHeidiEventHandlers(\n        onPointerLeave,\n        schedulePointerClose\n      ),\n      ref,\n      type: \"button\"\n    },\n    renderProps: { className, render, style },\n    state: { open },\n    structuralStyle: { anchorName } as CSSProperties\n  });\n}\n\nexport type TooltipContentState = {\n  align: TooltipAlign;\n  open: boolean;\n  side: TooltipSide;\n};\n\ntype TooltipContentNativeProps = Omit<\n  ComponentPropsWithoutRef<\"div\">,\n  | \"children\"\n  | \"className\"\n  | \"id\"\n  | \"onPointerEnter\"\n  | \"onPointerLeave\"\n  | \"popover\"\n  | \"ref\"\n  | \"role\"\n  | \"style\"\n>;\n\nexport type TooltipContentProps = HeidiIntrinsicHostProps<\n  TooltipContentState,\n  \"div\"\n> &\n  TooltipContentNativeProps & {\n    align?: TooltipAlign;\n    /** Cross-axis offset in CSS pixels. */\n    alignOffset?: number;\n    children?: ReactNode;\n    onPointerEnter?: ComponentPropsWithoutRef<\"div\">[\"onPointerEnter\"];\n    onPointerLeave?: ComponentPropsWithoutRef<\"div\">[\"onPointerLeave\"];\n    ref?: Ref<HTMLDivElement>;\n    side?: TooltipSide;\n    /** Main-axis gap in CSS pixels. */\n    sideOffset?: number;\n  };\n\nexport function TooltipContent({\n  align = \"center\",\n  alignOffset = 0,\n  children,\n  className,\n  onPointerEnter,\n  onPointerLeave,\n  ref,\n  render,\n  side = \"top\",\n  sideOffset = 4,\n  style,\n  ...nativeProps\n}: TooltipContentProps) {\n  const {\n    anchorName,\n    cancelClose,\n    contentId,\n    contentRef,\n    hoverable,\n    open,\n    scheduleClose\n  } = useTooltipContext(\"Content\");\n  const [direction, setDirection] = useState<\"ltr\" | \"rtl\">(\"ltr\");\n\n  // Physical side resolution needs the COMPUTED direction, which only exists\n  // on the client. The tip stays in the authored DOM (no portals), so it\n  // inherits `direction` from the consumer's dir=\"rtl\" subtree directly.\n  useHeidiLayoutEffect(() => {\n    const element = contentRef.current;\n    if (!element) {\n      return;\n    }\n    const next = getComputedStyle(element).direction === \"rtl\" ? \"rtl\" : \"ltr\";\n    setDirection((current) => (current === next ? current : next));\n  }, [contentRef, open]);\n\n  return renderHeidiElement({\n    className: TOOLTIP_CLASSES.content,\n    dataPart: \"tooltip-content\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      children,\n      \"data-align\": align,\n      \"data-hoverable\": hoverable ? undefined : \"false\",\n      \"data-position-side\": resolveTooltipPhysicalSide(side, direction),\n      \"data-side\": side,\n      ...tooltipStateAttributes(open),\n      id: contentId,\n      // WCAG 2.2 SC 1.4.13 Hoverable: the pointer must be able to rest on the\n      // tip. The trigger's pointer-leave uses a transport grace\n      // (POINTER_BRIDGE_GRACE_MS) to cross the sideOffset gap; arriving here\n      // cancels that pending close.\n      onPointerEnter: composeHeidiEventHandlers(\n        onPointerEnter,\n        cancelClose\n      ),\n      onPointerLeave: composeHeidiEventHandlers(\n        onPointerLeave,\n        scheduleClose\n      ),\n      popover: \"manual\",\n      ref: mergeHeidiRefs(contentRef, ref),\n      role: \"tooltip\"\n    },\n    renderProps: { className, render, style },\n    state: { align, open, side },\n    structuralStyle: {\n      \"--_hui-tooltip-align-offset\": `${alignOffset}px`,\n      \"--_hui-tooltip-side-offset\": `${Math.max(0, sideOffset)}px`,\n      positionAnchor: anchorName\n    } as CSSProperties\n  });\n}\n\nexport const Tooltip = {\n  Content: TooltipContent,\n  Root: TooltipRoot,\n  Trigger: TooltipTrigger\n} as const;\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.tsx",
      "target": "components/ui/heidi/tooltip/tooltip.tsx",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui tooltip — STRUCTURAL CSS only (platform behavior).\n * Same anchor/position-area contract as popover. No --hui-* / --heidi-*.\n */\n\n@layer heidi-ui-base {\n  .hui-tooltip-content:is(\n    [data-side=\"bottom\"],\n    [data-side=\"inline-end\"],\n    [data-side=\"inline-start\"],\n    [data-side=\"left\"],\n    [data-side=\"right\"],\n    [data-side=\"top\"]\n  ) {\n    --_hui-tooltip-align-offset: 0px;\n    --_hui-tooltip-side-offset: 4px;\n    --_hui-tooltip-viewport-gutter: 1rem;\n\n    box-sizing: border-box;\n    inset: auto;\n    /* main-axis gap between anchor and tip; sideOffset writes this var */\n    margin: var(--_hui-tooltip-side-offset);\n    max-block-size: calc(\n      100dvb - var(--_hui-tooltip-viewport-gutter) - 2 *\n        var(--_hui-tooltip-side-offset)\n    );\n    max-inline-size: calc(\n      100dvi - var(--_hui-tooltip-viewport-gutter) - 2 *\n        var(--_hui-tooltip-side-offset)\n    );\n    overflow: auto;\n    overscroll-behavior: contain;\n    /* WCAG 2.2 SC 1.4.13 Hoverable: the pointer must be able to move onto the\n       tip without dismissing it, so pointer events stay ON by default. The\n       gap above is crossed by the trigger's pointer-leave grace in\n       tooltip.tsx. Opt out with <Tooltip.Root hoverable={false}>. */\n    pointer-events: auto;\n    position: fixed;\n    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;\n    scrollbar-gutter: stable;\n    transition-behavior: allow-discrete;\n    transition-property: display, overlay;\n  }\n\n  .hui-tooltip-content[data-hoverable=\"false\"] {\n    pointer-events: none;\n  }\n\n  .hui-tooltip-content:not(:popover-open) {\n    display: none !important;\n  }\n\n  .hui-tooltip-content[data-position-side=\"top\"],\n  .hui-tooltip-content[data-position-side=\"bottom\"] {\n    translate: var(--_hui-tooltip-align-offset) 0;\n  }\n\n  .hui-tooltip-content[data-position-side=\"left\"],\n  .hui-tooltip-content[data-position-side=\"right\"] {\n    translate: 0 var(--_hui-tooltip-align-offset);\n  }\n\n  /* Keyed on the direction-resolved PHYSICAL side — see the same table in\n     popover.base.css. side=\"left\" is the physical left in RTL too; ask for\n     mirroring by name with side=\"inline-start\" / \"inline-end\". */\n  .hui-tooltip-content[data-position-side=\"top\"][data-align=\"start\"] {\n    position-area: block-start span-inline-end;\n  }\n  .hui-tooltip-content[data-position-side=\"top\"][data-align=\"center\"] {\n    position-area: block-start;\n  }\n  .hui-tooltip-content[data-position-side=\"top\"][data-align=\"end\"] {\n    position-area: block-start span-inline-start;\n  }\n  .hui-tooltip-content[data-position-side=\"bottom\"][data-align=\"start\"] {\n    position-area: block-end span-inline-end;\n  }\n  .hui-tooltip-content[data-position-side=\"bottom\"][data-align=\"center\"] {\n    position-area: block-end;\n  }\n  .hui-tooltip-content[data-position-side=\"bottom\"][data-align=\"end\"] {\n    position-area: block-end span-inline-start;\n  }\n  .hui-tooltip-content[data-position-side=\"left\"][data-align=\"start\"] {\n    position-area: left span-block-end;\n  }\n  .hui-tooltip-content[data-position-side=\"left\"][data-align=\"center\"] {\n    position-area: left;\n  }\n  .hui-tooltip-content[data-position-side=\"left\"][data-align=\"end\"] {\n    position-area: left span-block-start;\n  }\n  .hui-tooltip-content[data-position-side=\"right\"][data-align=\"start\"] {\n    position-area: right span-block-end;\n  }\n  .hui-tooltip-content[data-position-side=\"right\"][data-align=\"center\"] {\n    position-area: right;\n  }\n  .hui-tooltip-content[data-position-side=\"right\"][data-align=\"end\"] {\n    position-area: right span-block-start;\n  }\n}\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.base.css",
      "target": "components/ui/heidi/tooltip/tooltip.base.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui tooltip — VISUAL theme (opt-in). Consumes --hui-* semantic theme vars.\n */\n\n@layer heidi-ui {\n  .hui-tooltip-trigger {\n    background: var(--hui-color-bg-elevated);\n    border: var(--hui-border-width) var(--hui-border-style) var(--hui-color-border-default);\n    border-radius: var(--hui-radius-md);\n    color: var(--hui-color-fg-default);\n    cursor: default;\n    font: inherit;\n    padding: var(--hui-space-1-5) var(--hui-space-3);\n  }\n\n  .hui-tooltip-trigger:focus-visible {\n    outline: var(--hui-focus-ring-width) solid var(--hui-color-focus-ring);\n    outline-offset: var(--hui-focus-ring-offset);\n  }\n\n  /* Neither `margin` nor `pointer-events` here on purpose. The gap is the\n     structural sideOffset (--_hui-tooltip-side-offset, default 4px =\n     space-1); pointer-events is the WCAG 1.4.13 Hoverable contract owned by\n     tooltip.base.css and the `hoverable` root prop. A theme declaration of\n     either sits in a later layer and would silently defeat both. */\n  .hui-tooltip-content {\n    background: var(--hui-color-fg-strong);\n    border-radius: var(--hui-radius-sm);\n    color: var(--hui-color-bg-elevated);\n    font-size: var(--hui-text-body-size);\n    max-width: 16rem;\n    opacity: 0;\n    padding: var(--hui-space-1) var(--hui-space-2);\n    transform: scale(0.97);\n    transition-duration: var(--hui-duration-fast);\n    transition-property: display, opacity, overlay, transform;\n    transition-timing-function: var(--hui-ease-out);\n  }\n\n  .hui-tooltip-content[data-state=\"open\"] {\n    opacity: 1;\n    transform: none;\n  }\n\n  .hui-tooltip-content[data-state=\"closed\"] {\n    opacity: 0;\n    transform: scale(0.97);\n  }\n\n  @starting-style {\n    .hui-tooltip-content:popover-open[data-state=\"open\"] {\n      opacity: 0;\n      transform: scale(0.97);\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .hui-tooltip-content {\n      transition-duration: 0s;\n    }\n  }\n\n  @media (forced-colors: active) {\n    .hui-tooltip-trigger {\n      border-color: CanvasText;\n    }\n\n    .hui-tooltip-content {\n      background: Canvas;\n      border: var(--hui-border-width) solid CanvasText;\n      color: CanvasText;\n      forced-color-adjust: none;\n    }\n\n    .hui-tooltip-trigger:focus-visible {\n      outline-color: Highlight;\n    }\n  }\n}\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.theme.css",
      "target": "components/ui/heidi/tooltip/tooltip.theme.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui tooltip — aggregator (base + Heidi adapter + theme).\n * Headless: import tooltip.base.css only.\n * Themed: import this file (or heidi-ui/styles.css).\n */\n\n@import \"./tooltip.base.css\";\n@import \"../../../../styles/heidi-ui-adapter.css\";\n@import \"./tooltip.theme.css\";\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.css",
      "target": "components/ui/heidi/tooltip/tooltip.css",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from packages/heidi-ui/src/tooltip/tooltip.base.css + packages/heidi-ui/src/tooltip/tooltip.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\nexport const TOOLTIP_CLASSES = {\n  content: \"hui-tooltip-content\",\n  trigger: \"hui-tooltip-trigger\",\n} as const;\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.classes.generated.ts",
      "target": "components/ui/heidi/tooltip/tooltip.classes.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from tooltip.anatomy.json + tooltip.base.css + tooltip.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\n\nexport type TooltipAlign = \"center\" | \"end\" | \"start\";\nexport type TooltipHoverable = \"false\";\nexport type TooltipPositionSide = \"bottom\" | \"left\" | \"right\" | \"top\";\nexport type TooltipSide = \"bottom\" | \"inline-end\" | \"inline-start\" | \"left\" | \"right\" | \"top\";\nexport type TooltipState = \"closed\" | \"open\";\n\nexport const TOOLTIP_ANATOMY = {\n  \"component\": \"tooltip\",\n  \"description\": \"Anchored tooltip: CSS anchor positioning + [popover=manual] top layer (not popover=hint — not cross-browser). Thin JS opens on hover/focus and closes on leave/blur/Escape. role=tooltip + aria-describedby. WCAG 1.4.13 dismissible, hoverable (opt out with hoverable={false}) and persistent. Physical sides stay physical in RTL; logical sides are asked for by name. No light-dismiss (manual), no portals, no JS positioning.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"content\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": \"tooltip\"\n      },\n      \"class\": \"hui-tooltip-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"pointer-events\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"tooltip-content\",\n      \"description\": \"The [popover=manual] panel with role=tooltip; anchored to the trigger, hoverable by default, side/align/open state via data attributes.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"align\",\n        \"alignOffset\",\n        \"className\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"ref\",\n        \"render\",\n        \"side\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-align\": [\n          \"center\",\n          \"end\",\n          \"start\"\n        ],\n        \"data-hoverable\": [\n          \"false\"\n        ],\n        \"data-position-side\": [\n          \"bottom\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-side\": [\n          \"bottom\",\n          \"inline-end\",\n          \"inline-start\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-describedby\"\n        ],\n        \"role\": null\n      },\n      \"class\": \"hui-tooltip-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"tooltip-trigger\",\n      \"description\": \"Hover/focus target; carries the per-instance anchor-name, aria-describedby when open, and the open-state styling contract.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"onBlur\",\n        \"onFocus\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    }\n  },\n  \"rootProps\": [\n    \"closeDelay\",\n    \"defaultOpen\",\n    \"hoverable\",\n    \"onOpenChange\",\n    \"open\",\n    \"openDelay\"\n  ],\n  \"tokens\": [\n    \"--hui-border-style\",\n    \"--hui-border-width\",\n    \"--hui-color-bg-elevated\",\n    \"--hui-color-border-default\",\n    \"--hui-color-fg-default\",\n    \"--hui-color-fg-strong\",\n    \"--hui-color-focus-ring\",\n    \"--hui-duration-fast\",\n    \"--hui-ease-out\",\n    \"--hui-focus-ring-offset\",\n    \"--hui-focus-ring-width\",\n    \"--hui-radius-md\",\n    \"--hui-radius-sm\",\n    \"--hui-space-1\",\n    \"--hui-space-1-5\",\n    \"--hui-space-2\",\n    \"--hui-space-3\",\n    \"--hui-text-body-size\"\n  ]\n} as const;\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.anatomy.generated.ts",
      "target": "components/ui/heidi/tooltip/tooltip.anatomy.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "{\n  \"component\": \"tooltip\",\n  \"description\": \"Anchored tooltip: CSS anchor positioning + [popover=manual] top layer (not popover=hint — not cross-browser). Thin JS opens on hover/focus and closes on leave/blur/Escape. role=tooltip + aria-describedby. WCAG 1.4.13 dismissible, hoverable (opt out with hoverable={false}) and persistent. Physical sides stay physical in RTL; logical sides are asked for by name. No light-dismiss (manual), no portals, no JS positioning.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"content\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": \"tooltip\"\n      },\n      \"class\": \"hui-tooltip-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"pointer-events\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"tooltip-content\",\n      \"description\": \"The [popover=manual] panel with role=tooltip; anchored to the trigger, hoverable by default, side/align/open state via data attributes.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"align\",\n        \"alignOffset\",\n        \"className\",\n        \"onPointerEnter\",\n        \"onPointerLeave\",\n        \"ref\",\n        \"render\",\n        \"side\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\":popover-open\"],\n      \"states\": {\n        \"data-align\": [\"center\", \"end\", \"start\"],\n        \"data-hoverable\": [\"false\"],\n        \"data-position-side\": [\"bottom\", \"left\", \"right\", \"top\"],\n        \"data-side\": [\n          \"bottom\",\n          \"inline-end\",\n          \"inline-start\",\n          \"left\",\n          \"right\",\n          \"top\"\n        ],\n        \"data-state\": [\"closed\", \"open\"]\n      }\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [\"aria-describedby\"],\n        \"role\": null\n      },\n      \"class\": \"hui-tooltip-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"tooltip-trigger\",\n      \"description\": \"Hover/focus target; carries the per-instance anchor-name, aria-describedby when open, and the open-state styling contract.\",\n      \"element\": \"button\",\n      \"nativeProps\": true,\n      \"props\": [\"className\", \"onBlur\", \"onFocus\", \"onPointerEnter\", \"onPointerLeave\", \"ref\", \"render\", \"style\"],\n      \"pseudoStates\": [\":focus-visible\"],\n      \"states\": {\n        \"data-state\": [\"closed\", \"open\"]\n      }\n    }\n  },\n  \"rootProps\": [\"closeDelay\", \"defaultOpen\", \"hoverable\", \"onOpenChange\", \"open\", \"openDelay\"]\n}\n",
      "path": "packages/heidi-ui/src/tooltip/tooltip.anatomy.json",
      "target": "components/ui/heidi/tooltip/tooltip.anatomy.json",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared Escape layering for transient top-layer surfaces (tooltip, toast,\n * hover-card). Each surface registers while open; only the topmost entry may\n * consume an Escape keydown, and consuming cancels the event\n * (preventDefault + stopPropagation) so one keystroke never also dismisses\n * the layer underneath — native <dialog closedby=\"closerequest\"> hosts only\n * close on an uncancelled Escape keydown.\n */\n\ntype EscapeLayer = {\n  getElement: () => Element | null;\n  id: string;\n};\n\nconst openEscapeLayers: EscapeLayer[] = [];\n\nfunction removeFromStack(id: string): void {\n  const index = openEscapeLayers.findIndex((layer) => layer.id === id);\n  if (index >= 0) {\n    openEscapeLayers.splice(index, 1);\n  }\n}\n\n/**\n * Register an open surface (re-registering moves it to the top). Returns the\n * stack cleanup. `getElement` identifies the surface's top-layer element so\n * `hasEscapeConsumerOutsideStack` can tell stack popovers from foreign ones.\n */\nexport function addToOpenStack(\n  id: string,\n  getElement: () => Element | null = () => null\n): () => void {\n  removeFromStack(id);\n  openEscapeLayers.push({ getElement, id });\n  return () => removeFromStack(id);\n}\n\nexport function isTopmost(id: string): boolean {\n  return openEscapeLayers.at(-1)?.id === id;\n}\n\n/**\n * True when an open popover that does not participate in this stack (menu,\n * select, context-menu, navigation-menu, popover) could consume the same\n * Escape keystroke — an unfocused toast must defer to it.\n * ponytail: open `<dialog>` hosts are deliberately NOT counted. Dialogs close\n * via a native close request only when the Escape keydown goes uncancelled,\n * and a transient layer consuming (cancelling) the keydown is exactly what\n * shields them; counting them would leave an unfocused toast unclosable\n * whenever any modal is open.\n */\nexport function hasEscapeConsumerOutsideStack(): boolean {\n  const stackElements = new Set<Element>();\n  for (const layer of openEscapeLayers) {\n    const element = layer.getElement();\n    if (element !== null) {\n      stackElements.add(element);\n    }\n  }\n  for (const element of document.querySelectorAll(\":popover-open\")) {\n    if (!stackElements.has(element)) {\n      return true;\n    }\n  }\n  return false;\n}\n",
      "path": "packages/heidi-ui/src/_internal/escape-layer.ts",
      "target": "components/ui/heidi/_internal/escape-layer.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared host rendering for heidi-ui parts — Base-UI-shaped composition\n * (docs/HEIDI-UI-HEADLESS.md § 4 / HEIDI-UI.md § 4.5):\n *   - className / style as value or (state) => value\n *   - render as ReactElement or (props, state) => ReactElement\n *   - ref forwarding\n *   - data-hui-part always set (stable unstyled hook)\n *\n * Structural inline styles (e.g. anchorName) are merged last so a consumer\n * style override cannot drop platform wiring.\n */\n\nimport {\n  cloneElement,\n  createElement,\n  isValidElement,\n  type CSSProperties,\n  type ComponentPropsWithRef,\n  type HTMLAttributes,\n  type ReactElement,\n  type ReactNode,\n  type Ref,\n  type RefCallback\n} from \"react\";\n\nexport type HeidiClassName<State> = string | ((state: State) => string | undefined) | undefined;\n\nexport type HeidiStyle<State> =\n  | CSSProperties\n  | ((state: State) => CSSProperties | undefined)\n  | undefined;\n\nexport type HeidiRenderFn<State, Props> = (\n  props: Props,\n  state: State\n) => ReactElement;\n\nexport type HeidiRender<State, Props> =\n  | ReactElement\n  | HeidiRenderFn<State, Props>\n  | undefined;\n\ntype DefaultRenderProps = HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> };\n\nexport type HeidiHostProps<\n  State,\n  RenderProps extends object = DefaultRenderProps\n> = {\n  className?: HeidiClassName<State>;\n  render?: HeidiRender<State, RenderProps>;\n  style?: HeidiStyle<State>;\n};\n\nexport type HeidiIntrinsicHostProps<\n  State,\n  Tag extends keyof HTMLElementTagNameMap\n> = HeidiHostProps<State, ComponentPropsWithRef<Tag>>;\n\nfunction resolveClassName<State>(\n  className: HeidiClassName<State>,\n  state: State\n): string | undefined {\n  return typeof className === \"function\" ? className(state) : className;\n}\n\nfunction resolveStyle<State>(style: HeidiStyle<State>, state: State): CSSProperties | undefined {\n  return typeof style === \"function\" ? style(state) : style;\n}\n\nfunction mergeClassNames(...parts: Array<string | undefined>): string | undefined {\n  const merged = parts.filter(Boolean).join(\" \");\n  return merged.length > 0 ? merged : undefined;\n}\n\n/**\n * Compose a consumer event with library behavior. Consumers run first and may\n * cancel the component behavior with `event.preventDefault()`. Heidi keeps\n * this native-event convention intentionally: it avoids a second branded event\n * API while still making async/controlled actions vetoable.\n */\nexport function composeHeidiEventHandlers<Event extends { defaultPrevented: boolean }>(\n  consumer: ((event: Event) => void) | undefined,\n  library: (event: Event) => void\n): (event: Event) => void {\n  return (event) => {\n    consumer?.(event);\n    if (!event.defaultPrevented) {\n      library(event);\n    }\n  };\n}\n\n/** React 19-safe ref fan-out, including callback-ref cleanup functions. */\nexport function mergeHeidiRefs<Element>(\n  ...inputRefs: Array<Ref<Element> | undefined>\n): Ref<Element> | undefined {\n  const refs = Array.from(\n    new Set(\n      inputRefs.filter(\n        (ref): ref is Exclude<Ref<Element>, null> => ref != null\n      )\n    )\n  );\n  if (refs.length === 0) {\n    return undefined;\n  }\n  if (refs.length === 1) {\n    return refs[0];\n  }\n\n  let cache = mergedRefCache;\n  for (const ref of refs) {\n    const key = ref as object;\n    let child = cache.children.get(key);\n    if (!child) {\n      child = { children: new WeakMap() };\n      cache.children.set(key, child);\n    }\n    cache = child;\n  }\n  if (cache.callback) {\n    return cache.callback as RefCallback<Element>;\n  }\n\n  const callback: RefCallback<Element> = (node) => {\n    const cleanups: Array<() => void> = [];\n    for (const ref of refs) {\n      if (typeof ref === \"function\") {\n        const cleanup = ref(node);\n        if (node !== null) {\n          cleanups.push(typeof cleanup === \"function\" ? cleanup : () => ref(null));\n        }\n      } else if (ref) {\n        ref.current = node;\n        if (node !== null) {\n          cleanups.push(() => {\n            ref.current = null;\n          });\n        }\n      }\n    }\n    return cleanups.length > 0\n      ? () => {\n          for (const cleanup of cleanups) {\n            cleanup();\n          }\n        }\n      : undefined;\n  };\n  cache.callback = callback as RefCallback<unknown>;\n  return callback;\n}\n\ntype MergedRefCache = {\n  callback?: RefCallback<unknown>;\n  children: WeakMap<object, MergedRefCache>;\n};\n\nconst mergedRefCache: MergedRefCache = { children: new WeakMap() };\n\ntype UnknownHandler = (...args: never[]) => unknown;\n\nfunction isEventHandler(key: string, value: unknown): value is UnknownHandler {\n  return /^on[A-Z]/.test(key) && typeof value === \"function\";\n}\n\nfunction defaultPrevented(args: unknown[]): boolean {\n  const event = args[0];\n  return (\n    typeof event === \"object\" &&\n    event !== null &&\n    \"defaultPrevented\" in event &&\n    event.defaultPrevented === true\n  );\n}\n\nfunction composeUnknownHandlers(\n  consumer: UnknownHandler,\n  library: UnknownHandler\n): UnknownHandler {\n  if (consumer === library) {\n    return library;\n  }\n  return ((...args: unknown[]) => {\n    (consumer as (...handlerArgs: unknown[]) => unknown)(...args);\n    if (!defaultPrevented(args)) {\n      (library as (...handlerArgs: unknown[]) => unknown)(...args);\n    }\n  }) as UnknownHandler;\n}\n\n/** Library-owned host props — permissive so button `type`, `data-*`, `popover`, etc. type-check. */\nexport type HeidiElementProps<Tag extends keyof HTMLElementTagNameMap> = Record<\n  string,\n  unknown\n> & {\n  children?: ReactNode;\n  className?: string;\n  ref?: Ref<HTMLElementTagNameMap[Tag]>;\n  style?: CSSProperties;\n};\n\ntype RenderElementParams<\n  State,\n  Tag extends keyof HTMLElementTagNameMap,\n  RenderProps extends object = DefaultRenderProps\n> = {\n  /** Default class from the generated hui-* map (theme/base target). */\n  className?: string;\n  /** Stable machine hook — always emitted. */\n  dataPart: string;\n  /** Intrinsic tag when `render` is omitted. */\n  element: Tag;\n  /** Props the library owns (aria, ids, handlers, popover, …). */\n  props: HeidiElementProps<Tag>;\n  /** Optional consumer composition props. */\n  renderProps?: HeidiHostProps<State, RenderProps>;\n  /** Typed state passed to functional className/style/render. */\n  state: State;\n  /** Structural inline styles that must survive consumer style merges. */\n  structuralStyle?: CSSProperties;\n  /**\n   * Render-element handlers to remove instead of composing. This is reserved\n   * for states such as a focusable disabled composite item where the public\n   * contract requires press handlers to be completely inert.\n   */\n  suppressRenderedHandlers?: readonly string[];\n};\n\n/**\n * Render a heidi-ui host element with optional Base-shaped composition.\n */\nexport function renderHeidiElement<\n  State,\n  Tag extends keyof HTMLElementTagNameMap,\n  RenderProps extends object = DefaultRenderProps\n>(\n  params: RenderElementParams<State, Tag, RenderProps>\n): ReactElement {\n  const {\n    className,\n    dataPart,\n    element,\n    props,\n    renderProps,\n    state,\n    structuralStyle,\n    suppressRenderedHandlers\n  } = params;\n  const consumerClass = resolveClassName(renderProps?.className, state);\n  const consumerStyle = resolveStyle(renderProps?.style, state);\n  const mergedStyle: CSSProperties | undefined =\n    props.style || consumerStyle || structuralStyle\n      ? { ...props.style, ...consumerStyle, ...structuralStyle }\n      : undefined;\n\n  const outProps: HTMLAttributes<HTMLElement> & {\n    \"data-hui-part\": string;\n    ref?: Ref<HTMLElement>;\n  } = {\n    ...(props as HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> }),\n    className: mergeClassNames(consumerClass, className),\n    \"data-hui-part\": dataPart,\n    style: mergedStyle\n  };\n\n  const render = renderProps?.render;\n  if (typeof render === \"function\") {\n    // Post-merge the returned element as well. This deliberately makes the\n    // stable data hook, owned semantics, refs, and internal handlers survive\n    // even when a render function forgets to spread one of the supplied props.\n    return mergeRenderedElement(\n      render(outProps as unknown as RenderProps, state),\n      outProps,\n      dataPart,\n      structuralStyle,\n      suppressRenderedHandlers\n    );\n  }\n  if (isValidElement(render)) {\n    return mergeRenderedElement(\n      render,\n      outProps,\n      dataPart,\n      structuralStyle,\n      suppressRenderedHandlers\n    );\n  }\n\n  return createElement(element, outProps as never, props.children);\n}\n\ntype RenderedProps = Record<string, unknown> & {\n  children?: ReactNode;\n  className?: string;\n  ref?: Ref<HTMLElement>;\n  style?: CSSProperties;\n};\n\nfunction mergeRenderedElement(\n  element: ReactElement,\n  libraryProps: HTMLAttributes<HTMLElement> & {\n    \"data-hui-part\": string;\n    ref?: Ref<HTMLElement>;\n  },\n  dataPart: string,\n  structuralStyle: CSSProperties | undefined,\n  suppressRenderedHandlers: readonly string[] | undefined\n): ReactElement {\n  const rendered = element as ReactElement<RenderedProps>;\n  const renderedProps = rendered.props;\n  const renderedClass =\n    renderedProps.className === libraryProps.className\n      ? undefined\n      : renderedProps.className;\n  const merged: RenderedProps = {\n    ...renderedProps,\n    ...libraryProps,\n    className: mergeClassNames(renderedClass, libraryProps.className),\n    \"data-hui-part\": dataPart,\n    ref: mergeHeidiRefs(libraryProps.ref, renderedProps.ref),\n    style: {\n      ...libraryProps.style,\n      ...renderedProps.style,\n      ...structuralStyle\n    }\n  };\n\n  if (Object.prototype.hasOwnProperty.call(renderedProps, \"children\")) {\n    merged.children = renderedProps.children;\n  }\n\n  const suppressedHandlers = suppressRenderedHandlers\n    ? new Set(suppressRenderedHandlers)\n    : null;\n  for (const key of new Set([...Object.keys(renderedProps), ...Object.keys(libraryProps)])) {\n    const consumer = renderedProps[key];\n    const library = (libraryProps as unknown as Record<string, unknown>)[key];\n    if (suppressedHandlers?.has(key)) {\n      merged[key] = library;\n      continue;\n    }\n    if (isEventHandler(key, consumer) && isEventHandler(key, library)) {\n      merged[key] = composeUnknownHandlers(consumer, library);\n    }\n  }\n\n  return cloneElement(rendered, merged as never);\n}\n",
      "path": "packages/heidi-ui/src/_internal/render-element.ts",
      "target": "components/ui/heidi/_internal/render-element.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared id sanitizer for heidi-ui hosts.\n *\n * React's useId() emits characters that are invalid in HTML id / attribute\n * selectors and in CSS identifiers (`«r0»` in React 19, `:r0:` earlier).\n * heidi-ui interpolates these ids into element ids, aria relationships, and\n * dashed-ident anchor names (`--hui-*-anchor-…`), so everything outside\n * [a-zA-Z0-9_-] is stripped.\n */\n\nexport function safeId(id: string): string {\n  return id.replace(/[^a-zA-Z0-9_-]/g, \"\");\n}\n",
      "path": "packages/heidi-ui/src/_internal/safe-id.ts",
      "target": "components/ui/heidi/_internal/safe-id.ts",
      "type": "registry:ui"
    },
    {
      "content": "import { useEffect, useLayoutEffect } from \"react\";\n\n/**\n * `useLayoutEffect` that does not warn on the server.\n *\n * ponytail: fifteen primitives imported `useLayoutEffect` straight from React.\n * Every one of them carries `\"use client\"`, which is easy to misread as\n * \"client-only\" — in the App Router it means \"hydrated on the client\", and the\n * component is still rendered to HTML on the server first. React logs\n * \"useLayoutEffect does nothing on the server\" for each one, so a consumer\n * doing SSR saw a wall of warnings the app that ships this library never saw,\n * because it renders these routes on the client path.\n *\n * The branch is evaluated ONCE at module scope, not per render, and it is\n * therefore stable across a component's lifetime — swapping which hook is\n * called between renders would violate the rules of hooks. `typeof document`\n * rather than `typeof window`: both work, but `document` is the thing the\n * effect actually needs, and it keeps the check honest in exotic runtimes that\n * define a partial `window`.\n *\n * Rejected: `useInsertionEffect`, which runs earlier but is specified for\n * style injection and is not a general layout hook; and per-file guards, which\n * is what the 15 copies would have become.\n */\nexport const useHeidiLayoutEffect =\n  typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n",
      "path": "packages/heidi-ui/src/_internal/use-layout-effect.ts",
      "target": "components/ui/heidi/_internal/use-layout-effect.ts",
      "type": "registry:ui"
    }
  ],
  "name": "tooltip",
  "registryDependencies": [
    "@heidi-ui/heidi-tokens"
  ],
  "title": "Tooltip",
  "type": "registry:ui"
}
