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

Navigation

Accordion

Vertically collapsing panels.

Rust
use herogpui::components::accordion::{Accordion, AccordionItem};

Accordion 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

Titles, subtitles and body text keep their line heights when used inside larger text containers; opening and closing measures the body and animates height plus opacity over 200ms. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Accordion, AccordionItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion")
        .default_expanded("1")
        .into_any_element()])

Hover Colour

`hover_bg` names the fill a hovered, enabled, closed trigger takes; open rows do not hover. An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Accordion, AccordionItem, ActiveTheme};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion-hover")
        .hover_bg(cx.colors().accent.soft())
        .into_any_element()])

Without Separator

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

Rust
use herogpui::prelude::{Accordion, AccordionItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion-nosep")
        .hide_separator(true)
        .default_expanded("1")
        .into_any_element()])

Multiple Expanded

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

Rust
use herogpui::prelude::{Accordion, AccordionItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;

let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion-multi")
        .allows_multiple_expanded(true)
        .default_expanded_keys(
            [SharedString::from("1"), SharedString::from("2")]
                .into_iter()
                .collect(),
        )
        .into_any_element()])

Disabled State

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

Rust
use herogpui::prelude::{Accordion, AccordionItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;

let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        Accordion::new(items()).id("accordion-dis").is_disabled(true)
            .into_any_element(),
        Accordion::new(items())
            .id("accordion-dis-one")
            .disabled_keys([SharedString::from("2")])
            .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::{Accordion, AccordionItem, ActiveTheme};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;

let open = self.accordion_open.clone();
let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        Accordion::new(items())
            .id("accordion-controlled")
            .expanded_keys(open.clone())
            .on_expanded_change(cx.listener(
                |this, keys: &HashSet<SharedString>, _, cx| {
                    this.accordion_open = keys.clone();
                    cx.notify();
                },
            ))
            .on_toggle(cx.listener(|this, key: &SharedString, _, cx| {
                toggle_key(&mut this.accordion_open, key);
                cx.notify();
            }))
            .into_any_element(),
        para(&format!("{} expanded", open.len()), cx),
    ])

Also calls toggle_key, para (included when you copy).

Custom Indicator

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

Rust
use herogpui::prelude::{Accordion, AccordionItem, ActiveTheme, 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![Accordion::new(vec![
        AccordionItem::new("shipping", "Shipping details")
            .content(gpui::div().child("Free shipping on orders over $50."))
            .indicator(|state, _, cx| {
                gpui::svg()
                    .size(px(16.))
                    .path(if state.is_expanded {
                        icons::MINUS
                    } else {
                        icons::PLUS
                    })
                    .text_color(cx.colors().muted)
                    .into_any_element()
            }),
        AccordionItem::new("returns", "Returns policy")
            .content(gpui::div().child("Returns are accepted within thirty days."))
            .indicator(|state, _, cx| {
                gpui::svg()
                    .size(px(16.))
                    .path(if state.is_expanded {
                        icons::MINUS
                    } else {
                        icons::PLUS
                    })
                    .text_color(cx.colors().muted)
                    .into_any_element()
            }),
    ])
    .id("accordion-indicator")
    .default_expanded("shipping")
    .into_any_element()])

FAQ Layout

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

Rust
use herogpui::prelude::{Accordion, AccordionItem, AccordionVariant, Surface};
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![Surface::new()
        .padding(px(20.))
        .gap(px(12.))
        .child(gpui::div().child("Frequently asked"))
        .child(
            Accordion::new(vec![
                AccordionItem::new("ship", "When does it ship?").content(
                    gpui::div().child("Orders leave the warehouse next day."),
                ),
                AccordionItem::new("returns", "Can I return it?").content(
                    gpui::div().child("Within thirty days, in any condition."),
                ),
                AccordionItem::new("warranty", "Is there a warranty?")
                    .content(gpui::div().child("Two years, parts and labour.")),
            ])
            .id("accordion-faq")
            .variant(AccordionVariant::Surface),
        )
        .into_any_element()])

Default

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::{Accordion, AccordionItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;

let open = self.accordion_open.clone();
let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion-default")
        .expanded_keys(open.clone())
        .on_toggle(cx.listener(|this, key: &SharedString, _, cx| {
            toggle_key(&mut this.accordion_open, key);
            cx.notify();
        }))
        .into_any_element()])

Also calls toggle_key (included when you copy).

Surface

`radius(px)` rounds the `Surface` item card; the flush `Default` variant paints no card, so it is inert there. 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::{Accordion, AccordionItem, AccordionVariant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;

let open = self.accordion_open.clone();
let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion-surface")
        .variant(AccordionVariant::Surface)
        .radius(px(8.))
        .expanded_keys(open.clone())
        .on_toggle(cx.listener(|this, key: &SharedString, _, cx| {
            toggle_key(&mut this.accordion_open, key);
            cx.notify();
        }))
        .into_any_element()])

Also calls toggle_key (included when you copy).

Hidden separator

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::{Accordion, AccordionItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;

let open = self.accordion_open.clone();
let items = || {
    vec![
        AccordionItem::new("1", "What is HeroGPUI?")
            .content(gpui::div().child("A native Rust component library for GPUI.")),
        AccordionItem::new("2", "Does it support dark mode?")
            .content(gpui::div().child("Yes — every token has a light and dark value.")),
        AccordionItem::new("3", "Is it production ready?")
            .subtitle("Short answer")
            .content(
                gpui::div().child("The component set is complete; the API is settling."),
            ),
    ]
};

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![Accordion::new(items())
        .id("accordion-hidden-separator")
        .hide_separator(true)
        .expanded_keys(open)
        .on_toggle(cx.listener(|this, key: &SharedString, _, cx| {
            toggle_key(&mut this.accordion_open, key);
            cx.notify();
        }))
        .into_any_element()])

Also calls toggle_key (included when you copy).

Helpers used by these examples

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

toggle_key
fn toggle_key(set: &mut HashSet<SharedString>, key: &SharedString) {
    if !set.remove(key) {
        set.insert(key.clone());
    }
}
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

AccordionAccordionItem

Accordion 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 Accordion 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

Accordion styling
MethodValuesDescription
variant(AccordionVariant)AccordionVariant::Default | AccordionVariant::SurfaceSelects the flush or rounded surface treatment.
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: 29 entries ported, 4 partial, 10 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Accordion builders
BuilderValuesDefaultDescription
allows_multiple_expanded(bool)true | falsefalseAllows more than one item to remain expanded.
default_expanded_keys(keys)iterable of keys—Seeds the uncontrolled expanded-key set.
expanded_keys(keys)iterable of keys—The caller-owned expanded-key set.
on_expanded_change(callback)Fn(keys: Set<Key>) -> ()—Reports the complete proposed expanded-key set.
is_disabled(bool)true | falsefalseDisables every item trigger.
variant(AccordionVariant)AccordionVariant::Default | AccordionVariant::SurfaceAccordionVariant::DefaultSelects the flush or rounded surface treatment.
hide_separator(bool)true | falsefalseHides the rules between items.
new(items)PartialAnyElement—Typed AccordionItem values replace arbitrary compound children.
new(key, title)Key—Stable item identity used by every expanded and disabled set.
default_expanded(bool)true | falsefalseSeeds this item into the uncontrolled group set.
new(key, title) + subtitle(text) + indicator(render) + content(body)PartialAnyElement—Title, optional subtitle, indicator and body are typed composition seams.
new(key, title) + subtitle(text)PartialAnyElement | render closure—The title and subtitle are typed text; the indicator is independently replaceable.
on_toggle(callback)Fn() -> ()—Reports the key whose trigger was pressed.
content(body)AnyElement—Caller-provided panel body content.
indicator(render)AnyElementAnyElement::ChevronCustom indicator content receives the live item state.
Not ported10 HeroUI entries not ported
Accordion builders not ported
HeroUI v3Why it is not ported
Accordion.classNameArbitrary styles have no GPUI equivalent.
Accordion.renderRoot substitution has no GPUI equivalent.
Accordion.Item.classNameArbitrary item classes have no GPUI equivalent.
Accordion.Item.renderRoot substitution has no GPUI equivalent.
Accordion.Trigger.classNameArbitrary trigger classes have no GPUI equivalent.
Accordion.Trigger.renderRoot substitution has no GPUI equivalent.
Accordion.Panel.classNameArbitrary panel classes have no GPUI equivalent.
Accordion.Panel.renderRoot substitution has no GPUI equivalent.
Accordion.Indicator.classNameArbitrary indicator classes have no GPUI equivalent.
Accordion.Body.classNameArbitrary body classes have no GPUI equivalent.

Parts

Accordion parts
PartDescription
AccordionFull-width owner of item expansion and presentation. Give simultaneous accordions distinct id values when they reuse item keys.
AccordionItemKeyed item holding trigger and body configuration.
AccordionItemPartialHeading semantics are folded into the trigger row because GPUI has no heading accessibility node.
AccordionItemFocusable, pointer-pressable item header.
AccordionItemBuilt-in chevron or caller content receiving AccordionItemState.
AccordionItemMeasured body host that keeps closing content mounted through the 200ms exit transition.
AccordionItemPadded, muted body around caller content.

States

Accordion states
StateBuilderDescription
Expandedexpanded_keysShows the panel and supplies true to custom indicator content.
Hovered!is_open header.hover(default.soft)Only a closed enabled trigger receives the hover fill.
Focus visibletab_stop_handleKeyboard focus draws the inset status ring on the trigger.
Disabledis_disabledGroup, keyed-set and item flags remove press and focus while dimming the trigger.
Single expansionallows_multiple_expanded(false)Opening an item replaces the previous expanded set by default.
Multiple expansionallows_multiple_expanded(true)Opening an item preserves expanded siblings.