Components

Tooltip

Expose contextual helper content linked to a trigger through semantic ARIA relationships and

Release Scope: Canonical Implementation: Core Site Visibility: Listed Adapters: React, Vanilla, Solid, Vue, Svelte Category: overlay

1. Live Preview

vanilla live preview activates on the client; source HTML keeps a readable preview island and primitive contract.

vanilla tooltip preview activates on the client.

2. Installation

Install adapter-specific dependencies first, then complete setup via the installation guide. installation docs

pnpm add @nake-ui/primitives @nake-ui/adapter-vanilla @nake-ui/theme-vanilla

3. Usage

Minimal runnable usage for the current adapter.

import "@nake-ui/theme-vanilla/styles.css";
import { bindTooltip } from "@nake-ui/adapter-vanilla";

const binding = bindTooltip(nodes, items, options);
binding.destroy();

4. Examples

Delay

Control tooltip open/close timing with custom delay scheduling.

Hover or focus trigger with open=600ms / close=200ms delay.

import "@nake-ui/theme-vanilla/styles.css";
import { bindTooltip } from "@nake-ui/adapter-vanilla";

const OPEN_DELAY = 600;
const CLOSE_DELAY = 200;

if (typeof window !== "undefined" && typeof document !== "undefined") {
  let open = false;

  const tooltipOptions = {
    id: "vanilla-tooltip-delay-controller",
    placement: "top",
    offset: 10,
    open,
    openOnHover: false,
    openOnFocus: false,
    onOpenChange(nextOpen) {
      open = nextOpen;
      tooltipOptions.open = nextOpen;
    }
  };

  const binding = bindTooltip({ root, trigger, positioner, content, arrow }, tooltipOptions);

  let openTimer = null;
  let closeTimer = null;

  const setOpen = (nextOpen) => {
    if (open === nextOpen) return;
    open = nextOpen;
    tooltipOptions.open = nextOpen;
    binding.sync();
  };

  trigger.addEventListener("pointerenter", () => {
    if (closeTimer) clearTimeout(closeTimer);
    if (openTimer) clearTimeout(openTimer);
    openTimer = setTimeout(() => setOpen(true), OPEN_DELAY);
  });

  trigger.addEventListener("pointerleave", () => {
    if (openTimer) clearTimeout(openTimer);
    if (closeTimer) clearTimeout(closeTimer);
    closeTimer = setTimeout(() => setOpen(false), CLOSE_DELAY);
  });

  // cleanup on unmount
  binding.destroy();
}

trigger.textContent = "Delayed hint";

Controlled Open

Use controlled open state and toggle tooltip from an external button.

Current: closed

Use controlled open when tooltip visibility must sync with other widgets.

import "@nake-ui/theme-vanilla/styles.css";
import { bindTooltip } from "@nake-ui/adapter-vanilla";

if (typeof window !== "undefined" && typeof document !== "undefined") {
  let open = false;

  const tooltipOptions = {
    id: "vanilla-tooltip-controlled-controller",
    placement: "top",
    offset: 10,
    open,
    onOpenChange(nextOpen) {
      open = nextOpen;
      tooltipOptions.open = nextOpen;
      state.textContent = nextOpen ? "open" : "closed";
    }
  };

  const binding = bindTooltip({ root, trigger, positioner, content, arrow }, tooltipOptions);

  toggle.addEventListener("click", () => {
    open = !open;
    tooltipOptions.open = open;
    binding.sync();
    state.textContent = open ? "open" : "closed";
  });

  // cleanup on unmount
  binding.destroy();
}

trigger.textContent = "Controlled tooltip";

Disabled Trigger

Keep trigger visible while preventing tooltip interactions in disabled mode.

Disabled tooltip keeps trigger visible but blocks open interactions.

import "@nake-ui/theme-vanilla/styles.css";
import { bindTooltip } from "@nake-ui/adapter-vanilla";

if (typeof window !== "undefined" && typeof document !== "undefined") {
  const binding = bindTooltip(
    { root, trigger, positioner, content, arrow },
    {
      id: "vanilla-tooltip-disabled-controller",
      placement: "top",
      offset: 10,
      disabled: true
    }
  );

  // cleanup on unmount
  binding.destroy();
}

trigger.textContent = "Disabled action";
trigger.disabled = true;

Placement

Demonstrate tooltip placement matrix across top/right/bottom/left and start/center/end.

Hover a trigger
import "@nake-ui/theme-vanilla/styles.css";
import { bindTooltip } from "@nake-ui/adapter-vanilla";

const topPlacements = ["top-start", "top", "top-end"];
const rightPlacements = ["right-start", "right", "right-end"];
const bottomPlacements = ["bottom-start", "bottom", "bottom-end"];
const leftPlacements = ["left-start", "left", "left-end"];

if (typeof window !== "undefined" && typeof document !== "undefined") {
  const placements = [...topPlacements, ...rightPlacements, ...bottomPlacements, ...leftPlacements];

  for (const placement of placements) {
    const binding = bindTooltip(
      {
        root: roots[placement],
        trigger: triggers[placement],
        positioner: positioners[placement],
        content: contents[placement],
        arrow: arrows[placement]
      },
      { id: "vanilla-tooltip-placement-controller-${placement}", placement, offset: 10 }
    );

    // cleanup on unmount
    binding.destroy();
  }
}

const contentText = placement;

5. RTL

The same structure running inside a `dir="rtl"` container.

vanilla tooltip preview activates on the client.

6. Closure Coverage

Complete

Closure Score: 100% (19/19)

Required Checks

  • schemaRegistered
  • specDocumented
  • reactMapper
  • siteExampleCoverage
  • siteA11yCoverage
  • siteVisible
  • schemaValidatorCoverage
  • schemaAriaCoverage
  • specValidatorRulesSection
  • specTestChecklistSection
  • specSsrHydrationSection
  • reactSsrHydrationCoverage
  • reactRenderLevelSsrCoverage
  • solidSsrCoverage
  • coreImplemented
  • primitiveUnitTest
  • domContractSnapshot
  • vanillaBinding
  • adapterParitySnapshot

All required closure checks are currently satisfied.

7. Purpose / Non-goals

Expose contextual helper content linked to a trigger through semantic ARIA relationships and

  • Rich popover interaction model
  • Visual animation and styling ownership

8. Anatomy & Slot

  • root
  • trigger
  • positioner
  • content
  • arrow (optional)

9. DOM Contract

<div data-ui="tooltip" data-slot="root" data-state="open" data-expanded="true">
  <button
    data-ui="tooltip"
    data-slot="trigger"
    data-state="open"
    data-expanded="true"
    aria-describedby="tooltip-content"
  ></button>
  <div data-ui="tooltip" data-slot="positioner" data-placement="top-start" data-side="top">
    <div
      id="tooltip-content"
      data-ui="tooltip"
      data-slot="content"
      data-placement="top-start"
      data-side="top"
      role="tooltip"
    >
      <span data-ui="tooltip" data-slot="arrow" data-placement="top-start" data-side="top"></span>
    </div>
  </div>
</div>

10. State & Events

State Model

  • open values: open / closed
  • controlled: open + onOpenChange
  • uncontrolled: defaultOpen
  • react and Solid timing controls: openDelay, closeDelay, optional forceMount
  • vanilla interaction toggles: openOnHover, openOnFocus
  • placement controls: placement, offset, viewportPadding

Event Model

  • pointer enter/focus opens tooltip
  • pointer leave/blur closes tooltip
  • outside pointer down closes tooltip
  • Escape closes tooltip from trigger or content path

11. Keyboard & Focus

  • trigger retains focus throughout tooltip visibility
  • tooltip content is not part of tab order by default
  • floating layer reflects resolved side with data-side and placement with data-placement

12. ARIA

  • content uses role=tooltip
  • trigger aria-describedby points to content id
  • trigger should keep accessible name independent from tooltip text

13. Validator Rules

  • structure.missing-required-slot
  • aria.broken-controls-linkage
  • dom.missing-public-data-attr
  • keyboard.unhandled-escape
  • state.invalid-open-state

14. Adapter Notes

Vanilla

  • Primary binder: bindTooltip.
  • Binds explicit DOM nodes and should be cleaned up on unmount.

15. Example Mapping

  • No direct example mapping yet.

16. API

Core

  • createTooltipController value
  • TooltipController type
  • TooltipControllerOptions type

Vanilla

  • bindTooltip function
  • TooltipBinding interface
  • TooltipBindingOptions interface
  • TooltipController type
  • TooltipControllerOptions type
  • TooltipElements interface
  • TooltipPositioningPlacement type