Components
Tooltip
Expose contextual helper content linked to a trigger through semantic ARIA relationships and
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.
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.
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.
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.
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
roottriggerpositionercontentarrow(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, optionalforceMount - 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-sideand placement withdata-placement
12. ARIA
- content uses
role=tooltip - trigger
aria-describedbypoints 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
-
createTooltipControllervalue -
TooltipControllertype -
TooltipControllerOptionstype
Vanilla
-
bindTooltipfunction -
TooltipBindinginterface -
TooltipBindingOptionsinterface -
TooltipControllertype -
TooltipControllerOptionstype -
TooltipElementsinterface -
TooltipPositioningPlacementtype