A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
<Tooltip.provider delay_duration={100}>
<Tooltip.root
id="tooltip-primitive"
open_delay={100}
close_delay={0}
>
<Tooltip.trigger
id="tooltip-trigger"
content_id="tooltip-content"
class="DemoIconButton"
aria-label="Add to library"
>
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
aria-hidden="true"
>
<path
d="M7.5 1v13M1 7.5h13"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
</svg>
</Tooltip.trigger>
<Tooltip.content
id="tooltip-content"
class="DemoTooltipContent"
side_offset={5}
>
Add to library <Tooltip.arrow class="DemoTooltipArrow" />
</Tooltip.content>
</Tooltip.root>
</Tooltip.provider>
/* reset */
button {
all: unset;
}
.DemoTooltipContent {
border-radius: 4px;
padding: 10px 15px;
font-size: 15px;
line-height: 1;
color: var(--violet-11);
background-color: white;
box-shadow:
hsl(206 22% 7% / 35%) 0px 10px 38px -10px,
hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
user-select: none;
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
}
.DemoTooltipContent[data-state="delayed-open"][data-side="top"] {
animation-name: slideDownAndFade;
}
.DemoTooltipContent[data-state="delayed-open"][data-side="right"] {
animation-name: slideLeftAndFade;
}
.DemoTooltipContent[data-state="delayed-open"][data-side="bottom"] {
animation-name: slideUpAndFade;
}
.DemoTooltipContent[data-state="delayed-open"][data-side="left"] {
animation-name: slideRightAndFade;
}
.DemoTooltipArrow {
fill: white;
}
.DemoIconButton {
font-family: inherit;
border-radius: 100%;
height: 35px;
width: 35px;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--violet-11);
background-color: white;
box-shadow: 0 2px 10px var(--black-a7);
user-select: none;
}
.DemoIconButton:hover {
background-color: var(--violet-3);
}
.DemoIconButton:focus {
box-shadow: 0 0 0 2px black;
}
@keyframes slideUpAndFade {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideRightAndFade {
from {
opacity: 0;
transform: translateX(-2px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideDownAndFade {
from {
opacity: 0;
transform: translateY(-2px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideLeftAndFade {
from {
opacity: 0;
transform: translateX(2px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
Anatomy
Import all parts and piece them together.
<Tooltip.provider>
<Tooltip.root>
<Tooltip.trigger />
<Tooltip.content>
<Tooltip.arrow />
</Tooltip.content>
</Tooltip.root>
</Tooltip.provider>
Anatomy
-
Provider - Wraps your app to provide global delay defaults.
-
Root - Contains all the parts of a tooltip.
-
Trigger - The button that toggles the tooltip.
-
Portal - Optional portal for content.
-
Content - The component that pops out when the tooltip is open.
-
Arrow - An optional arrow element.
API Reference
Provider
Wraps your app to provide global functionality to your tooltips.
| Prop | Type | Default | Description |
|---|---|---|---|
delay_duration
|
integer
|
700
|
Default open delay in ms for tooltips in this provider. |
disable_hoverable_content
|
boolean
|
false
|
When true, tooltip content is not hoverable. |
skip_delay_duration
|
integer
|
300
|
Delay when moving between tooltips. |
delay_duration
Default open delay in ms for tooltips in this provider.
integer
Default
700
disable_hoverable_content
When true, tooltip content is not hoverable.
boolean
Default
false
skip_delay_duration
Delay when moving between tooltips.
integer
Default
300
Root
Contains all the parts of a tooltip.
Use open with on_open_change for controlled open state in LiveView:
<Tooltip.root id="info-tooltip" open={@open} on_open_change="tooltip_open_change">
…
</Tooltip.root>
def handle_event("tooltip_open_change", %{"open" => open}, socket) do
{:noreply, assign(socket, :open, open)}
end
| Prop | Type | Default | Description |
|---|---|---|---|
close_delay
|
integer
|
0
|
Delay before closing (ms). |
default_open
|
boolean
|
false
|
Initial open state when uncontrolled. |
id
|
string
|
—
|
Unique id for the tooltip root. |
on_open_change
|
string
|
nil
|
LiveView event name pushed when open state changes. |
open
|
boolean
|
false
|
Controlled open state. |
open_delay
|
integer
|
nil
|
Override provider open delay (ms). |
close_delay
Delay before closing (ms).
integer
Default
0
default_open
Initial open state when uncontrolled.
boolean
Default
false
id
Unique id for the tooltip root.
string
Default
—
on_open_change
LiveView event name pushed when open state changes.
string
Default
nil
open
Controlled open state.
boolean
Default
false
open_delay
Override provider open delay (ms).
integer
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the tooltip is open. |
Trigger
The button that toggles the tooltip. By default, the Tooltip.content will position itself against the trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
as
|
string
|
"button"
|
|
content_id
|
string
|
nil
|
|
id
|
string
|
nil
|
as
string
Default
"button"
content_id
string
Default
nil
id
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the tooltip is open. |
Portal
When used, portals the content part into the target (default body).
| Prop | Type | Default | Description |
|---|---|---|---|
class
|
any
|
nil
|
|
container
|
string
|
"div"
|
|
id
|
string
|
—
|
|
target
|
string
|
"body"
|
class
any
Default
nil
container
string
Default
"div"
id
string
Default
—
target
string
Default
"body"
Content
The component that pops out when the tooltip is open.
| Prop | Type | Default | Description |
|---|---|---|---|
align
|
string
|
"center"
|
|
id
|
string
|
nil
|
|
side
|
string
|
"top"
|
|
side_offset
|
integer
|
4
|
|
style
|
string
|
nil
|
align
string
Default
"center"
id
string
Default
nil
side
string
Default
"top"
side_offset
integer
Default
4
style
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
closed | delayed-open
|
Reflects whether the tooltip is open. Content uses `delayed-open` when visible. |
[data-side]
|
top | right | bottom | left
|
Preferred side relative to the trigger. |
[data-align]
|
start | center | end
|
Alignment along the side. |
Arrow
An optional arrow element to render alongside the tooltip. This can be used to help visually link the trigger with the Tooltip.content. Must be rendered inside Tooltip.content.
| Prop | Type | Default | Description |
|---|---|---|---|
height
|
integer
|
5
|
|
id
|
string
|
nil
|
|
width
|
integer
|
10
|
height
integer
Default
5
id
string
Default
nil
width
integer
Default
10
Examples
Configure globally
Use the Provider to control delay_duration and skip_delay_duration globally.
<Tooltip.provider delay_duration={800} skip_delay_duration={500}>
<Tooltip.root id="tooltip-a">
<Tooltip.trigger id="tooltip-a-trigger" content_id="tooltip-a-content">…</Tooltip.trigger>
<Tooltip.content id="tooltip-a-content">…</Tooltip.content>
</Tooltip.root>
<Tooltip.root id="tooltip-b">
<Tooltip.trigger id="tooltip-b-trigger" content_id="tooltip-b-content">…</Tooltip.trigger>
<Tooltip.content id="tooltip-b-content">…</Tooltip.content>
</Tooltip.root>
</Tooltip.provider>
Show instantly
Use the open_delay prop to control the time it takes for the tooltip to open.
<Tooltip.root id="instant-tooltip" open_delay={0}>
<Tooltip.trigger id="instant-tooltip-trigger" content_id="instant-tooltip-content">…</Tooltip.trigger>
<Tooltip.content id="instant-tooltip-content">…</Tooltip.content>
</Tooltip.root>
Constrain the content size
You may want to constrain the width of the content. Use standard CSS on the content element:
.DemoTooltipContent {
max-width: 20ch;
}
Collision-aware animations
Essence exposes data-side and data-align attributes on content. Their values reflect placement at runtime. Use them to create direction-aware animations:
<Tooltip.content id="animated-tooltip-content" class="DemoTooltipContent" side_offset={5}>
…
</Tooltip.content>
.DemoTooltipContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.DemoTooltipContent[data-side="top"] {
animation-name: slideUp;
}
.DemoTooltipContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
Accessibility
Adheres to the Tooltip WAI-ARIA design pattern.
Keyboard Interactions
Keyboard Interactions
| Key | Description |
|---|---|
Tab
|
Opens/closes the tooltip without delay. |
Space
|
If open, closes the tooltip without delay. |
Enter
|
If open, closes the tooltip without delay. |
Escape
|
If open, closes the tooltip without delay. |
Custom APIs
Create your own API by abstracting the primitive parts into your own component.
Usage
<.info_tooltip label="Add to library">
<.icon_button aria-label="Add to library">+</.icon_button>
</.info_tooltip>
Implementation
def info_tooltip(assigns) do
~H"""
<Tooltip.root id={@id} open_delay={@open_delay}>
<Tooltip.trigger id={"#{@id}-trigger"} content_id={"#{@id}-content"} as="div">
{render_slot(@inner_block)}
</Tooltip.trigger>
<Tooltip.content id={"#{@id}-content"} class="DemoTooltipContent" side_offset={5}>
{@label}
<Tooltip.arrow class="DemoTooltipArrow" />
</Tooltip.content>
</Tooltip.root>
"""
end