Link
Navigate or open external resources.
1use herogpui::components::link::Link;Usage
1use herogpui::prelude::Link;
2
3Link::new("link-hover")
4 .label("Hover to see the underline")
5 .href("#")Live HeroGPUI compiled to WebAssembly. Select any example without loading another WASM instance.
Anatomy
Rust types
Link
Link is assembled from these builders. The API reference lists each one.
Customization
Appearance builders and theme tokens Link uses.
Styling
| Style | Description |
|---|---|
flex + items_center + text_color(colors.link) + FontWeight::MEDIUM + small_radius + cursor_pointer | The stylesheet is the contract — the docs' Global CSS snippet still says `font-semibold`, but the rule is `font-medium`. `--link` defaults to `--foreground`. Is a block-level flex row with no gap of its own (`.link` has none), and `underline-offset-4` has no GPUI analogue. |
hover/active StyleRefinement underline 1.5px via colors.muted | Only the decoration colour changes between the two states; gpui panics on a second `hover` call, so each state's closure owns its whole underline. |
icon(element) laid out as a plain root child; no gap, no default-icon spacing | Em-sized icon box with a 60% resting opacity that fills on interaction. The `ms-1 pb-1.5` spacing belongs to `[data-default-icon=true]` alone — the childless built-in arrow, which does not draw. The port's `icon(element)` draws the caller's element as-is, flush against the label. |
API reference
Builders
| Builder | Type | Default | Description |
|---|---|---|---|
href( | url | — | Destination for the anchor. GPUI has no in-app navigation graph, so `href` opens through the OS handler (`App::open_url`) on press. |
is_disabled( | bool | false | Disables pointer and keyboard interaction: the disabled opacity, no pointer reach, and no tab stop. |
label( | text / AnyElement | — | Content rendered inside the link. Composes it as the label text plus an optional icon element, ordered by where the icon belongs (orders `Link.Icon` among the children). |
on_press( | handler | — | Fired when the link is activated, in addition to opening `href`. The callback receives GPUI's ClickEvent. |
auto_focus( | bool | — | Whether the element should receive focus on render. A link is not otherwise a focus target here, so this also makes it one; a disabled link is skipped by Tab and must not grab focus either. |
render( | closure | — | Overrides the default element with a custom render function. GPUI has no props to spread onto a caller-built element, so the closure receives the interactive half alone (`isHovered`, `isPressed`, `isFocused`, `isFocusVisible`, `isDisabled`) and draws the content; the root keeps the `href`, `onPress`, focus and disabled wiring either way. |
icon( | AnyElement | — | Custom icon content. A childless `Link.Icon` renders v3's built-in arrow with `data-default-icon="true"` and its `ms-1 pb-1.5` spacing, and that default arrow has no port path: `icon(element)` is the custom-children path only, so draws no icon at all without it. `icon_first(true)` moves the icon before the label the way v3's child ordering does. |
Parts
| Part | Description |
|---|---|
Link | Root anchor on the `.link` BEM class: `relative inline-flex h-fit w-fit items-center rounded-xl font-medium text-link no-underline decoration-[1.5px]`. The text colour is the `--link` token and never changes state; is a block-level flex row (no inline-flex) that hugs its content. |
Link | `hit testing-none inline-flex size-[0.75em] shrink-0 items-center justify-center text-current opacity-60`, raised to full opacity on hover, press and focus. `[data-default-icon=true]` adds `ms-1 pb-1.5`, but that rule belongs only to the childless built-in arrow, which does not draw; `icon(element)` is the arbitrary-children path and gets no default-icon spacing. Draws the caller's element verbatim, flush against the label, without the em-sized box or the default 60% opacity. |
States
| Builder | State | Description |
|---|---|---|
hover(StyleRefinement underline) with colors.muted.alpha(0.5) | Hovered | Turns the 1.5px underline on with `decoration-muted/50`; the text colour never changes. |
active(StyleRefinement underline) with colors.muted | Pressed | Takes the underline decoration to full `decoration-muted`. |
tab_stop_handle + ring_if_focused | Focus visible | Keyboard focus displays the status-focused ring; `track_focus` is what puts the link in the tab order. |
is_disabled + disabled_opacity + track_focus gated on interactivity | Disabled | Status-disabled: the disabled opacity, no pointer reach, and no tab stop. |