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

Buttons

Toggle Button

Toggle between a selected and unselected state.

Rust
use herogpui::components::toggle_button::{ToggleButton, ToggleButtonGroup};

ToggleButton on docs.rsSource at v0.14.0

Usage

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

Usage

`radius` sets the corners; `sx` squares the top-left corner, including while pressed. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ToggleButton, ToggleButtonGroup};
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![ToggleButton::new("toggle-button")
        .radius(px(8.))
        .sx(|el| el.rounded_tl(px(0.)))
        .label("Bold")
        .into_any_element()])

Hover Colour

`hover_bg` follows the Button resting-endpoint contract: the fade runs from the resting background to the named hover fill. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let accent = cx.colors().accent.color;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ToggleButton::new("toggle-button-hover-bg")
        .label("Hover")
        .hover_bg(accent)
        .into_any_element()])

Sizes

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

Rust
use herogpui::prelude::{Size, ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(Size::ALL
        .iter()
        .filter_map(|sz| {
            let key = format!("toggle-size-{sz:?}");
            Some({
                ToggleButton::new(format!("toggle-button-sz-{sz:?}"))
                    .label(sz.label())
                    .size(*sz)
                    .default_selected(true)
            })
        }))

Icon Only

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

Rust
use herogpui::prelude::{ActiveTheme, ToggleButton, ToggleButtonGroup, 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![
        ToggleButton::new("toggle-button-1")
            .is_icon_only(true)
            .default_selected(true)
            .child(gpui::svg()
                       .size(px(16.))
                       .path(icons::EYE)
                       .text_color(cx.colors().foreground)
                       .into_any_element())
            .into_any_element(),
        ToggleButton::new("toggle-button-2")
            .is_icon_only(true)
            .child(gpui::svg()
                       .size(px(16.))
                       .path(icons::COPY)
                       .text_color(cx.colors().foreground)
                       .into_any_element())
            .into_any_element(),
        ToggleButton::new("toggle-button-3")
            .is_icon_only(true)
            .child(gpui::svg()
                       .size(px(16.))
                       .path(icons::SEARCH)
                       .text_color(cx.colors().foreground)
                       .into_any_element())
            .into_any_element(),
    ])

Disabled

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

Rust
use herogpui::prelude::{ToggleButton, ToggleButtonGroup};
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![
        ToggleButton::new("toggle-button-dis-1")
            .label("Off")
            .is_disabled(true)
            .into_any_element(),
        ToggleButton::new("toggle-button-dis-2")
            .label("On")
            .is_selected(true)
            .is_disabled(true)
            .into_any_element(),
    ])

Controlled

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, ToggleButton, ToggleButtonGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

let liked = self.toggle_like;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ToggleButton::new("toggle-button-like")
            .label(if liked { "Liked" } else { "Like" })
            .is_selected(liked)
            .child(gpui::svg()
                       .size(px(16.))
                       .path(if liked {
                                   icons::HEART_FILL
                               } else {
                                   icons::HEART
                               })
                       .text_color(cx.colors().foreground)
                       .into_any_element())
            .on_change(cx.listener(|this, v: &bool, _, cx| {
                this.toggle_like = *v;
                cx.notify();
            }))
            .into_any_element(),
        para(
            if liked {
                "Status: selected"
            } else {
                "Status: not selected"
            },
            cx,
        ),
    ])

Also calls para (included when you copy).

Single selection

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::{SelectionMode, ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;

let single = self.toggle_single.clone();

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ToggleButtonGroup::new("toggle-button-single")
        .selection_mode(SelectionMode::Single)
        .separators(true)
        .selected_keys(single.into_iter().collect::<Vec<_>>())
        .child_toggle(ToggleButton::new("toggle-button-left").key("left").label("Left"))
        .child_toggle(
            ToggleButton::new("toggle-button-center")
                .key("center")
                .label("Center"),
        )
        .child_toggle(ToggleButton::new("toggle-button-right").key("right").label("Right"))
        .on_selection_change(cx.listener(|this, keys: &[SharedString], _, cx| {
            this.toggle_single = keys.first().cloned();
            cx.notify();
        }))
        .into_any_element()])

Multiple selection

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::{SelectionMode, ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;

let multiple = self.toggle_multiple.clone();

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ToggleButtonGroup::new("toggle-button-multiple")
        .selection_mode(SelectionMode::Multiple)
        .separators(true)
        .selected_keys(multiple.iter().cloned().collect::<Vec<_>>())
        .child_toggle(ToggleButton::new("toggle-button-bold").key("bold").label("Bold"))
        .child_toggle(
            ToggleButton::new("toggle-button-italic")
                .key("italic")
                .label("Italic"),
        )
        .child_toggle(
            ToggleButton::new("toggle-button-underline")
                .key("underline")
                .label("Underline"),
        )
        .on_change(cx.listener(|this, keys: &[SharedString], _, cx| {
            this.toggle_multiple = keys.iter().cloned().collect();
            cx.notify();
        }))
        .into_any_element()])

Variants

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

Rust
use herogpui::prelude::{ToggleButton, ToggleButtonGroup, ToggleVariant};
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![
        ToggleButton::new("toggle-button-default")
            .label("Default")
            .default_selected(true)
            .into_any_element(),
        ToggleButton::new("toggle-button-ghost")
            .label("Ghost")
            .variant(ToggleVariant::Ghost)
            .default_selected(true)
            .into_any_element(),
    ])

Orientation

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

Rust
use herogpui::prelude::{Orientation, ToggleButton, ToggleButtonGroup};
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![ToggleButtonGroup::new("toggle-button-orientation-horizontal")
                     .separators(true)
                     .child_toggle(ToggleButton::new("toggle-button-h-1").label("Day"))
                     .child_toggle(ToggleButton::new("toggle-button-h-2").label("Week"))
                     .child_toggle(ToggleButton::new("toggle-button-h-3").label("Month"))
                     .into_any_element(),
        ToggleButtonGroup::new("toggle-button-orientation-vertical")
            .orientation(Orientation::Vertical)
            .separators(true)
            .child_toggle(ToggleButton::new("toggle-button-1").label("Day"))
            .child_toggle(ToggleButton::new("toggle-button-2").label("Week"))
            .child_toggle(ToggleButton::new("toggle-button-3").label("Month"))
            .into_any_element(),
    ])

Full Width

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

Rust
use herogpui::prelude::{ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![gpui::div()
        .w_full()
        .child(
            ToggleButtonGroup::new("toggle-button-full-width")
                .full_width(true)
                .separators(true)
                .child_toggle(ToggleButton::new("toggle-button-1").label("Left"))
                .child_toggle(ToggleButton::new("toggle-button-2").label("Center"))
                .child_toggle(ToggleButton::new("toggle-button-3").label("Right")),
        )
        .into_any_element()])

Without Separator

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

Rust
use herogpui::prelude::{ToggleButton, ToggleButtonGroup};
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![ToggleButtonGroup::new("toggle-button-without-separator")
        // v3: omit the `<ToggleButtonGroup.Separator />` child
        // composition — the port's default draws no dividers.
        .child_toggle(ToggleButton::new("toggle-button-1").label("One"))
        .child_toggle(ToggleButton::new("toggle-button-2").label("Two"))
        .child_toggle(ToggleButton::new("toggle-button-3").label("Three"))
        .into_any_element()])

Selection Mode

Single: exactly one member stays selected. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, SelectionMode, ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ToggleButtonGroup::new("toggle-button-selection-single")
            .selection_mode(SelectionMode::Single)
            .separators(true)
            .child_toggle(ToggleButton::new("toggle-button-1").key("a").label("A"))
            .child_toggle(ToggleButton::new("toggle-button-2").key("b").label("B"))
            .child_toggle(ToggleButton::new("toggle-button-3").key("c").label("C"))
            .into_any_element(),
        para("Multiple: any number of members can be selected.", cx),
        ToggleButtonGroup::new("toggle-button-selection-multiple")
            .selection_mode(SelectionMode::Multiple)
            .separators(true)
            .child_toggle(ToggleButton::new("toggle-button-m-1").key("a").label("A"))
            .child_toggle(ToggleButton::new("toggle-button-m-2").key("b").label("B"))
            .child_toggle(ToggleButton::new("toggle-button-m-3").key("c").label("C"))
            .into_any_element(),
    ])

Also calls para (included when you copy).

Default Selected Keys

Uncontrolled: `defaultSelectedKeys` seeds the group's own selection, and the group keeps ownership from there — clicking a member still toggles it. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, SelectionMode, ToggleButton, ToggleButtonGroup};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ToggleButtonGroup::new("toggle-button-default-single")
            .selection_mode(SelectionMode::Single)
            .separators(true)
            .default_selected_keys(["center"])
            .child_toggle(
                ToggleButton::new("toggle-button-left").key("left").label("Left")
            )
            .child_toggle(
                ToggleButton::new("toggle-button-center")
                    .key("center")
                    .label("Center"),
            )
            .child_toggle(
                ToggleButton::new("toggle-button-right")
                    .key("right")
                    .label("Right"),
            )
            .into_any_element(),
        para("Multiple: any number of members can be selected.", cx),
        ToggleButtonGroup::new("toggle-button-default-multiple")
            .selection_mode(SelectionMode::Multiple)
            .separators(true)
            .default_selected_keys(["bold", "underline"])
            .child_toggle(
                ToggleButton::new("toggle-button-m-bold").key("bold").label("Bold")
            )
            .child_toggle(
                ToggleButton::new("toggle-button-m-italic")
                    .key("italic")
                    .label("Italic"),
            )
            .child_toggle(
                ToggleButton::new("toggle-button-m-underline")
                    .key("underline")
                    .label("Underline"),
            )
            .into_any_element(),
    ])

Also calls para (included when you copy).

Vertical

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

Rust
use herogpui::prelude::{Orientation, ToggleButton, ToggleButtonGroup};
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![
        ToggleButtonGroup::new("toggle-button-vertical")
            .orientation(Orientation::Vertical)
            .separators(true)
            .child_toggle(ToggleButton::new("toggle-button-1").label("Top"))
            .child_toggle(ToggleButton::new("toggle-button-2").label("Bottom"))
            .into_any_element(),
    ])

Detached

`is_detached` separates the buttons with gaps instead of connecting them; the attached icon group above the detached one shows the default for contrast. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, ToggleButton, ToggleButtonGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ToggleButtonGroup::new("toggle-button-attached")
            .separators(true)
            .child_toggle(
                ToggleButton::new("toggle-button-1")
                    .is_icon_only(true)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::HEART)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            )
            .child_toggle(
                ToggleButton::new("toggle-button-2")
                    .is_icon_only(true)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::GEAR)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            )
            .child_toggle(
                ToggleButton::new("toggle-button-3")
                    .is_icon_only(true)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::MAIL)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            )
            .into_any_element(),
        ToggleButtonGroup::new("toggle-button-detached")
            .is_detached(true)
            .child_toggle(
                ToggleButton::new("toggle-button-1")
                    .is_icon_only(true)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::HEART)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            )
            .child_toggle(
                ToggleButton::new("toggle-button-2")
                    .is_icon_only(true)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::GEAR)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            )
            .child_toggle(
                ToggleButton::new("toggle-button-3")
                    .is_icon_only(true)
                    .child(gpui::svg()
                               .size(px(16.))
                               .path(icons::MAIL)
                               .text_color(cx.colors().foreground)
                               .into_any_element()),
            )
            .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.

para
fn para(text: &str, cx: &App) -> gpui::AnyElement {
    let colors = cx.colors();
    gpui::div()
        .text_size(px(14.5))
        .line_height(px(24.))
        .text_color(colors.foreground)
        .max_w(px(720.))
        .child(text.to_owned())
        .into_any_element()
}

Anatomy

Rust types

ToggleButtonToggleButtonGroup

Toggle Button 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 Toggle Button 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

Toggle Button styling
MethodValuesDescription
variant(ToggleVariant)ToggleVariant::Default | ToggleVariant::GhostVisual style variant
size(Size)Size::Sm | Size::Md | Size::LgSize of the toggle button
is_icon_only(bool)true | falseWhether the button contains only an icon
orientation(SelectionOrientation)SelectionOrientation::Horizontal | SelectionOrientation::VerticalLayout direction
full_width(bool)true | falseWhether the group fills available width
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: 34 entries ported, 11 partial, 2 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Toggle Button builders
BuilderValuesDefaultDescription
variant(ToggleVariant)ToggleVariant::Default | ToggleVariant::GhostToggleVariant::DefaultVisual style variant
size(Size)Size::Sm | Size::Md | Size::LgSize::MdSize of the toggle button
is_icon_only(bool)true | falsefalseWhether the button contains only an icon
is_selected(bool)true | false—Controlled selected state
default_selected(bool)true | falsefalseDefault selected state (uncontrolled)
is_disabled(bool)true | falsefalseWhether the toggle button is disabled
on_change(callback)Fn(isSelected: bool) -> ()—Handler called when selection changes
on_press(event)PartialFn(e: ClickEvent) -> ()—Handler called when the button is pressed
label(value) / content(render) / child(element)PartialAnyElement | Fn(values: ToggleButtonRenderProps) -> AnyElement—Button content or render prop
content(render)true | false—Whether the button is currently selected
selection_mode(SelectionMode)SelectionMode::Single | SelectionMode::MultipleSelectionMode::SingleWhether one or multiple buttons can be selected
selected_keys(keys)Partialiterable of keys—Controlled selection state; accepts SharedString keys only.
default_selected_keys(keys)Partialiterable of keys—Default selected keys (uncontrolled); accepts SharedString keys only.
on_selection_change(callback)PartialFn(keys: Set<Key>) -> ()—Called when selection changes; reports SharedString keys only.
disallow_empty_selection(bool)true | falsefalsePrevents clearing all selections
orientation(SelectionOrientation)SelectionOrientation::Horizontal | SelectionOrientation::VerticalSelectionOrientation::HorizontalLayout direction
is_detached(bool)true | falsefalseWhether buttons are visually separated with gaps
full_width(bool)true | falsefalseWhether the group fills available width
Not ported2 HeroUI entries not ported
Toggle Button builders not ported
HeroUI v3Why it is not ported
ToggleButtonGroup.className
ToggleButtonGroup.Separator.className

Parts

Toggle Button parts
PartDescription
ToggleButtonFocusable toggle root that owns selected, press and disabled state.
ToggleButtonGroupSelection and layout root; separators are explicit composition requested with separators(true).
ToggleButtonGroupPartialOptional explicit divider composition; uses separators(true), and custom style is unavailable.

States

Toggle Button states
StateBuilderDescription
Selectedis_selectedAccent-soft background and foreground show the selected state.
HoveredPartialis_hoveredVariant-specific hover background, including selected accent-soft-hover.
PressedPartialis_pressedPressed feedback applies the size-specific scale and pressed background.
Focus visibletab_stop_handleKeyboard focus shows the status-focused ring.
Disabledis_disabledDisabled toggles are dimmed, inert and removed from the tab order.
Controlled selectionselected_keys(keys)The caller owns the selected key set and receives selection callbacks.
Uncontrolled selectiondefault_selected_keys(keys)The group owns its selected key set after the initial seed.
Single selectionselection_mode(SelectionMode::Single)Selecting a member replaces the previous selection.
Multiple selectionselection_mode(SelectionMode::Multiple)Members can be selected independently.
Disallow empty selectiondisallow_empty_selection(bool)The last selected member cannot be cleared in single-selection mode.
Horizontal grouporientation(SelectionOrientation::Horizontal)Members flow along the horizontal axis.
Vertical grouporientation(SelectionOrientation::Vertical)Members stack along the vertical axis.
Full width groupfull_width(true)The group fills its parent and stretches each member.
Attached groupgroup_edgeAdjacent members share an edge and keep only the outer corners rounded.
Detached groupis_detached(true)Members regain full radii and are separated by a 4px gap.
Group pressedis_grouped gates anim::pressedAttached members do not scale and therefore cannot open gaps at the seam.
Group focus visiblePartialring_if_focused(offset = false)Removes the outer offset but GPUI has no inset box-shadow equivalent.