Expansion Panel
Expansion panels show summary content and reveal additional detail on tap, for progressive disclosure inside a card, list, or table.
Overview
Expansion panels are lightweight containers that support progressive disclosure by highlighting important summary information and revealing additional details when expanded. They help focus designs by displaying critical information first while keeping secondary details accessible but not overwhelming. Expansion panels can stand alone or be integrated within various components including cards, navigation items, lists, and tables.
Usage
forge-expansion-panel reveals its default-slot content on toggle, through open, orientation, trigger (an external button’s id), and name — pairing a header slot or an external button with a forge-open-icon for the chevron indicator. Give several panels the same name to form an accordion group where opening one closes the others, or wrap them in forge-accordion for the same effect scoped to a specific set of panels. Use it for progressive disclosure inside a card, list, or table row. Content inside a closed panel stays findable by the browser’s native find-in-page search — a match automatically opens the panel to reveal it.
Rules
Basic usage
A header-slotted button toggles the panel; a forge-open-icon inside it rotates to reflect the open state.
A single expansion panel toggled by its header button.
<forge-expansion-panel orientation="vertical" animation-type="default" trigger open-icon name>
<button slot="header" type="button" aria-controls="content">
Toggle
</button>
<div id="content" role="group">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem
nulla quod corporis similique consequuntur facere laborum veniam error eius.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem
nulla quod corporis similique consequuntur facere laborum veniam error eius.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem
nulla quod corporis similique consequuntur facere laborum veniam error eius.
</p>
</div>
</forge-expansion-panel>In a card
Nest an expansion panel inside a card to expand the whole card downward instead of navigating away.
An expansion panel inside a card, with a custom header layout.
<forge-card>
<forge-expansion-panel orientation="vertical" animation-type="default" trigger open-icon name>
<div slot="header" role="button" tabindex="0" style="display: flex; justify-content: space-between;">
<div>
Header text
</div>
<forge-open-icon orientation="vertical" rotation="full">
</forge-open-icon>
</div>
<p id="forge-expansion-panel-content-jkpw0">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem nulla
quod corporis similique consequuntur facere laborum veniam error eius.
</p>
</forge-expansion-panel>
</forge-card>Named group
Give several panels the same name to form an accordion group — opening one automatically closes the others sharing that name.
Three panels sharing a name, so opening one closes the others.
<forge-expansion-panel name="panel-group" orientation="vertical" animation-type="default" trigger open-icon>
<button slot="header">
Panel 1
</button>
<p id="forge-expansion-panel-content-6mo6i">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem nulla
quod corporis similique consequuntur facere laborum veniam error eius.
</p>
</forge-expansion-panel>
<forge-expansion-panel name="panel-group" orientation="vertical" animation-type="default" trigger open-icon>
<button slot="header">
Panel 2
</button>
<p id="forge-expansion-panel-content-vyl5g">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem nulla
quod corporis similique consequuntur facere laborum veniam error eius.
</p>
</forge-expansion-panel>
<forge-expansion-panel name="panel-group" orientation="vertical" animation-type="default" trigger open-icon>
<button slot="header">
Panel 3
</button>
<p id="forge-expansion-panel-content-1395g">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Veritatis minus ut illum corporis incidunt quod temporibus consequatur rem! Libero rem nulla
quod corporis similique consequuntur facere laborum veniam error eius.
</p>
</forge-expansion-panel>External trigger
Set trigger to the id of a button anywhere else in the DOM to toggle the panel from outside its own header slot — it also wires up that button’s aria-expanded and aria-controls automatically. The referenced element needs to already be in the DOM when the panel initializes, so in an incrementally-rendered UI where that’s not guaranteed, set the triggerElement property to a direct element reference instead — it isn’t subject to that timing requirement.
A button outside the panel toggles it via the trigger attribute.
<button id="button-id" aria-controls="forge-expansion-panel-content-t63ev">
Toggle
</button>
<forge-card>
<forge-expansion-panel trigger="button-id" orientation="vertical" animation-type="default" open-icon name>
<div slot="header" role="button" tabindex="0" style="display: flex; justify-content: space-between;">
<div>
Header text
</div>
<forge-open-icon orientation="vertical" rotation="full">
</forge-open-icon>
</div>
<p id="forge-expansion-panel-content-t63ev">
Content text
</p>
</forge-expansion-panel>
</forge-card>Open icon sync
A forge-open-icon placed in the header slot, or nested inside the panel’s trigger button, automatically syncs its rotation to the panel’s open state — no wiring required. To sync an open icon that lives elsewhere in the document, set openIcon to its id (subject to the same DOM-timing requirement as trigger), or set openIconElement to a direct reference when timing can’t be guaranteed.
Accordion
Wrap a set of panels in forge-accordion instead of giving them a shared name when you want the “only one open at a time” behavior scoped to a specific group of panels, regardless of name. It adds no visual styling of its own — pair it with a divider between panels for visual separation.
An accordion wrapping three panels, with dividers between them.
<forge-accordion class="w-[200px] inline-block">
<forge-expansion-panel orientation="vertical" animation-type="default" trigger open-icon name>
<div slot="header" class="flex justify-between items-center">
Panel One
<forge-open-icon orientation="vertical" rotation="full">
</forge-open-icon>
</div>
<div id="forge-expansion-panel-content-dmk3t">
Panel One Content
</div>
</forge-expansion-panel>
<forge-divider>
</forge-divider>
<forge-expansion-panel orientation="vertical" animation-type="default" trigger open-icon name>
<div slot="header" class="flex justify-between items-center">
Panel Two
<forge-open-icon orientation="vertical" rotation="full">
</forge-open-icon>
</div>
<div id="forge-expansion-panel-content-1ogog">
Panel Two Content
</div>
</forge-expansion-panel>
<forge-divider>
</forge-divider>
<forge-expansion-panel orientation="vertical" animation-type="default" trigger open-icon name>
<div slot="header" class="flex justify-between items-center">
Panel Three
<forge-open-icon orientation="vertical" rotation="full">
</forge-open-icon>
</div>
<div id="forge-expansion-panel-content-kxzv3">
Panel Three Content
</div>
</forge-expansion-panel>
</forge-accordion>Expansion panels provide progressive disclosure of content.
import "@tylertech/forge/expansion-panel";| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
open | open | boolean | false | Whether the panel is open or closed. |
orientation | orientation | ExpansionPanelOrientation | 'vertical' | The orientation of the panel. |
animationType | animationType | ExpansionPanelAnimationType | 'default' | The type of animation to use when opening/closing the panel. |
trigger | trigger | string | '' | The id of the button that the expansion panel should be toggled by. |
triggerElement | — | HTMLElement | null | null | The button that the expansion panel should be toggled by. |
openIcon | openIcon | string | '' | The id of the `<forge-open-icon>` that the expansion panel should toggle. |
openIconElement | — | IOpenIconComponent | null | null | The `<forge-open-icon>` that the expansion panel should toggle. |
name | name | string | '' | The expansion panel's name. Expansion panels that share the same name form an accordion group where only one panel may be open at a time. |
| Name | Type | Description |
|---|---|---|
forge-expansion-panel-toggle | CustomEvent<boolean> | Event fired when the panel is toggled open or closed. |
forge-expansion-panel-animation-complete | CustomEvent<boolean> | Event fired when the panel has finished animating when toggling. |
| Name | Description |
|---|---|
(default) | The content of the panel. |
header | The header of the panel. This is deprecated, prefer using the trigger property instead, or manually associating a button with the panel. |
| Name | Description |
|---|---|
::part(root) | The root element of the panel. |
::part(header) | The header of the panel. |
::part(content) | The content of the panel. |
| Name | Description |
|---|---|
--forge-expansion-panel-animation-duration | The duration of the open/close animation. |
--forge-expansion-panel-animation-easing | The easing function of the open/close animation. |
| Name | Returns | Description |
|---|---|---|
toggle() | void |
Trigger markup
Use a real <button> as the interactive element that toggles the panel, and give it a clear label. Don’t place complex or interactive content inside that button — if the header needs more than the toggle itself, put a plain button in the header alongside that content instead of wrapping it all in one.
Make sure the trigger stays keyboard-focusable and can be activated from the keyboard, not just by pointer.
ARIA wiring
- Set
aria-expandedon the trigger button to reflect the panel’s open state, and update it when the panel opens or closes. - Set
aria-controlson the trigger button to reference the id of the expandable content. - Give the expandable content itself
role="group"(or another non-generic role).
Associating a button via the trigger attribute manages aria-expanded and aria-controls for you automatically:
<forge-expansion-panel trigger="button-id">
<button slot="header" id="button-id">Toggle panel</button>
<div id="expandable-content" role="group">Expandable content</div>
</forge-expansion-panel>
Interactive elements in the header
Placing another interactive element (like an icon button) in the header slot alongside the trigger needs the same forge-ignore handling described in nested interactive elements — otherwise clicking it will also toggle the panel.