Components
Accordion
Let's you show and hide stuff. Uses the native HTML arrow, check out how to add your own custom marker.
interpolate-size.Partial supportMissing: interpolate-size.Anatomy
Accordion title
Explain more about the topic shown in the summary through supporting text.
<Accordion>Container element.
slot="summary"The always visible header.
slot="default"The collapsible content.
slot="actions"A group of actions, such as buttons.
Basics
Accordion
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. Pellentesque id sapien lacinia, venenatis est aliquam, dignissim elit. Suspendisse potenti. Cras ut ante in libero tempus sodales sed quis dolor.
---import { Accordion } from "@opui/astro"---
<Accordion> <Fragment slot="summary">Accordion</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. Pellentesque id sapien lacinia, venenatis est aliquam, dignissim elit. Suspendisse potenti. Cras ut ante in libero tempus sodales sed quis dolor. </p></Accordion><details class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-2" aria-controls="content-2">Accordion</summary> <!-- Content --> <div id="content-2" class="ui-content" role="region" aria-labelledby="summary-2" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. Pellentesque id sapien lacinia, venenatis est aliquam, dignissim elit. Suspendisse potenti. Cras ut ante in libero tempus sodales sed quis dolor. </p> </div> <!-- Actions --></details>Variants
Use the variant prop to change how it looks.
Text
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Elevated
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Outlined
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Tonal
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
---import { Accordion } from "@opui/astro"---
<Accordion> <Fragment slot="summary">Text</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p></Accordion>
<Accordion variant="elevated"> <Fragment slot="summary">Elevated</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p></Accordion>
<Accordion variant="outlined"> <Fragment slot="summary">Outlined</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p></Accordion>
<Accordion variant="tonal"> <Fragment slot="summary">Tonal</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p></Accordion><details class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-3" aria-controls="content-3">Text</summary> <!-- Content --> <div id="content-3" class="ui-content" role="region" aria-labelledby="summary-3" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --></details><details class="ui-accordion ui-card ui-elevated"> <!-- Summary --> <summary id="summary-4" aria-controls="content-4">Elevated</summary> <!-- Content --> <div id="content-4" class="ui-content" role="region" aria-labelledby="summary-4" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --></details><details class="ui-accordion ui-card ui-outlined"> <!-- Summary --> <summary id="summary-5" aria-controls="content-5">Outlined</summary> <!-- Content --> <div id="content-5" class="ui-content" role="region" aria-labelledby="summary-5" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --></details><details class="ui-accordion ui-card ui-tonal"> <!-- Summary --> <summary id="summary-6" aria-controls="content-6">Tonal</summary> <!-- Content --> <div id="content-6" class="ui-content" role="region" aria-labelledby="summary-6" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --></details>Accordion group
Group multiple accordions by wrapping them in a Cardcomponent with role="group". To theme the entire group, apply the variant prop to the parent container.
Accordion title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Accordion title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Accordion title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
---import { Accordion } from "@opui/astro"import { Card } from "@opui/astro"---
<Card variant="outlined" role="group"> <Accordion> <Fragment slot="summary">Accordion title</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </Accordion> <Accordion> <Fragment slot="summary">Accordion title</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </Accordion> <Accordion> <Fragment slot="summary">Accordion title</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </Accordion></Card><div class="ui-card ui-outlined" role="group"> <details class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-7" aria-controls="content-7">Accordion title</summary> <!-- Content --> <div id="content-7" class="ui-content" role="region" aria-labelledby="summary-7" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --> </details> <details class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-8" aria-controls="content-8">Accordion title</summary> <!-- Content --> <div id="content-8" class="ui-content" role="region" aria-labelledby="summary-8" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --> </details> <details class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-9" aria-controls="content-9">Accordion title</summary> <!-- Content --> <div id="content-9" class="ui-content" role="region" aria-labelledby="summary-9" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --> </details></div>Mutually exclusive
Set the name prop to allow only one accordion in a group to be open at a time.
Accordion title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Accordion title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
Accordion title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo.
---import { Accordion } from "@opui/astro"import { Card } from "@opui/astro"---
<Card variant="outlined" role="group"> <Accordion name="example-group"> <Fragment slot="summary">Accordion title</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </Accordion> <Accordion name="example-group"> <Fragment slot="summary">Accordion title</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </Accordion> <Accordion name="example-group"> <Fragment slot="summary">Accordion title</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </Accordion></Card><div class="ui-card ui-outlined" role="group"> <details name="example-group" class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-10" aria-controls="content-10"> Accordion title </summary> <!-- Content --> <div id="content-10" class="ui-content" role="region" aria-labelledby="summary-10" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --> </details> <details name="example-group" class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-11" aria-controls="content-11"> Accordion title </summary> <!-- Content --> <div id="content-11" class="ui-content" role="region" aria-labelledby="summary-11" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --> </details> <details name="example-group" class="ui-accordion ui-card"> <!-- Summary --> <summary id="summary-12" aria-controls="content-12"> Accordion title </summary> <!-- Content --> <div id="content-12" class="ui-content" role="region" aria-labelledby="summary-12" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. </p> </div> <!-- Actions --> </details></div>Actions
Include interactive elements in the header by using the .ui-actions class.
Accordion with actions
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare.
---import { Accordion } from "@opui/astro"import { Button } from "@opui/astro"---
<Accordion open variant="elevated"> <Fragment slot="summary">Accordion with actions</Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. </p> <Fragment slot="actions"> <Button>Cancel</Button> <Button>Agree</Button> </Fragment></Accordion><details class="ui-accordion ui-card ui-elevated" open> <!-- Summary --> <summary id="summary-13" aria-controls="content-13"> Accordion with actions </summary> <!-- Content --> <div id="content-13" class="ui-content" role="region" aria-labelledby="summary-13" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. </p> </div> <!-- Actions --> <div class="ui-actions"> <button class="ui-button">Cancel</button ><button class="ui-button">Agree</button> </div></details>Custom marker
Replace the default marker by adding an SVG inside the summary.
Custom marker
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. Pellentesque id sapien lacinia, venenatis est aliquam, dignissim elit. Suspendisse potenti. Cras ut ante in libero tempus sodales sed quis dolor.
---import { Accordion } from "@opui/astro"---
<Accordion variant="outlined"> <Fragment slot="summary">Custom marker</Fragment> <Fragment slot="marker"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" ><path fill="currentColor" d="M4.293 8.293a1 1 0 0 1 1.414 0L12 14.586l6.293-6.293a1 1 0 1 1 1.414 1.414l-7 7a1 1 0 0 1-1.414 0l-7-7a1 1 0 0 1 0-1.414" ></path></svg > </Fragment> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. Pellentesque id sapien lacinia, venenatis est aliquam, dignissim elit. Suspendisse potenti. Cras ut ante in libero tempus sodales sed quis dolor. </p></Accordion><details class="ui-accordion ui-card ui-outlined"> <!-- Summary --> <summary id="summary-14" aria-controls="content-14"> Custom marker<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" > <path fill="currentColor" d="M4.293 8.293a1 1 0 0 1 1.414 0L12 14.586l6.293-6.293a1 1 0 1 1 1.414 1.414l-7 7a1 1 0 0 1-1.414 0l-7-7a1 1 0 0 1 0-1.414" ></path> </svg> </summary> <!-- Content --> <div id="content-14" class="ui-content" role="region" aria-labelledby="summary-14" > <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus, lacus ex vestibulum libero, ac mollis neque ante id justo. Nam tempor euismod nisi ac ornare. Pellentesque id sapien lacinia, venenatis est aliquam, dignissim elit. Suspendisse potenti. Cras ut ante in libero tempus sodales sed quis dolor. </p> </div> <!-- Actions --></details>Accessibility
The WAI-ARIA guidelines for accordions recommend:
summaryelement- adding id and aria-controls
- adding aria-expanded (if using JS)
- content wrapper
- adding id, role and aria-labelledby
API
Accordion API
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | Groups accordions so only one of them can be open at a time. |
open | boolean | false | Whether the accordion is open. |
variant | "default", "outlined", "elevated", "tonal" | "default" | The variant to use. |
Slots
| Slot | Description |
|---|---|
actions | A group of actions, such as buttons. |
default | The collapsible content. |
marker | A custom marker that replaces the native one. |
summary | The always visible header. |
Group accordions in a <Card role="group">. Set its variant to theme the whole group.
Browser support
interpolate-size.Partial supportMissing: interpolate-size.See also the full browser support guide.
Installation
Dependencies
@layer components.extended { :where(details.ui-accordion) { --_motion: var(--motion, 1); --_actions-gap: var(--size-1); --_actions-margin-block-start: var(--size-3); --_actions-padding-block-end: var(--size-1); --_actions-padding-inline: var(--size-3) var(--size-1); --_bg-color: transparent; --_border-color: transparent; --_border-radius: var(--border-radius, 4px); --_border-width: 0; --_content-padding-block: var(--size-2) var(--size-3); --_content-padding-inline: var(--_padding-inline); --_margin-inline: var(--size-1); --_padding-inline: initial; --_shadow: none; --_summary-padding-block: var(--size-3); --_summary-padding-inline: var(--_padding-inline); --_svg-transition: calc(0.2s * var(--_motion)) var(--ease-1); --_transition: calc(0.2s * var(--_motion));
background-color: var(--_bg-color); border: var(--_border-width) solid var(--_border-color); border-radius: var(--_border-radius); box-shadow: var(--_shadow); display: block; margin-inline: var(--_margin-inline); transition: all var(--_transition) ease-out;
/* Accordion animation */ /* https://nerdy.dev/open-and-close-transitions-for-the-details-element */ interpolate-size: allow-keywords;
&.ui-elevated { --_bg-color: var(--surface-elevated); --_margin-inline: 0; --_padding-inline: var(--size-3); --_shadow: var(--shadow-3); }
&.ui-outlined { --_bg-color: var(--surface-default); --_border-color: var(--border-color); --_border-width: 1px; --_margin-inline: 0; --_padding-inline: var(--size-3); }
&.ui-tonal { --_bg-color: var(--surface-tonal); --_margin-inline: 0; --_padding-inline: var(--size-3); }
&::details-content { block-size: 0; opacity: 0; overflow-y: clip; transition: content-visibility var(--_transition) allow-discrete, opacity var(--_transition), block-size var(--_transition); }
&[open]::details-content { block-size: auto; opacity: 1; }
/* Layout */ & > summary { background-color: inherit; cursor: pointer; font-weight: 700; padding-block: var(--_summary-padding-block); padding-inline: var(--_summary-padding-inline); user-select: none;
&:has(svg) { align-items: center; display: flex; justify-content: space-between; list-style: none;
&::marker, &::-webkit-details-marker { display: none; } }
svg { transition: rotate var(--_svg-transition); } }
&[open] > summary svg { rotate: 180deg; }
& > .ui-content { margin-block-start: 0; padding-block: var(--_content-padding-block); padding-inline: var(--_content-padding-inline); }
& > .ui-actions { display: flex; gap: var(--_actions-gap); justify-content: end; margin-block-start: var(--_actions-margin-block-start); padding-block-end: var(--_actions-padding-block-end); padding-inline: var(--_actions-padding-inline); } }
/* Accordion group */ :where(.ui-card[role="group"]:has(details.ui-accordion)) { --_group-radius: var(--border-radius); --_gutter-color: var(--border-color); --_item-padding-inline: var(--size-3); --_item-summary-padding-inline: var(--_item-padding-inline);
display: block;
&.ui-card { &:not(.ui-tonal, .ui-outlined, .ui-elevated) { --_item-summary-padding-inline: 0; } }
& > .ui-content { margin-block: 0; padding: 0; }
& > details.ui-accordion { --_border-color: transparent; --_border-width: 0; --_margin-inline: 0; --_padding-inline: var(--_item-padding-inline); --_shadow: none; --_summary-padding-inline: var(--_item-summary-padding-inline);
/* Border between accordion items */ & + & { border-block-start: 1px solid var(--_gutter-color); border-radius: 0; }
/* First item */ &:first-of-type { border-start-end-radius: var(--_group-radius); border-start-start-radius: var(--_group-radius);
summary { border-start-end-radius: var(--_group-radius); border-start-start-radius: var(--_group-radius); } }
/* Last item */ &:last-of-type { border-end-end-radius: var(--_group-radius); border-end-start-radius: var(--_group-radius);
summary { border-end-end-radius: var(--_group-radius); border-end-start-radius: var(--_group-radius); } } } }}@layer components.root { :where(.ui-card) { --_bg-tonal: var(--surface-tonal); --_bg-elevated: var(--surface-elevated); --_bg-surface: var(--surface-default); --_border-color: var(--border-color); --_card-bg-color: var(--_bg-surface); --_card-border-color: transparent; --_card-border-width: 0;
--_card-shadow: none; --_shadow-light: var(--shadow-3); --_shadow-dark: var(--shadow-4); --_shadow-elevated: var(--_shadow-light);
@container style(--color-scheme: dark) { --_shadow-elevated: var(--_shadow-dark); }
background-color: var(--_card-bg-color); border-color: var(--_card-border-color); border-radius: var(--border-radius); border-style: solid; border-width: var(--_card-border-width); box-shadow: var(--_card-shadow); display: flex; flex-direction: column; gap: var(--size-3); overflow: hidden; padding-inline: 0; position: relative;
/* Variants */ &.ui-text { --_card-bg-color: transparent; --_card-border-color: transparent; --_card-border-width: 0; --_card-shadow: none; }
&.ui-tonal { --_card-bg-color: var(--_bg-tonal); --_card-border-width: 1px; }
&.ui-elevated { --_card-bg-color: var(--_bg-elevated); --_card-shadow: var(--_shadow-elevated); }
&.ui-outlined { --_card-bg-color: var(--_bg-surface); --_card-border-color: var(--_border-color); --_card-border-width: 1px; }
& > :where(hgroup, .ui-content) { padding-inline: var(--size-3);
&:last-child { padding-block-end: var(--size-3); } }
/* Header */ & > hgroup { padding-block: var(--size-3) 0;
/* Top paragraph */ & > p:first-of-type:first-child { font-size: var(--font-size-0); line-height: 1.3; }
:where(h1, h2, h3, h4, h5, h6):last-child { margin-block-end: 0; }
/* Bottom paragraph */ & > p:last-of-type:last-child:not(:first-child) { font-size: var(--font-size-1); } }
/* Content */ & > .ui-content:where(:only-child, :first-child) { padding-block: var(--size-3) var(--size-4); }
/* Actions */ & > .ui-actions { display: flex; gap: var(--size-2); margin-block: var(--size-2) 0; padding-block-end: var(--size-2); padding-inline: var(--size-3);
&:has(.ui-button:first-child[class="ui-button"]) { padding-inline: var(--size-1) var(--size-3); }
&:has(.ui-button:not([class="ui-button"])) { padding-block-end: var(--size-2); }
/* Alignment */ &.ui-align-end { justify-content: end;
&:has(.ui-button:first-child[class="ui-button"]) { padding-inline: var(--size-3) var(--size-1); } } } }}