Skip to content

Tooltip

Text shown as an element is hovered or focused, HeroUI's on Kobalte's.

Solidtype_heroui.Tooltip is Kobalte’s Tooltip, with the classes HeroUI’s own tooltipVariants gives and HeroUI’s arrow. A Button inside it is its trigger, as in HeroUI; anything else is wrapped in Tooltip.Trigger.

tooltip_usage.mlx
module Button = Solidtype_heroui.Button
module Tooltip = Solidtype_heroui.Tooltip
let make () =
<div class_="flex items-center gap-4">
<Tooltip delay_ms=0.>
<Button variant=`secondary>"Hover me"</Button>
<Tooltip.Content><p>"This is a tooltip"</p></Tooltip.Content>
</Tooltip>
<Tooltip delay_ms=0.>
<Button is_icon_only=true aria_label="More information" variant=`tertiary>
<Gravity.Circle_info />
</Button>
<Tooltip.Content><p>"More information"</p></Tooltip.Content>
</Tooltip>
</div>
<Tooltip>
<Tooltip.Trigger><Button>"Hover for tooltip"</Button></Tooltip.Trigger>
<Tooltip.Content>
<Tooltip.Arrow />
"Helpful information about this element"
</Tooltip.Content>
</Tooltip>
Hover buttons
tooltip_placement.mlx
module Button = Solidtype_heroui.Button
module Tooltip = Solidtype_heroui.Tooltip
let make () =
<div class_="grid grid-cols-3 gap-4">
<div />
<Tooltip delay_ms=0.>
<Button class_="w-full" variant=`tertiary>"Top"</Button>
<Tooltip.Content show_arrow=true placement=`top>
<Tooltip.Arrow /> <p>"Top placement"</p>
</Tooltip.Content>
</Tooltip>
<div />
<Tooltip delay_ms=0.>
<Button class_="w-full" variant=`tertiary>"Left"</Button>
<Tooltip.Content show_arrow=true placement=`left>
<Tooltip.Arrow /> <p>"Left placement"</p>
</Tooltip.Content>
</Tooltip>
<div class_="flex items-center justify-center">
<span class_="text-sm text-muted">"Hover buttons"</span>
</div>
<Tooltip delay_ms=0.>
<Button class_="w-full" variant=`tertiary>"Right"</Button>
<Tooltip.Content show_arrow=true placement=`right>
<Tooltip.Arrow /> <p>"Right placement"</p>
</Tooltip.Content>
</Tooltip>
<div />
<Tooltip delay_ms=0.>
<Button class_="w-full" variant=`tertiary>"Bottom"</Button>
<Tooltip.Content show_arrow=true placement=`bottom>
<Tooltip.Arrow /> <p>"Bottom placement"</p>
</Tooltip.Content>
</Tooltip>
<div />
</div>
tooltip_with_arrow.mlx
module Button = Solidtype_heroui.Button
module Tooltip = Solidtype_heroui.Tooltip
let make () =
<div class_="flex items-center gap-4">
<Tooltip delay_ms=0.>
<Button variant=`secondary>"With Arrow"</Button>
<Tooltip.Content show_arrow=true>
<Tooltip.Arrow /> <p>"Tooltip with arrow indicator"</p>
</Tooltip.Content>
</Tooltip>
<Tooltip delay_ms=0.>
<Button variant=`primary>"Custom Offset"</Button>
<Tooltip.Content show_arrow=true offset=12.>
<Tooltip.Arrow /> <p>"Custom offset from trigger"</p>
</Tooltip.Content>
</Tooltip>
</div>
JD
Active
tooltip_custom_triggers.mlx
module Avatar = Solidtype_heroui.Avatar
module Chip = Solidtype_heroui.Chip
module Tooltip = Solidtype_heroui.Tooltip
let make () =
<div class_="flex items-center gap-6">
<Tooltip delay_ms=0.>
<Tooltip.Trigger aria_label="User avatar">
<Avatar size=`sm>
<Avatar.Image
alt="Jane Doe"
src="https://img.heroui.chat/image/avatar?w=400&h=400&u=4"
/>
<Avatar.Fallback>"JD"</Avatar.Fallback>
</Avatar>
</Tooltip.Trigger>
<Tooltip.Content show_arrow=true>
<Tooltip.Arrow />
<div class_="flex flex-col gap-0 py-1">
<p class_="font-semibold">"Jane Doe"</p>
<p class_="text-xs text-muted">"jane@example.com"</p>
</div>
</Tooltip.Content>
</Tooltip>
<Tooltip delay_ms=0.>
<Tooltip.Trigger aria_label="Status chip">
<Chip color=`success>
<Gravity.Circle_check_fill width=12 />
<Chip.Label>"Active"</Chip.Label>
</Chip>
</Tooltip.Trigger>
<Tooltip.Content class_="flex items-center gap-1.5">
<span class_="relative flex size-2">
<span
class_="absolute inline-flex h-full w-full animate-ping \
rounded-full bg-success opacity-75"
/>
<span class_="relative inline-flex size-2 rounded-full bg-success" />
</span>
<p>"Jane is currently online"</p>
</Tooltip.Content>
</Tooltip>
<Tooltip delay_ms=0.>
<Tooltip.Trigger aria_label="Info icon">
<div class_="rounded-full bg-accent-soft p-2">
<Gravity.Circle_question class_="text-accent-soft-foreground" />
</div>
</Tooltip.Trigger>
<Tooltip.Content show_arrow=true>
<Tooltip.Arrow />
<div class_="max-w-xs px-1 py-1.5">
<p class_="mb-1 font-semibold">"Help Information"</p>
<p class_="text-sm text-muted">
"This is a helpful tooltip with more detailed information about \
this feature."
</p>
</div>
</Tooltip.Content>
</Tooltip>
</div>

HeroUI’s Render Function example gives the content render, React’s way to swap the element it renders, which a Solid component has no need of.

class_ is added after HeroUI’s classes.

tooltip_tailwind.mlx
module Button = Solidtype_heroui.Button
module Tooltip = Solidtype_heroui.Tooltip
let make () =
<Tooltip delay_ms=0.>
<Button variant=`secondary>"Share link"</Button>
<Tooltip.Content
class_="rounded-lg border border-border/80 bg-surface px-2.5 py-1 \
text-xs text-foreground shadow-sm">
<p>"Copied to clipboard"</p>
</Tooltip.Content>
</Tooltip>

HeroUI’s classes are global, so a stylesheet restyles every one, as with HeroUI in React:

@layer components {
.tooltip {
@apply rounded-xl shadow-lg;
}
.tooltip__trigger {
@apply cursor-help;
}
}

A tooltip given no delay_ms or close_delay_ms reads the page’s, as HeroUI’s does, else waits 1500 ms to open and 500 ms to close:

:root {
--tooltip-delay: 1500ms;
--tooltip-close-delay: 500ms;
}
  • .tooltip, .tooltip__trigger
  • Entering: [data-entering="true"], while it appears
  • Exiting: [data-exiting="true"], while it disappears
  • Placement: [data-placement="top"], and the other sides

Kobalte’s tooltip opens, closes, places itself and names its trigger’s description. As its pointer leaves the trigger, Kobalte keeps the tooltip open while the pointer crosses toward it, where React Aria waits out its close delay. The port keeps the tooltip on the page while HeroUI’s exit animation plays, and declares data-entering and data-exiting as React Aria does, so HeroUI’s keyframes run both ways.

Kobalte’s arrow draws a shape of its own, so the port keeps it, hidden, where Kobalte places it, and draws HeroUI’s arrow there. Tooltip.Trigger is Kobalte’s trigger as the <div role="button"> HeroUI’s is, so a button inside it is not a button inside a button.

Each prop beside the name HeroUI’s React component gives it.

React solidtype Type Default Description
delay delay_ms float --tooltip-delay, else 1500 Before it opens
closeDelay close_delay_ms float --tooltip-close-delay, else 500 Before it closes
trigger trigger `hover | `focus `hover What opens it
isDisabled is_disabled bool false Whether it never opens
isOpen is_open bool Whether it is open, controlled
defaultOpen default_open bool false Whether it starts open
onOpenChange on_open_change bool -> unit Told as it opens or closes
children children elements Its trigger and its content
React solidtype Type Default Description
placement placement `top | `bottom | `left | `right `top Its side of the trigger
offset offset float 3, 7 with the arrow Its distance from the trigger
showArrow show_arrow bool false Whether it draws an arrow, for its offset
shouldFlip should_flip bool true Whether it moves to the other side where there is no room
className class_ string Added after HeroUI’s classes
children children elements What it shows
React solidtype Type Description
aria-label aria_label string Its name, where its children give none
className class_ string Added after HeroUI’s classes
children children elements What it wraps
React solidtype Type Description
className class_ string Its class
children children elements An arrow of its own, in place of HeroUI’s

Original HeroUI documentation: Tooltip.

The same HeroUI Tooltip twice, under the same props, each open as it starts and as its own trigger is hovered: in solidtype on the left, in React on the right. Below them, every attribute of each trigger: a marked row is one they disagree on, and a dimmed row is a framework’s own, which no style reads.

solidtype — OCaml, on Solid and Kobalte
React — TypeScript, on React Aria
attributesolidtypeReact