Accordion
Vertically collapsing panels.
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.
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.
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_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.
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()])An element expression with window: &mut Window and cx: &mut App in scope.
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()])An element expression with window: &mut Window and cx: &mut App in scope.
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()])An element expression with window: &mut Window and cx: &mut App in scope.
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(),
])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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).
An element expression with window: &mut Window and cx: &mut App in scope.
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()])An element expression with window: &mut Window and cx: &mut App in scope.
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()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
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).
`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>.
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).
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.
fn toggle_key(set: &mut HashSet<SharedString>, key: &SharedString) {
if !set.remove(key) {
set.insert(key.clone());
}
}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
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
| Method | Values | Description |
|---|---|---|
variant(AccordionVariant) | AccordionVariant::Default | AccordionVariant::Surface | Selects the flush or rounded surface treatment. |
// 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
| Builder | Values | Default | Description |
|---|---|---|---|
allows_multiple_expanded( | true | | false | Allows more than one item to remain expanded. |
default_expanded_keys( | iterable of keys | — | Seeds the uncontrolled expanded-key set. |
expanded_keys( | iterable of keys | — | The caller-owned expanded-key set. |
on_expanded_change( | Fn( | — | Reports the complete proposed expanded-key set. |
is_disabled( | true | | false | Disables every item trigger. |
variant( | AccordionVariant:: | AccordionVariant:: | Selects the flush or rounded surface treatment. |
hide_separator( | true | | false | Hides the rules between items. |
new(Partial | AnyElement | — | Typed AccordionItem values replace arbitrary compound children. |
new( | Key | — | Stable item identity used by every expanded and disabled set. |
default_expanded( | true | | false | Seeds this item into the uncontrolled group set. |
new(Partial | AnyElement | — | Title, optional subtitle, indicator and body are typed composition seams. |
new(Partial | AnyElement | | — | The title and subtitle are typed text; the indicator is independently replaceable. |
on_toggle( | Fn( | — | Reports the key whose trigger was pressed. |
content( | AnyElement | — | Caller-provided panel body content. |
indicator( | AnyElement | AnyElement:: | Custom indicator content receives the live item state. |
Not ported10 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
Accordion.className | Arbitrary styles have no GPUI equivalent. |
Accordion.render | Root substitution has no GPUI equivalent. |
Accordion.Item.className | Arbitrary item classes have no GPUI equivalent. |
Accordion.Item.render | Root substitution has no GPUI equivalent. |
Accordion.Trigger.className | Arbitrary trigger classes have no GPUI equivalent. |
Accordion.Trigger.render | Root substitution has no GPUI equivalent. |
Accordion.Panel.className | Arbitrary panel classes have no GPUI equivalent. |
Accordion.Panel.render | Root substitution has no GPUI equivalent. |
Accordion.Indicator.className | Arbitrary indicator classes have no GPUI equivalent. |
Accordion.Body.className | Arbitrary body classes have no GPUI equivalent. |
Parts
| Part | Description |
|---|---|
Accordion | Full-width owner of item expansion and presentation. Give simultaneous accordions distinct id values when they reuse item keys. |
AccordionItem | Keyed item holding trigger and body configuration. |
AccordionItemPartial | Heading semantics are folded into the trigger row because GPUI has no heading accessibility node. |
AccordionItem | Focusable, pointer-pressable item header. |
AccordionItem | Built-in chevron or caller content receiving AccordionItemState. |
AccordionItem | Measured body host that keeps closing content mounted through the 200ms exit transition. |
AccordionItem | Padded, muted body around caller content. |
States
| State | Builder | Description |
|---|---|---|
Expanded | expanded_keys | Shows 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 visible | tab_stop_handle | Keyboard focus draws the inset status ring on the trigger. |
Disabled | is_disabled | Group, keyed-set and item flags remove press and focus while dimming the trigger. |
Single expansion | allows_multiple_expanded(false) | Opening an item replaces the previous expanded set by default. |
Multiple expansion | allows_multiple_expanded(true) | Opening an item preserves expanded siblings. |