@xsolla/xui-tooltip
v0.206.0
Published
A hover- or focus-triggered popover that displays contextual information next to a trigger element. Web-only rendering (uses a portal); on React Native the trigger is rendered without the tooltip surface. <!-- BEGIN:xui-mcp-instructions:tooltip --> Toolti
Readme
Tooltip
A hover- or focus-triggered popover that displays contextual information next to a trigger element. Web-only rendering (uses a portal); on React Native the trigger is rendered without the tooltip surface.
Tooltip is a small popup that shows brief, supplementary information when the user hovers or focuses an element. Its pointer indicates the element it describes, and its position (top, bottom, left, right, and corners) keeps it anchored to that.
When to use
- To clarify an icon-only control or a truncated value on hover or focus
- To give a short hint or extra context without cluttering the interface
- When the information is helpful but not essential to complete the task
When not to use
- For essential information the user must see — place it directly in the UI
- For long or interactive content — use a Popover or Modal instead
- As a replacement for a visible Label
- On controls where hover isn't reliably available without a clear tap pattern
Content guidelines
Keep the text short — one or two lines of plain language.
Describe or clarify; don*'t restate what'*s already obvious on screen.
Match the size (S, M, L, XL) to the surrounding context and keep it consistent.
Behaviour guidelines
Appears on hover or keyboard focus of the trigger and dismisses on mouse-out, blur, or Escape, ideally with a small delay to avoid flicker.
Position — the pointer aims at the trigger; choose the placement that keeps the tooltip fully on screen and let it flip when space is tight.
Show only one tooltip at a time, and don't let it cover the element it describes
Accessibility
The trigger must be focusable so the tooltip appears on keyboard focus, not hover alone.
Associate the tooltip with its trigger via aria-describedby so screen readers announce it.
Make it dismissible with Escape and keep it readable long enough to move the pointer onto it.
Never place essential or interactive content in a tooltip, and ensure the text meets contrast minimums.
Installation
npm install @xsolla/xui-tooltipImports
import {
Tooltip,
type TooltipProps,
type TooltipPlacement,
type TooltipSize,
} from "@xsolla/xui-tooltip";Quick start
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Button } from "@xsolla/xui-button";
export default function Example() {
return (
<Tooltip content="This is a helpful tip">
<Button>Hover me</Button>
</Tooltip>
);
}API Reference
<Tooltip>
| Prop | Type | Default | Description |
| ------------------- | ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------- |
| content | ReactNode | — | Required. Tooltip content. Strings/numbers render as themed text. |
| children | ReactNode | — | Required. Trigger element. |
| size | "sm" \| "md" \| "lg" \| "xl" | "md" | Typography size. |
| placement | TooltipPlacement | "top" | Position relative to trigger. |
| maxWidth | number | — | Maximum tooltip width in pixels before long content wraps. Omitted = no cap (max-width: none). |
| offset | number | 12 | Distance from trigger in pixels. |
| delayEnter | number | 0 | Delay before showing (ms). |
| delayLeave | number | 100 | Delay before hiding (ms). A short grace period keeps the tooltip visible while the cursor moves from the trigger onto the tooltip, preventing flicker. Set to 0 to hide immediately. |
| controlledVisible | boolean | — | Externally controlled visibility. Overrides hover/focus state. |
| style | CSSProperties | — | Custom styles applied to the tooltip surface (web only). |
| className | string | — | Custom class for the trigger wrapper. |
| data-testid | string | — | Test identifier. |
| testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. |
Inherits ThemeOverrideProps (themeMode, themeProductContext).
TooltipPlacement is "top" | "top-left" | "top-right" | "bottom" | "bottom-left" | "bottom-right" | "left" | "right".
The arrow always points at the centre of the trigger. The four corner placements
align the tooltip flush with a trigger edge — -right extends to the right of
the trigger's left edge, -left to the left of its right edge. Triggers narrower
than 34px cannot fit the arrow's 17px inset from that edge, so the tooltip slides
out by the shortfall to keep the arrow on the trigger's centre; icon-sized
triggers stay correctly aimed.
Size typography
| Size | Font size | Line height |
| ---- | --------- | ----------- |
| sm | 14px | 16px |
| md | 16px | 18px |
| lg | 18px | 20px |
| xl | 20px | 22px |
Examples
Placements
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Button } from "@xsolla/xui-button";
export default function Placements() {
return (
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: 16,
padding: 48,
}}
>
<Tooltip content="Top left" placement="top-left">
<Button variant="secondary">Top Left</Button>
</Tooltip>
<Tooltip content="Top centre" placement="top">
<Button variant="secondary">Top</Button>
</Tooltip>
<Tooltip content="Top right" placement="top-right">
<Button variant="secondary">Top Right</Button>
</Tooltip>
<Tooltip content="Left" placement="left">
<Button variant="secondary">Left</Button>
</Tooltip>
<div />
<Tooltip content="Right" placement="right">
<Button variant="secondary">Right</Button>
</Tooltip>
<Tooltip content="Bottom left" placement="bottom-left">
<Button variant="secondary">Bottom Left</Button>
</Tooltip>
<Tooltip content="Bottom centre" placement="bottom">
<Button variant="secondary">Bottom</Button>
</Tooltip>
<Tooltip content="Bottom right" placement="bottom-right">
<Button variant="secondary">Bottom Right</Button>
</Tooltip>
</div>
);
}Sizes
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Button } from "@xsolla/xui-button";
export default function Sizes() {
return (
<div style={{ display: "flex", gap: 16 }}>
<Tooltip content="Small" size="sm">
<Button variant="secondary">Small</Button>
</Tooltip>
<Tooltip content="Medium" size="md">
<Button variant="secondary">Medium</Button>
</Tooltip>
<Tooltip content="Large" size="lg">
<Button variant="secondary">Large</Button>
</Tooltip>
<Tooltip content="Extra large" size="xl">
<Button variant="secondary">XL</Button>
</Tooltip>
</div>
);
}With delay
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Button } from "@xsolla/xui-button";
export default function Delays() {
return (
<div style={{ display: "flex", gap: 16 }}>
<Tooltip content="Appears immediately" delayEnter={0}>
<Button variant="secondary">No delay</Button>
</Tooltip>
<Tooltip content="Appears after 500ms" delayEnter={500}>
<Button variant="secondary">500ms enter</Button>
</Tooltip>
<Tooltip content="Stays for 500ms" delayLeave={500}>
<Button variant="secondary">Slow hide</Button>
</Tooltip>
</div>
);
}Rich content
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Avatar } from "@xsolla/xui-avatar";
export default function Rich() {
return (
<Tooltip
content={
<div style={{ textAlign: "center" }}>
<strong>John Doe</strong>
<br />
<span style={{ opacity: 0.7 }}>Senior Developer</span>
</div>
}
>
<Avatar text="JD" />
</Tooltip>
);
}Icons with tooltip
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Edit, TrashCan, MoreHr } from "@xsolla/xui-icons-base";
export default function Icons() {
return (
<div style={{ display: "flex", gap: 16 }}>
<Tooltip content="Edit item">
<Edit style={{ cursor: "pointer" }} />
</Tooltip>
<Tooltip content="Delete item">
<TrashCan style={{ cursor: "pointer" }} />
</Tooltip>
<Tooltip content="More options">
<MoreHr style={{ cursor: "pointer" }} />
</Tooltip>
</div>
);
}Controlled visibility
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Button } from "@xsolla/xui-button";
export default function Controlled() {
const [visible, setVisible] = React.useState(false);
return (
<div style={{ display: "flex", gap: 16 }}>
<Tooltip content="I'm controlled" controlledVisible={visible}>
<Button variant="secondary">Target</Button>
</Tooltip>
<Button onPress={() => setVisible((v) => !v)}>
{visible ? "Hide" : "Show"}
</Button>
</div>
);
}Disabled trigger
disabled elements do not emit pointer events; wrap them so the tooltip can still register hover.
import * as React from "react";
import { Tooltip } from "@xsolla/xui-tooltip";
import { Button } from "@xsolla/xui-button";
export default function DisabledTrigger() {
return (
<Tooltip content="Complete the form first">
<span>
<Button disabled>Submit</Button>
</span>
</Tooltip>
);
}Behaviour
- Hover bridge. Moving the cursor from the trigger onto the tooltip keeps it visible — the tooltip surface is hoverable and a short
delayLeavegrace period (default 100ms) bridges the gap between trigger and tooltip, so the tooltip no longer flickers or disappears when the cursor moves toward it. The tooltip hides once the cursor leaves both the trigger and the tooltip.
Theme
- The tooltip is a floating surface: both the panel and its arrow use the
layer/floatcolour token (theme.colors.layer.float), not a page-levelbackground/*token, so the tooltip stays visibly lifted off the page in light and dark mode. - Label colour comes from
theme.colors.content.primary; corner radius comes fromtheme.shape.tooltip[size].
Accessibility
- Tooltip surface uses
role="tooltip"andaria-hiddenreflects visibility. - Trigger receives
aria-describedbywhile the tooltip is visible. - Press Escape to dismiss.
- Tooltip content is non-interactive — for interactive content (links, buttons), prefer Toggletip. The tooltip surface is hoverable only so the pointer can rest on it without flicker.
Related
- Toggletip — click-triggered popover with rich content.
