{
  "name": "Tooltip",
  "type": "component",
  "description": "Hover and focus explanation anchored to a trigger. Portalled and theme-aware.",
  "export": "Tooltip",
  "path": "@gtmi/strix-react/atoms/tooltip",
  "figma": null,
  "figmaAbsent": "Base UI primitive. Figma draws tooltip TRIGGERS on four nodes but no tooltip surface of its own.",
  "props": [
    {
      "name": "title",
      "type": "string",
      "description": "Bold line above the body."
    },
    {
      "name": "text",
      "type": "string",
      "description": "Body text."
    },
    {
      "name": "customContent",
      "type": "ReactNode",
      "description": "Pre-formatted popup content. Overrides title and text."
    },
    {
      "name": "side",
      "type": "top | bottom | left | right",
      "default": "top"
    },
    {
      "name": "align",
      "type": "start | center | end",
      "default": "center",
      "description": "Edge-aligned variants square off the corner nearest the trigger so the two read as one shape."
    },
    {
      "name": "sideOffset",
      "type": "number",
      "default": "6"
    },
    {
      "name": "delayDuration",
      "type": "number",
      "default": "0"
    },
    {
      "name": "defaultOpen",
      "type": "boolean",
      "default": "false"
    },
    {
      "name": "dismissAfter",
      "type": "number",
      "description": "Auto-dismiss after N ms. The timer restarts each time the tooltip opens."
    },
    {
      "name": "disableHoverablePopup",
      "type": "boolean",
      "default": "false",
      "description": "Make the popup unhoverable, for when a tooltip shares a trigger with another hover interaction."
    }
  ],
  "ai_tags": ["tooltip", "hover", "portal", "overlay", "primitive"],
  "since": "0.2.0",
  "tokens_used": [
    "--color-surface-tooltip",
    "--color-text-on-tooltip",
    "--color-text-on-tooltip-muted",
    "--shadow-xs",
    "--radius-md",
    "--spacing-gap-200",
    "--spacing-gap-300",
    "--z-index-tooltip",
    "--animate-popup-in",
    "--animate-popup-out"
  ],
  "notes": "PORTALLED: the popup renders into document.body, outside the element carrying data-theme, so the active theme is stamped onto the popup itself. Without it a tooltip renders in light theme over a dark page. The theme comes from useOptionalTheme(), so a consumer with no ThemeProvider still gets a working tooltip in light. Three tests cover this, including one asserting the popup is NOT inside the provider element - that is the condition the stamp exists for. With no title, text or customContent the trigger renders alone: a tooltip with nothing to say is a hover target that swallows pointer events. Upstream's animate-in/fade-in-0/zoom-in-95 come from the tailwindcss-animate plugin, which neither repo installs, so its tooltips never animated; --animate-popup-in / --animate-popup-out replace them. Upstream's z-tooltip was also dead: Tailwind v4 has no z-index theme namespace, so z-(--z-index-tooltip) is how a token reaches a z utility.\n\nTHE BUBBLE IS FIGMA'S INVERTED WHITE, and this fixed a real defect. It was bg-surface-base -- which after Finding 0's page re-point is THE SAME LITERAL as the page it floats over, so the bubble measured 1.00:1 against its own backdrop with shadow-xs as the only separation. A shadow is dropped entirely in forced-colours mode. Figma's .label atom (22:3960) draws the only tooltip in the file and draws it inverted: a white surface at 19.37:1 against the page, which needs no border at all. Verified from the exported SVG rather than the variable table -- the binding recorded #121c2d as \"the bubble's Tip and Body shapes\" and that value is in no rendered pixel; bubble and tip are one white Union path.\n\nINVERTING A SURFACE INVERTS ITS INK, which is why three roles moved and not one. --color-text-primary is color/brand/white and would have been 1:1 on the white bubble. The title takes --color-text-on-tooltip (color/brand/ink, 19.37:1) and the body --color-text-on-tooltip-muted, a rule-2 gap filled from Figma's own palette with gray-700 at 9.96:1 because .label's bubble draws one line of body text and no second one. --color-text-secondary #c5d0e3 would have been 1.55:1 on white.\n\nTHIS COMPONENT HAD NO CONTRAST TEST AT ALL, which is the reason a 1:1 bubble shipped -- not a wrong number but a missing check. There is one now, and it asserts the PROPERTY rather than the value: the bubble must never resolve to the same colour as the page. A popup is the surface where this matters most, because it has no parent to sit against -- the page is its backdrop. Resolves question 6 in docs/DESIGN_DECISIONS_NEEDED.md, which had framed the inversion as a visual decision; under rule 1 it is not ours to make."
}
