Skip to content
PorabuildHeroGPUI component library
v0.14.0 ยท Apache-2.0

Overlays

Tooltip

Contextual information shown on hover or focus.

Rust
use herogpui::components::tooltip::Tooltip;

Tooltip on docs.rsSource at v0.14.0

Usage

Showing With Arrow, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.

With Arrow

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, Tooltip, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        Tooltip::new("With an arrow")
            .show_arrow(true)
            // `offset` is the gap between trigger and panel.
            .offset(px(10.))
            .radius(px(4.))
            .child(
                Button::new("tooltip-arrow-on")
                    .label("Arrow")
                    .variant(Variant::Secondary),
            )
            .into_any_element(),
        Tooltip::new("Without one")
            .show_arrow(false)
            .child(
                Button::new("tooltip-arrow-off")
                    .label("No arrow")
                    .variant(Variant::Secondary),
            )
            .into_any_element(),
    ])

Rich Content

`body` draws the tip yourself, in place of the measured single line. The string handed to `Tooltip::new` is still required: it stays the tip's accessible name, so a rich tip cannot ship unnamed. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, Button, Tooltip, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{AnyElement, px};
use herogpui::gpui;

Tooltip::new("name is the identifier, kind is the token class, scope is where it resolves")
    .delay(0)
    .body(|_, cx| {
        let muted = cx.colors().muted;
        let row = |name: &str, meaning: &str| {
            gpui::div()
                .flex()
                .gap(px(8.))
                .child(gpui::div().w(px(44.)).child(name.to_owned()))
                .child(
                    gpui::div()
                        .text_color(muted)
                        .child(meaning.to_owned()),
                )
        };
        gpui::div()
            .flex()
            .flex_col()
            .gap(px(2.))
            .child(row("name", "the identifier"))
            .child(row("kind", "the token class"))
            .child(row("scope", "where it resolves"))
            .into_any_element()
    })
    .child(
        Button::new("tooltip-rich")
            .label("Naming tokens")
            .variant(Variant::Secondary),
    )

Also calls row (included when you copy).

Custom Triggers

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
use herogpui::prelude::{ActiveTheme, Avatar, Button, Chip, ChipLabel, ChipVariant, Color, Size, Tooltip, TooltipTrigger, Variant, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        Tooltip::new("Jane Doe")
            .delay(0)
            .show_arrow(true)
            .child(Avatar::new("tooltip-avatar").name("Jane Doe").size(Size::Sm))
            .into_any_element(),
        Tooltip::new("Verified account")
            .delay(0)
            .child(
                Chip::new()
                    .color(Color::Success)
                    .variant(ChipVariant::Soft)
                    .child(ChipLabel::new().child("Verified")),
            )
            .into_any_element(),
        Tooltip::new("What is this?")
            .delay(0)
            .child(gpui::svg()
                       .size(px(16.))
                       .path(icons::SEARCH)
                       .text_color(cx.colors().foreground)
                       .into_any_element())
            .into_any_element(),
        Tooltip::new("Tab to me")
            .delay(0)
            // `trigger="focus"`: the pointer does nothing and
            // keyboard focus is what opens it.
            .trigger(TooltipTrigger::Focus)
            .child(
                Button::new("tooltip-focus-only")
                    .label("Focus only")
                    .variant(Variant::Secondary),
            )
            .into_any_element(),
    ])

Usage

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, Tooltip, TooltipPlacement, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        Tooltip::new("Above the trigger")
            .placement(TooltipPlacement::Top)
            .child(
                Button::new("tooltip-top")
                    .label("Top")
                    .variant(Variant::Secondary),
            )
            .into_any_element(),
        Tooltip::new("Below the trigger")
            .placement(TooltipPlacement::Bottom)
            .child(
                Button::new("tooltip-bottom")
                    .label("Bottom")
                    .variant(Variant::Secondary),
            )
            .into_any_element(),
    ])

Placement

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, Tooltip, TooltipPlacement, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(TooltipPlacement::ALL
        .iter()
        .map(|p| {
            Tooltip::new(p.label())
                .placement(*p)
                .show_arrow(true)
                .child(
                    Button::new(format!("tooltip-{}", p.label()))
                        .label(p.label())
                        .variant(Variant::Secondary)
                        .into_any_element(),
                )
                .into_any_element()
        }))

Long Content

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, Tooltip, Variant};
use herogpui::gpui::prelude::*;

Tooltip::new(
    "https://example.com/a-really-long-unbroken-token-that-must-wrap-inside-the-tooltip",
)
.delay(0)
.show_arrow(true)
.child(
    Button::new("tooltip-long-content-trigger")
        .label("Long token")
        .variant(Variant::Secondary),
)

Delay

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, Tooltip};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        Tooltip::new("Opens at once")
            .delay(0)
            .child(
                Button::new("tooltip-instant")
                    .label("No delay")
                    .into_any_element(),
            )
            .into_any_element(),
        Tooltip::new("Waits half a second")
            .delay(500)
            .close_delay(0)
            .child(Button::new("tooltip-slow").label("500ms").into_any_element())
            .into_any_element(),
    ])

Hover Card

HeroGPUI extension, not a example: `HoverCard` previews rich content while its trigger is hovered (after `open_delay`, 700ms by default) or focused from the keyboard (at once). Moving the pointer onto the card keeps it open so its content can be read and pressed; leaving both starts `close_delay`, and Escape or a press outside closes it. The card is a labelled group, not a tooltip. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, Avatar, Button, HoverCard, Link, Tooltip, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        HoverCard::new("tooltip-hover-card-profile")
            .label("Jane Doe's profile")
            .content(|_, cx| {
                let muted = cx.colors().muted;
                gpui::div()
                    .flex()
                    .gap(px(12.))
                    .child(Avatar::new("tooltip-hover-card-avatar").name("Jane Doe"))
                    .child(
                        gpui::div()
                            .flex()
                            .flex_col()
                            .gap(px(4.))
                            .child(
                                gpui::div()
                                    .font_weight(gpui::FontWeight::MEDIUM)
                                    .child("Jane Doe"),
                            )
                            .child(
                                gpui::div()
                                    .text_color(muted)
                                    .child("Design engineer. Builds the component library and its docs."),
                            )
                            .child(
                                Link::new("tooltip-hover-card-link")
                                    .label("View profile"),
                            ),
                    )
                    .into_any_element()
            })
            .child(
                Button::new("tooltip-hover-card-trigger")
                    .label("@jane")
                    .variant(Variant::Tertiary),
            )
            .into_any_element(),
    ])

Helpers used by these examples

Plain functions over the public API, defined once here and copied with each example that calls them.

row
fn row(children: Vec<AnyElement>) -> AnyElement {
    gpui::div()
        .flex()
        .flex_wrap()
        .items_center()
        .justify_center()
        .gap(px(12.))
        .children(children)
        .into_any_element()
}

Anatomy

Rust types

Tooltip

Tooltip is assembled from these builders. The API reference lists each one.

Customization

Shared appearance belongs on ThemeBuilder::components and .recipe("name"). Typed builders change how Tooltip looks at one call site. Every builder also carries sx for placement, width and flex: it takes GPUI's own styling methods and refines them over the component's root after the theme, so an instance override wins. Behaviour builders live in the API reference below.

Styling

Tooltip styling
MethodValuesDescription
placement(TooltipPlacement)PlacementThe full 22-value union renders through the shared placement vocabulary: physical and aligned spellings plus the logical start/end aliases, which share their left/right side's pixels because GPUI has no RTL layout mode.
Rust
// The one slot for caller-owned low-level styling: GPUI's
// styling methods, refined over the root element last.
.sx(|el| {
    el.bg(gpui::rgba(0xffa500ff))             // background
        .text_color(gpui::rgba(0x000000ff))   // text
        .w(gpui::px(13.))                     // size
        .h(gpui::px(12.))
})

API reference

Against HeroUI v3.2.6: 13 entries ported, 6 partial, 6 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Tooltip builders
BuilderValuesDefaultDescription
new(content) + ParentElement::extendPartialAnyElementโ€”Trigger and content composition; takes tip text positionally and trigger children through ParentElement.
delay(u64)number1500The pinned docs table says 700; v3.2.6 runtime resolves 1500 from --tooltip-delay, matching pinned 's default.
close_delay(u64)number500The pinned docs table says zero; v3.2.6 runtime resolves 500 from --tooltip-close-delay, matching pinned 's default.
trigger(TooltipTrigger)TooltipTrigger::Hover | TooltipTrigger::FocusTooltipTrigger::HoverHover also answers keyboard focus; Focus ignores pointer hover.
is_disabled(bool)true | falsefalseSuppresses the tooltip while leaving its trigger content rendered.
should_skip_animation(bool)true | falsefalseShows and hides without the tooltip animation wrapper.
new(content)PartialAnyElementโ€”Tooltip body content; accepts text rather than arbitrary elements.
show_arrow(bool)true | falsefalseDraws the built-in curved arrow.
offset(Pixels)number3 (7 with arrow)Distance from the trigger, including v3's arrow-aware default.
placement(TooltipPlacement)PlacementTooltipPlacement::TopThe full 22-value union renders through the shared placement vocabulary: physical and aligned spellings plus the logical start/end aliases, which share their left/right side's pixels because GPUI has no RTL layout mode.
ParentElement::extendPartialAnyElementโ€”Trigger content is composed as the Tooltip's child, but does not expose a separately replaceable trigger part.
Not ported6 HeroUI entries not ported
Tooltip builders not ported
HeroUI v3Why it is not ported
Tooltip.Content.classNameCSS class override.
Tooltip.Content.renderRender override.
Tooltip.Trigger.classNameCSS class override for the trigger wrapper.
Tooltip.Arrow.childrenCustom arrow content; exposes only the built-in arrow switch.
Tooltip.Arrow.classNameCSS class override for the arrow wrapper.
Tooltip.Arrow.renderArrow render override.

Parts

Tooltip parts
PartDescription
TooltipState owner for delay, trigger mode and open lifecycle.
TooltipPartialHover/focus wrapper around caller-provided trigger content; GPUI has no inline-block display mode.
TooltipFloating overlay surface containing the tooltip text.
TooltipPartialOptional 12px curved arrow; custom arrow content and the separate 40% border stroke are unavailable.

States

Tooltip states
StateBuilderDescription
Global sequenceTooltipManagerOnly one tooltip stays open; the first hover waits for delay, later hovers during max(500ms, closeDelay) cooldown open immediately, and the sequence becomes cold again after cooldown.
EnteringPartialoverlay_phase Entering150ms Smooth fade and zoom from 90% with the 4px translation for the tooltip side; GPUI still has no transform-origin primitive.
Exitingoverlay_phase Exiting100ms Smooth fade and zoom to 95%.
PlacementTooltipPlacement matchThe 22-value union keeps the panel and arrow on the matching trigger edge: side-plus-alignment spellings pin their named edge, and the logical start/end aliases share their physical left/right spelling without RTL.