Tooltip
Tooltips appear on demand to explain UI elements, clarify functionality, or provide helpful context without permanently occupying screen space. They automatically position themselves relative to their trigger element.
import {Tooltip} from "@qualcomm-ui/react/tooltip"Overview
The tooltip should be used to provide additional non-essential information. It is linked to an interactive element (the trigger) and will be shown when this element is focused or hovered. Because the tooltip itself can't be focused or tabbed to, it should not contain interactive elements. Only one tooltip can be open at a time.
In general, tooltips should be used sparingly and only contain succinct information. Here are guidelines to help you decide when to use one:
- When using controls that lack visual labels, like icon buttons.
- When defining a term or inline item.
- When additional information may help a user make decisions.
- When providing more context to an element.
Examples
Simple
The simple API provides a standalone component with built-in layout. The trigger is supplied as a prop, and the content of the tooltip is the children.
<Tooltip trigger={<Button emphasis="primary">Hover me</Button>}>
Hello world!
</Tooltip>
Composite
Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.
<Tooltip.Root>
<Tooltip.Trigger>
<Button emphasis="primary">Hover me</Button>
</Tooltip.Trigger>
<Portal>
<Tooltip.Positioner>
<Tooltip.Content>
<Tooltip.Arrow>
<Tooltip.ArrowTip />
</Tooltip.Arrow>
Hello world!
</Tooltip.Content>
</Tooltip.Positioner>
</Portal>
</Tooltip.Root>
Placement
The tooltip's position, relative to the trigger element, can be set using the placement option of the positioning prop. Its default value is top.
Change the values in the example below and hover over the select to see the tooltip position change.
import {type ReactElement, useState} from "react"
import {selectCollection} from "@qualcomm-ui/core/select"
import type {Placement} from "@qualcomm-ui/dom/floating-ui"
import {Select} from "@qualcomm-ui/react/select"
import {Tooltip} from "@qualcomm-ui/react/tooltip"
const positions = selectCollection({
items: [
"top-start",
"top",
"top-end",
"right-start",
"right",
"right-end",
"bottom-start",
"bottom",
"bottom-end",
"left-start",
"left",
"left-end",
],
})
export function TooltipPlacementDemo(): ReactElement {
const [placement, setPlacement] = useState<Placement>("top")
return (
<div className="w-48">
<Tooltip
positioning={{placement}}
trigger={
<Select
aria-label="Select a position"
collection={positions}
onValueChange={(value) => setPlacement(value[0] as Placement)}
positioning={{sameWidth: true}}
value={[placement]}
/>
}
>
{placement}
</Tooltip>
</div>
)
}Close On Click / Escape
By default, the tooltip will close when the Escape key is pressed or a click is detected. You can customize this behavior by using the closeOnClick and closeOnEscape props.
<Tooltip
closeOnClick={false}
closeOnEscape={false}
trigger={<Button emphasis="primary">Hover me</Button>}
>
Hello world!
</Tooltip>
Controlled Visibility
Set the initial visibility using the defaultOpen prop, or use open and onOpenChange to control the visibility manually. These props follow our controlled state pattern.
import {type ReactElement, useState} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Tooltip} from "@qualcomm-ui/react/tooltip"
export function TooltipControlledStateDemo(): ReactElement {
const [open, setOpen] = useState(false)
return (
<div className="flex w-40 flex-col items-center gap-4">
<Tooltip
onOpenChange={setOpen}
open={open}
trigger={<Button emphasis="primary">Hover me</Button>}
>
Hello world!
</Tooltip>
<output className="font-body-sm text-neutral-primary flex">
The tooltip is {open ? "visible" : "hidden"}
</output>
</div>
)
}Disabled
You can disable the tooltip by using the disabled prop.
import {type ReactElement, useState} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Tooltip} from "@qualcomm-ui/react/tooltip"
export function TooltipDisabledDemo(): ReactElement {
const [disabled, setDisabled] = useState(true)
return (
<div className="flex flex-col items-center gap-4">
<Tooltip
disabled={disabled}
trigger={<Button emphasis="primary">Hover me</Button>}
>
Hello world!
</Tooltip>
<Button onClick={() => setDisabled(!disabled)} variant="outline">
{disabled ? "Enable" : "Disable"} tooltip
</Button>
</div>
)
}Shortcuts
Shortcuts are enabled for common use cases.
Arrow
If you don't wish to customize the arrow tip, you can omit it and Tooltip.Arrow will render the default one. At that, <Tooltip.Arrow /> is equivalent to:
<Tooltip.Arrow>
<Tooltip.ArrowTip />
</Tooltip.Arrow>API
<Tooltip>
The simple tooltip extends the Tooltip.Root component with the following props:
| Prop | Type | Default |
|---|---|---|
Props applied to the arrow element. | ||
Props applied to the arrow tip element. | ||
Props applied to the content element. | ||
Whether to hide the arrow. | boolean | false |
Props applied to the portal element. | PortalProps | |
Props applied to the positioner element. |
booleanPortalPropsComposite API
<Tooltip.Root>
| Prop | Type | Default |
|---|---|---|
Whether the tooltip should close when the trigger is clicked. | boolean | |
Whether the tooltip should close when the escape key is pressed. | boolean | |
The document's text/writing direction. | 'ltr' | 'rtl' | 'ltr' |
Controls the component's interactivity. If true, the component's trigger
element will not activate the tooltip. | boolean | |
Function called when the tooltip is opened/closed. | ( | |
The open state of the tooltip. | boolean | |
The user provided options used to position the popover content |
booleanboolean'ltr' | 'rtl'
booleantrue, the component's trigger
element will not activate the tooltip.(
open: boolean,
) => void
boolean<Tooltip.Trigger>
| Prop | Type |
|---|---|
React children Render Prop | |
id attribute. If
omitted, a unique identifier will be automatically generated for accessibility. | string |
string| Attribute / Property | Value |
|---|---|
data-expanded | |
data-part | 'trigger' |
data-scope | 'tooltip' |
data-state | | 'open' |
data-expandeddata-part'trigger'data-scope'tooltip'data-state| 'open'
| 'closed'
<Tooltip.Positioner>
<div> element by default.| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be automatically generated for accessibility. | string |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
className | 'qui-tooltip__positioner' |
data-part | 'positioner' |
data-scope | 'tooltip' |
style |
className'qui-tooltip__positioner'data-part'positioner'data-scope'tooltip'style<Tooltip.Content>
<div> element by default.| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be automatically generated for accessibility. | string |
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
string| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
className | 'qui-tooltip__content' |
data-part | 'content' |
data-placement | | 'bottom' |
data-scope | 'tooltip' |
data-state | | 'open' |
hidden | boolean |
className'qui-tooltip__content'data-part'content'data-placement| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
data-scope'tooltip'data-state| 'open'
| 'closed'
hiddenboolean<Tooltip.Arrow>
<Tooltip.ArrowTip/> by default if no children are provided.| Prop | Type |
|---|---|
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
className | 'qui-tooltip__arrow' |
data-part | 'arrow' |
data-scope | 'tooltip' |
style |
className'qui-tooltip__arrow'data-part'arrow'data-scope'tooltip'style<Tooltip.ArrowTip>
<div> element by default.| Prop | Type |
|---|---|
Allows you to replace the component's HTML element with a different tag or component. Learn more | | ReactElement |
| ReactElement
| ((
props: object,
) => ReactElement)
| Attribute / Property | Value |
|---|---|
className | 'qui-tooltip__arrow-tip' |
data-part | 'arrowTip' |
data-scope | 'tooltip' |
style |
className'qui-tooltip__arrow-tip'data-part'arrowTip'data-scope'tooltip'styleTooltipPositioningOptions
| Prop | Type | Default |
|---|---|---|
The minimum padding between the arrow and the floating element's corner. | number | 10 |
The overflow boundary of the reference element | () => | |
Whether the popover should fit the viewport. | boolean | |
Whether to flip the placement when the floating element overflows the boundary. | | boolean | true |
Function that returns the anchor rect | ( | |
The main axis offset or gap between the reference and floating element | number | 8 |
Whether the popover should be hidden when the reference element is detached | boolean | |
Options to activate auto-update listeners | | boolean | true |
The offset of the floating element | { | |
Function called when the placement is computed | ( | |
Function called when the floating element is positioned or not | (data: { | |
The virtual padding around the viewport edges to check for overflow | number | |
Whether the floating element can overlap the reference element | boolean | false |
The initial placement of the floating element | | 'bottom' | 'top' |
Whether to make the floating element same width as the reference element | boolean | |
The secondary axis offset or gap between the reference and floating elements | number | |
Whether the popover should slide when it overflows. | boolean | |
The strategy to use for positioning | | 'absolute' | 'absolute' |
A callback that will be called when the popover needs to calculate its
position. | (data: { |
number() =>
| 'clippingAncestors'
| Element
| Array<Element>
| {
height: number
width: number
x: number
y: number
}
boolean| boolean
| Array<
| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
>
(
element:
| HTMLElement
| VirtualElement,
) => {
height?: number
width?: number
x?: number
y?: number
}
numberboolean| boolean
| {
ancestorResize?: boolean
ancestorScroll?: boolean
animationFrame?: boolean
elementResize?: boolean
layoutShift?: boolean
}
{
crossAxis?: number
mainAxis?: number
}
(
data: ComputePositionReturn,
) => void
(data: {
placed: boolean
}) => void
numberboolean| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
booleannumberboolean| 'absolute'
| 'fixed'
(data: {
updatePosition: () => Promise<void>
}) => void | Promise<void>