IconButton
IconButton is a fixed square, icon-only action for toolbars, compact title bars, and familiar actions with clear surrounding context. Use another component when visible text, complex content, or link navigation is required.
Basic Usage
let example = IconButton::new("settings-button", IconName::Settings)
.aria_label("设置")
.tooltip("设置");Every icon-only button must supply a name through aria_label(...); Tooltip is only visual help.
Variants
let example = div().flex().gap(px(8.)).flex_wrap().children(
[
IconButtonVariant::Primary,
IconButtonVariant::Outline,
IconButtonVariant::Ghost,
IconButtonVariant::Destructive,
IconButtonVariant::Secondary,
]
.into_iter()
.enumerate()
.map(|(index, variant)| {
IconButton::new(("icon-variant", index), IconName::Settings)
.aria_label(format!("{variant:?}"))
.variant(variant)
}),
);Sizes
let example = div().flex().items_center().gap(px(8.)).children(
[
ComponentSize::Xs,
ComponentSize::Sm,
ComponentSize::Md,
ComponentSize::Lg,
]
.into_iter()
.enumerate()
.map(|(index, size)| {
IconButton::new(("icon-size", index), IconName::Settings)
.aria_label(format!("{size:?}"))
.size(size)
}),
);State and focus
let example = div()
.flex()
.flex_col()
.items_center()
.gap(px(10.))
.child(
div().flex().gap(px(8.)).children([
IconButton::new("icon-state-normal", IconName::Settings)
.aria_label(normal)
.on_focus_in(cx, move |this, _, cx| {
this.record_focus(normal, true, cx);
})
.on_blur_in(cx, move |this, _, cx| {
this.record_focus(normal, false, cx);
}),
IconButton::new("icon-state-selected", IconName::Settings)
.aria_label(selected)
.selected(true),
IconButton::new("icon-state-disabled", IconName::Settings)
.aria_label(disabled)
.disabled(true),
]),
)
.child(focus_status);selected(bool) is a controlled toggle state. It uses the same selected tokens as Button and exposes accessibility toggled semantics; the component never flips it internally.
Tooltip and accessible name
let example = IconButton::new("icon-tooltip", IconName::Settings)
.aria_label(label)
.aria_description(description)
.tooltip(label);Anatomy and API
The root supplies Button role, a square hit area, themed states, and one Tab stop. The inner Icon is decorative and does not create another name or focus target.
| API | Description |
|---|---|
IconButton::new(id, icon) | Creates an icon-only button with a stable ElementId. |
.aria_label(text) | Sets the required accessible name; a visual Tooltip cannot replace it. |
.aria_description(text) | Sets supplementary information for assistive technology. |
.tooltip(text_or_tooltip) | Accepts a string or Tooltip configuration with open, arrow, color, and animation options. |
.tooltip_placement(TooltipPlacement) | Sets the preferred Tooltip placement; defaults to Bottom with automatic flip/shift. |
.variant(...) | Primary, Outline, Ghost, Destructive, or Secondary. |
.size(...) | Xs 24px, Sm 32px, Md 36px (default), or Lg 40px. |
.icon_color(color) | Overrides enabled icon color only; disabled tokens still win. |
.disabled(bool) | Blocks mouse/keyboard activation and leaves the Tab order. |
.selected(bool) | Sets controlled selected/toggled state without flipping it internally. |
.on_click(...) / .on_click_in(...) | Registers the shared mouse, Enter, and Space activation contract. |
.on_focus(...) / .on_blur(...) | Registers callbacks for real focus and blur transitions. |
.on_focus_in(...) / .on_blur_in(...) | Uses a host Entity listener so handlers can mutate state and call cx.notify(). |
States, Keyboard, and Accessibility
Normal, hover, pressed, focus-visible, and disabled use the Button theme matrix. The host wires Tab/Shift+Tab to GPUI focus traversal. Mouse, touch, and focused Enter/Space activation all enter the same on_click; Enter and Space each fire exactly once only after a complete KeyDown + KeyUp cycle. A string Tooltip appears after 500ms of hover or keyboard focus; configuration supports immediate open(true) or forced open(false). Blur starts the exit transition. Escape dismisses without moving focus, and a controlled true value must change false -> true to reopen. Mouse-created focus does not start the keyboard Tooltip path.
Business focus callbacks and Tooltip share one FocusHandle, so one transition calls business code once. Rerenders use the latest handler and disabled leaves Tab order. See Focusable for _in and lifecycle semantics.
Every icon-only button must have an aria_label. aria_description is supplementary semantics and Tooltip is visual help; Vektra does not copy between them. A disabled IconButton cannot focus or activate, but hover Tooltip remains available to explain why it is disabled.
Theme, Responsive Behavior, and Platforms
Light, Dark, and System resolve semantic tokens. Fixed Tooltip instance colors remain the caller's theme and contrast responsibility, while default Tooltip motion respects GPUI reduced motion. IconButton uses logical pixels and SVG for high-DPI output and keeps its square size in narrow parents. Desktop and WASM use the same GPUI component path. The host owns application shortcuts, Tab Actions, and platform window lifecycle.
Current Limits
- No visible label or
Linkvariant. - Tooltip is plain text and does not replace the accessible name.
- No arbitrary padding, radius, background, or hit-area styling pass-through.
- Pointer visuals still need real pointer verification; the preview requires WebGPU and host-provided Chinese fonts.
Performance contract
- The standard load is 100 visible IconButtons; construction, layout, and paint are O(1).
- SVG paths are delegated to GPUI's resource/SVG cache and must not be reparsed per frame by Vektra. Vektra adds no parallel icon cache.
- 100K same-path construction and 10K visible stress cases are covered by
coverage/tooltip_icon_focusand the stress target.