Tooltip
Display container for Tooltip content. Has a directional arrow dependent on its placement.
install | yarn add @adobe/react-spectrum |
---|---|
added | 3.4.0 |
usage | import {Tooltip, TooltipTrigger} from '@adobe/react-spectrum' |
Examples#
<TooltipTrigger>
<ActionButton aria-label="Edit Name"><Edit /></ActionButton>
<Tooltip>Change Name</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Edit Name">
<Edit />
</ActionButton>
<Tooltip>Change Name</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Edit Name">
<Edit />
</ActionButton>
<Tooltip>
Change Name
</Tooltip>
</TooltipTrigger>
Content#
The TooltipTrigger accepts exactly two children: the element which triggers the display of the Tooltip and the Tooltip itself. The trigger element must be the first child passed into the TooltipTrigger. All content should be internationalized.
Accessibility#
Tooltip triggers must be focusable and hoverable in order to ensure that all users can activate them. When displayed, TooltipTrigger automatically associates the tooltip with the trigger element so that it is described by the tooltip content.
Tooltip Delay#
Tooltips should appear after a short delay when hovering the trigger, or instantly when using keyboard focus. This delay can be adjusted for hover. View guidelines
<TooltipTrigger delay={0}>
<ActionButton aria-label="Save"><Save /></ActionButton>
<Tooltip>Saving applies your new settings right away.</Tooltip>
</TooltipTrigger>
<TooltipTrigger delay={0}>
<ActionButton aria-label="Save">
<Save />
</ActionButton>
<Tooltip>
Saving applies your new settings right away.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger
delay={0}
>
<ActionButton aria-label="Save">
<Save />
</ActionButton>
<Tooltip>
Saving applies your
new settings right
away.
</Tooltip>
</TooltipTrigger>
Warmup / Cooldown#
Tooltips have a warm up and cool down period, see guidelines. Only one tooltip can be open at a time.
<Flex gap="size-200">
<TooltipTrigger>
<ActionButton>Hover me</ActionButton>
<Tooltip>I come up after a delay.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton>Then hover me</ActionButton>
<Tooltip>If you did it quickly, I appear immediately.</Tooltip>
</TooltipTrigger>
</Flex>
<Flex gap="size-200">
<TooltipTrigger>
<ActionButton>Hover me</ActionButton>
<Tooltip>I come up after a delay.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton>Then hover me</ActionButton>
<Tooltip>
If you did it quickly, I appear immediately.
</Tooltip>
</TooltipTrigger>
</Flex>
<Flex gap="size-200">
<TooltipTrigger>
<ActionButton>
Hover me
</ActionButton>
<Tooltip>
I come up after a
delay.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton>
Then hover me
</ActionButton>
<Tooltip>
If you did it
quickly, I appear
immediately.
</Tooltip>
</TooltipTrigger>
</Flex>
Tooltip placement#
Tooltips support a variety of placement options.
Placement#
The Tooltip's placement with respect to its trigger element can be adjusted using the placement
prop. See the props table below for a full list of available placement combinations.
<TooltipTrigger placement="end">
<ActionButton aria-label="Foo">Placement</ActionButton>
<Tooltip>
In left-to-right, this is on the right. In right-to-left, this is on the
left.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger placement="end">
<ActionButton aria-label="Foo">Placement</ActionButton>
<Tooltip>
In left-to-right, this is on the right. In
right-to-left, this is on the left.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger placement="end">
<ActionButton aria-label="Foo">
Placement
</ActionButton>
<Tooltip>
In left-to-right,
this is on the
right. In
right-to-left, this
is on the left.
</Tooltip>
</TooltipTrigger>
Offset and cross offset#
The Tooltip's offset with respect to its trigger can be adjusted using the offset
and
crossOffset
props. The offset
prop controls the spacing applied along the main axis between the element and its
trigger whereas the crossOffset
prop handles the spacing applied along the cross axis.
Below is a tooltip offset by an additional 50px above the trigger.
<TooltipTrigger offset={50}>
<ActionButton aria-label="Offset from trigger">Offset</ActionButton>
<Tooltip>This will shift up.</Tooltip>
</TooltipTrigger>
<TooltipTrigger offset={50}>
<ActionButton aria-label="Offset from trigger">
Offset
</ActionButton>
<Tooltip>This will shift up.</Tooltip>
</TooltipTrigger>
<TooltipTrigger
offset={50}
>
<ActionButton aria-label="Offset from trigger">
Offset
</ActionButton>
<Tooltip>
This will shift up.
</Tooltip>
</TooltipTrigger>
Below is a tooltip cross offset by an additional 100px to the right of the trigger.
<TooltipTrigger crossOffset={100} placement="bottom">
<ActionButton aria-label="Cross Offset from trigger">
Cross Offset
</ActionButton>
<Tooltip>This will shift over to the right.</Tooltip>
</TooltipTrigger>
<TooltipTrigger crossOffset={100} placement="bottom">
<ActionButton aria-label="Cross Offset from trigger">
Cross Offset
</ActionButton>
<Tooltip>This will shift over to the right.</Tooltip>
</TooltipTrigger>
<TooltipTrigger
crossOffset={100}
placement="bottom"
>
<ActionButton aria-label="Cross Offset from trigger">
Cross Offset
</ActionButton>
<Tooltip>
This will shift
over to the right.
</Tooltip>
</TooltipTrigger>
Events#
TooltipTrigger accepts an onOpenChange
handler which is triggered whenever the Tooltip is shown or hidden.
The example below uses onOpenChange
to update a separate element with the current open state of the
Dialog.
function Example() {
let [isOpen, setOpen] = React.useState(false);
return (
<Flex alignItems="center" gap="size-100">
<TooltipTrigger isOpen={isOpen} onOpenChange={setOpen}>
<ActionButton aria-label="Resize"><Resize /></ActionButton>
<Tooltip>Resize text.</Tooltip>
</TooltipTrigger>
<Text>Tooltip is {isOpen ? 'showing' : 'not showing'}</Text>
</Flex>
);
}
function Example() {
let [isOpen, setOpen] = React.useState(false);
return (
<Flex alignItems="center" gap="size-100">
<TooltipTrigger
isOpen={isOpen}
onOpenChange={setOpen}
>
<ActionButton aria-label="Resize">
<Resize />
</ActionButton>
<Tooltip>Resize text.</Tooltip>
</TooltipTrigger>
<Text>
Tooltip is {isOpen ? 'showing' : 'not showing'}
</Text>
</Flex>
);
}
function Example() {
let [isOpen, setOpen] =
React.useState(
false
);
return (
<Flex
alignItems="center"
gap="size-100"
>
<TooltipTrigger
isOpen={isOpen}
onOpenChange={setOpen}
>
<ActionButton aria-label="Resize">
<Resize />
</ActionButton>
<Tooltip>
Resize text.
</Tooltip>
</TooltipTrigger>
<Text>
Tooltip is{' '}
{isOpen
? 'showing'
: 'not showing'}
</Text>
</Flex>
);
}
Props#
TooltipTrigger Props#
Name | Type | Default | Description |
children | [
ReactElement,
ReactElement
] | — | |
offset | number | 7 | The additional offset applied along the main axis between the element and its anchor element. |
isDisabled | boolean | — | Whether the tooltip should be disabled, independent from the trigger. |
delay | number | 1500 | The delay time for the tooltip to show up. See guidelines. |
trigger | 'focus' | — | By default, opens for both focus and hover. Can be made to open only for focus. |
isOpen | boolean | — | Whether the overlay is open by default (controlled). |
defaultOpen | boolean | — | Whether the overlay is open by default (uncontrolled). |
placement | Placement | 'bottom' | The placement of the element with respect to its anchor element. |
containerPadding | number | 12 | The placement padding that should be applied between the element and its surrounding container. |
crossOffset | number | 0 | The additional offset applied along the cross axis between the element and its anchor element. |
shouldFlip | boolean | true | Whether the element should flip its orientation (e.g. top to bottom or left to right) when there is insufficient room for it to render completely. |
Events
Name | Type | Description |
onOpenChange | (
(isOpen: boolean
)) => void | Handler that is called when the overlay's open state changes. |
Tooltip Props#
Name | Type | Default | Description |
children | ReactNode | — | |
variant | 'neutral'
| 'positive'
| 'negative'
| 'info' | — | The visual style of the Tooltip. |
placement | 'start'
| 'end'
| 'right'
| 'left'
| 'top'
| 'bottom' | 'top' | The placement of the element with respect to its anchor element. |
showIcon | boolean | — | Whether the element is rendered. |
isOpen | boolean | — |
Layout
Name | Type | Description |
flex | Responsive<string
| number
| boolean> | When used in a flex layout, specifies how the element will grow or shrink to fit the space available. See MDN. |
flexGrow | Responsive<number> | When used in a flex layout, specifies how the element will grow to fit the space available. See MDN. |
flexShrink | Responsive<number> | When used in a flex layout, specifies how the element will shrink to fit the space available. See MDN. |
flexBasis | Responsive<number | string> | When used in a flex layout, specifies the initial main size of the element. See MDN. |
alignSelf | Responsive<'auto'
| 'normal'
| 'start'
| 'end'
| 'center'
| 'flex-start'
| 'flex-end'
| 'self-start'
| 'self-end'
| 'stretch'> | Overrides the alignItems property of a flex or grid container. See MDN. |
justifySelf | Responsive<'auto'
| 'normal'
| 'start'
| 'end'
| 'flex-start'
| 'flex-end'
| 'self-start'
| 'self-end'
| 'center'
| 'left'
| 'right'
| 'stretch'> | Specifies how the element is justified inside a flex or grid container. See MDN. |
order | Responsive<number> | The layout order for the element within a flex or grid container. See MDN. |
gridArea | Responsive<string> | When used in a grid layout, specifies the named grid area that the element should be placed in within the grid. See MDN. |
gridColumn | Responsive<string> | When used in a grid layout, specifies the column the element should be placed in within the grid. See MDN. |
gridRow | Responsive<string> | When used in a grid layout, specifies the row the element should be placed in within the grid. See MDN. |
gridColumnStart | Responsive<string> | When used in a grid layout, specifies the starting column to span within the grid. See MDN. |
gridColumnEnd | Responsive<string> | When used in a grid layout, specifies the ending column to span within the grid. See MDN. |
gridRowStart | Responsive<string> | When used in a grid layout, specifies the starting row to span within the grid. See MDN. |
gridRowEnd | Responsive<string> | When used in a grid layout, specifies the ending row to span within the grid. See MDN. |
Spacing
Name | Type | Description |
margin | Responsive<DimensionValue> | The margin for all four sides of the element. See MDN. |
marginTop | Responsive<DimensionValue> | The margin for the top side of the element. See MDN. |
marginBottom | Responsive<DimensionValue> | The margin for the bottom side of the element. See MDN. |
marginStart | Responsive<DimensionValue> | The margin for the logical start side of the element, depending on layout direction. See MDN. |
marginEnd | Responsive<DimensionValue> | The margin for the logical end side of an element, depending on layout direction. See MDN. |
marginX | Responsive<DimensionValue> | The margin for both the left and right sides of the element. See MDN. |
marginY | Responsive<DimensionValue> | The margin for both the top and bottom sides of the element. See MDN. |
Sizing
Name | Type | Description |
width | Responsive<DimensionValue> | The width of the element. See MDN. |
minWidth | Responsive<DimensionValue> | The minimum width of the element. See MDN. |
maxWidth | Responsive<DimensionValue> | The maximum width of the element. See MDN. |
height | Responsive<DimensionValue> | The height of the element. See MDN. |
minHeight | Responsive<DimensionValue> | The minimum height of the element. See MDN. |
maxHeight | Responsive<DimensionValue> | The maximum height of the element. See MDN. |
Positioning
Name | Type | Description |
position | Responsive<'static'
| 'relative'
| 'absolute'
| 'fixed'
| 'sticky'> | Specifies how the element is positioned. See MDN. |
top | Responsive<DimensionValue> | The top position for the element. See MDN. |
bottom | Responsive<DimensionValue> | The bottom position for the element. See MDN. |
left | Responsive<DimensionValue> | The left position for the element. See MDN. Consider using start instead for RTL support. |
right | Responsive<DimensionValue> | The right position for the element. See MDN. Consider using start instead for RTL support. |
start | Responsive<DimensionValue> | The logical start position for the element, depending on layout direction. See MDN. |
end | Responsive<DimensionValue> | The logical end position for the element, depending on layout direction. See MDN. |
zIndex | Responsive<number> | The stacking order for the element. See MDN. |
isHidden | Responsive<boolean> | Hides the element. |
Accessibility
Name | Type | Description |
id | string | The element's unique identifier. See MDN. |
aria-label | string | Defines a string value that labels the current element. |
aria-labelledby | string | Identifies the element (or elements) that labels the current element. |
aria-describedby | string | Identifies the element (or elements) that describes the object. |
aria-details | string | Identifies the element (or elements) that provide a detailed, extended description for the object. |
Advanced
Name | Type | Description |
UNSAFE_className | string | Sets the CSS className for the element. Only use as a last resort. Use style props instead. |
UNSAFE_style | CSSProperties | Sets inline style for the element. Only use as a last resort. Use style props instead. |
Visual options#
Positive
<TooltipTrigger>
<ActionButton aria-label="Approve"><ThumbUp /></ActionButton>
<Tooltip variant="positive" showIcon>Approve workflow.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Approve">
<ThumbUp />
</ActionButton>
<Tooltip variant="positive" showIcon>
Approve workflow.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Approve">
<ThumbUp />
</ActionButton>
<Tooltip
variant="positive"
showIcon
>
Approve workflow.
</Tooltip>
</TooltipTrigger>
Information
<TooltipTrigger>
<ActionButton aria-label="Information"><Question /></ActionButton>
<Tooltip variant="info" showIcon>More information menu.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Information">
<Question />
</ActionButton>
<Tooltip variant="info" showIcon>
More information menu.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Information">
<Question />
</ActionButton>
<Tooltip
variant="info"
showIcon
>
More information
menu.
</Tooltip>
</TooltipTrigger>
Negative
<TooltipTrigger>
<ActionButton aria-label="Danger Will Robinson"><Delete /></ActionButton>
<Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Danger Will Robinson">
<Delete />
</ActionButton>
<Tooltip variant="negative" showIcon>
Dangerous action.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger>
<ActionButton aria-label="Danger Will Robinson">
<Delete />
</ActionButton>
<Tooltip
variant="negative"
showIcon
>
Dangerous action.
</Tooltip>
</TooltipTrigger>
Options#
A TooltipTrigger can be disabled without disabling the trigger it displays on.
isDisabled
<TooltipTrigger isDisabled>
<ActionButton
aria-label="Danger Will Robinson"
onPress={() => alert('pressed trigger')}
>
<Delete />
</ActionButton>
<Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
</TooltipTrigger>
<TooltipTrigger isDisabled>
<ActionButton
aria-label="Danger Will Robinson"
onPress={() => alert('pressed trigger')}
>
<Delete />
</ActionButton>
<Tooltip variant="negative" showIcon>
Dangerous action.
</Tooltip>
</TooltipTrigger>
<TooltipTrigger
isDisabled
>
<ActionButton
aria-label="Danger Will Robinson"
onPress={() =>
alert(
'pressed trigger'
)}
>
<Delete />
</ActionButton>
<Tooltip
variant="negative"
showIcon
>
Dangerous action.
</Tooltip>
</TooltipTrigger>
Usage on disabled or non-interactive elements#
Tooltips need to be accessible to keyboard and screen reader users, so we ensure that they are only placed on focusable and hoverable elements. For example, plain text and disabled buttons aren't focusable, meaning keyboard and screen reader users would be unable to access the information in that tooltip.
If you need to display some additional context, consider using ContextualHelp.
import {Content, ContextualHelp, Flex, Heading} from '@adobe/react-spectrum';
<Flex gap="size-100" alignItems="center">
<TooltipTrigger>
<ActionButton isDisabled>Delete resource</ActionButton>
<Tooltip variant="negative" showIcon>Dangerous action.</Tooltip>
</TooltipTrigger>
<ContextualHelp variant="info">
<Heading>Permission required</Heading>
<Content>
Your admin must grant you permission before you can delete resources.
</Content>
</ContextualHelp>
</Flex>
import {
Content,
ContextualHelp,
Flex,
Heading
} from '@adobe/react-spectrum';
<Flex gap="size-100" alignItems="center">
<TooltipTrigger>
<ActionButton isDisabled>
Delete resource
</ActionButton>
<Tooltip variant="negative" showIcon>
Dangerous action.
</Tooltip>
</TooltipTrigger>
<ContextualHelp variant="info">
<Heading>Permission required</Heading>
<Content>
Your admin must grant you permission before you can
delete resources.
</Content>
</ContextualHelp>
</Flex>
import {
Content,
ContextualHelp,
Flex,
Heading
} from '@adobe/react-spectrum';
<Flex
gap="size-100"
alignItems="center"
>
<TooltipTrigger>
<ActionButton
isDisabled
>
Delete resource
</ActionButton>
<Tooltip
variant="negative"
showIcon
>
Dangerous action.
</Tooltip>
</TooltipTrigger>
<ContextualHelp variant="info">
<Heading>
Permission
required
</Heading>
<Content>
Your admin must
grant you
permission before
you can delete
resources.
</Content>
</ContextualHelp>
</Flex>
Testing#
The TooltipTrigger features an popover that transitions in and out of the page as it is opened and closed. It also has a warmup and cooldown time that you've have to account for when interacting with it. Please see the following sections in the testing docs for more information on how to handle these behaviors in your test suite.
Please also refer to React Spectrum's test suite if you find that the above isn't sufficient when resolving issues in your own test cases.