Accordion
The accordion component is for displaying collapsible content.
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<Accordion.Root> <Accordion.Summary> <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> </Accordion.Summary> <Accordion.Content> <p><strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </Accordion.Content></Accordion.Root>
<details data-component="Accordion" class="group/details flex flex-col border-t last-of-type:border-b"> <summary class="group/summary flex cursor-pointer items-center justify-between py-24 marker:content-none focus:outline-none" > <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> <span class="relative size-28 shrink-0 rounded border-2 border-transparent text-gray-600 transition-all group-hover/summary:bg-sky-200 group-focus-visible/summary:border-sky-700 group-active/summary:bg-sky-300 dark:group-hover/summary:bg-gray-800 dark:group-focus-visible/summary:border-gray-600 dark:group-active/summary:bg-gray-700" > <span class="absolute inset-0 m-auto h-4 w-20 bg-[var(--sl-color-gray-1)]" ></span> <span class="absolute inset-0 m-auto h-4 w-20 rotate-90 bg-[var(--sl-color-gray-1)] transition-transform duration-500 group-open/details:rotate-[360deg]" ></span> </span> </summary> <div class="mb-24"> <p> <strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> </div></details>
Anatomy
This component is comprised of several elements that work together to create an accordion. The following are the elements that make up the Accordion component.
---import Accordion from '@/components/elements/accordion/Accordion.ts'---
<Accordion.Root> <Accordion.Summary /> <Accordion.Content /></Accordion.Root>
API Reference
Root
The root element of the Accordion component. This element wraps the summary and content elements.
Summary
The summary is the element that is always visible and is used to trigger the accordion to open and close. This component is based on the <summary>
element and can take in any additional HTML attributes. The following are the custom props that can be passed to the component.
Prop | Type | Default |
---|---|---|
hideMarker | boolean | false |
Content
The content is the element that is hidden by default and is shown when the accordion is opened. By default, this component is based on the <div>
element but can be changed to any HTML element by adjusting the as
property.
Prop | Type | Default |
---|---|---|
as | TagType | div |
Accessibility
The <Accordion>
component leverages the <details>
and <summary>
elements which are natively accessible. The following are some additional accessibility features that are built into the component:
- The
<summary>
element has both a hover and focus state that can be styled to indicate that it is interactive for both keyboard and mouse users.
Examples
Custom Summary & Content
You can customize the summary and content elements to fit the needs of your project. In this example, we have replaced the marker in the summary and provided an image as the content.
Lorem ipsum dolor sit amet
Expand
<Accordion.Root> <Accordion.Summary hideMarker> <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> <div class="btn-contained group-open/details:hidden group-focus-visible/summary:ring-4">Expand</div> <div class="btn-contained hidden group-open/details:flex group-focus-visible/summary:ring-4">Collapse</div> </Accordion.Summary> <Accordion.Content as="img" src="https://plus.unsplash.com/premium_photo-1675629118284-c9eb039df8cd?q=80&w=2576&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="a snow covered field with trees and mountains in the background" class="w-full object-cover"/></Accordion.Root>
<details data-component="Accordion" class="group/details flex flex-col border-t last-of-type:border-b" open=""> <summary class="group/summary flex cursor-pointer items-center justify-between py-24 marker:content-none focus:outline-none" > <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> <div class="btn-contained group-open/details:hidden group-focus-visible/summary:ring-4" > Expand </div> <div class="btn-contained hidden group-open/details:flex group-focus-visible/summary:ring-4" > Collapse </div> </summary> <img src="https://plus.unsplash.com/premium_photo-1675629118284-c9eb039df8cd?q=80&w=2576&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="a snow covered field with trees and mountains in the background" class="mb-24 w-full object-cover" /></details>
Accordion List
Accordions can be stacked to display a list of items that can be expanded and collapsed.
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<Accordion.Root> <Accordion.Summary> <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> </Accordion.Summary> <Accordion.Content> <p><strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </Accordion.Content></Accordion.Root><Accordion.Root> <Accordion.Summary> <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> </Accordion.Summary> <Accordion.Content> <p><strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </Accordion.Content></Accordion.Root><Accordion.Root> <Accordion.Summary> <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> </Accordion.Summary> <Accordion.Content> <p><strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </Accordion.Content></Accordion.Root>
<details data-component="Accordion" class="group/details flex flex-col border-t last-of-type:border-b"> <summary class="group/summary flex cursor-pointer items-center justify-between py-24 marker:content-none focus:outline-none" > <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> <span class="relative size-28 shrink-0 rounded border-2 border-transparent text-gray-600 transition-all group-hover/summary:bg-sky-200 group-focus-visible/summary:border-sky-700 group-active/summary:bg-sky-300 dark:group-hover/summary:bg-gray-800 dark:group-focus-visible/summary:border-gray-600 dark:group-active/summary:bg-gray-700" > <span class="absolute inset-0 m-auto h-4 w-20 bg-[var(--sl-color-gray-1)]" ></span> <span class="absolute inset-0 m-auto h-4 w-20 rotate-90 bg-[var(--sl-color-gray-1)] transition-transform duration-500 group-open/details:rotate-[360deg]" ></span> </span> </summary> <div class="mb-24"> <p> <strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> </div></details><details data-component="Accordion" class="group/details flex flex-col border-t last-of-type:border-b"> <summary class="group/summary flex cursor-pointer items-center justify-between py-24 marker:content-none focus:outline-none" > <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> <span class="relative size-28 shrink-0 rounded border-2 border-transparent text-gray-600 transition-all group-hover/summary:bg-sky-200 group-focus-visible/summary:border-sky-700 group-active/summary:bg-sky-300 dark:group-hover/summary:bg-gray-800 dark:group-focus-visible/summary:border-gray-600 dark:group-active/summary:bg-gray-700" > <span class="absolute inset-0 m-auto h-4 w-20 bg-[var(--sl-color-gray-1)]" ></span> <span class="absolute inset-0 m-auto h-4 w-20 rotate-90 bg-[var(--sl-color-gray-1)] transition-transform duration-500 group-open/details:rotate-[360deg]" ></span> </span> </summary> <div class="mb-24"> <p> <strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> </div></details><details data-component="Accordion" class="group/details flex flex-col border-t last-of-type:border-b"> <summary class="group/summary flex cursor-pointer items-center justify-between py-24 marker:content-none focus:outline-none" > <h2 class="text-lg">Lorem ipsum dolor sit amet</h2> <span class="relative size-28 shrink-0 rounded border-2 border-transparent text-gray-600 transition-all group-hover/summary:bg-sky-200 group-focus-visible/summary:border-sky-700 group-active/summary:bg-sky-300 dark:group-hover/summary:bg-gray-800 dark:group-focus-visible/summary:border-gray-600 dark:group-active/summary:bg-gray-700" > <span class="absolute inset-0 m-auto h-4 w-20 bg-[var(--sl-color-gray-1)]" ></span> <span class="absolute inset-0 m-auto h-4 w-20 rotate-90 bg-[var(--sl-color-gray-1)] transition-transform duration-500 group-open/details:rotate-[360deg]" ></span> </span> </summary> <div class="mb-24"> <p> <strong>Lorem ipsum dolor</strong> <a href="#">sit amet</a>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> </div></details>
Astro Component
It’s recommended that an element like this is made as a reusable component. In this case, we have put together the following Astro compound component to serve as a blueprint for how you could possibly set up an Accordion component, and what properties to consider.
import Root from './Root.astro'import Summary from './Summary.astro'import Content from './Content.astro'
export default Object.assign({ Root, Summary, Content })
---import type { HTMLAttributes } from 'astro/types'
interface Props extends HTMLAttributes<'details'> {}
const { class: className, ...attrs } = Astro.props---
<details data-component="Accordion" class:list={[ 'group/details flex flex-col border-t last-of-type:border-b', className, ]} {...attrs}> <slot /></details>
---import type { HTMLAttributes } from 'astro/types'
interface Props extends HTMLAttributes<'summary'> { /** Hide expand/collapse marker for the summary element */ hideMarker?: boolean}
const { hideMarker = false, class: className, ...attrs } = Astro.props---
<summary class:list={[ 'group/summary flex cursor-pointer items-center justify-between py-24 marker:content-none focus:outline-none', className, ]} {...attrs}> <slot />
{ !hideMarker && ( <span class:list={[ 'relative size-28 shrink-0 rounded border-2 border-transparent text-gray-600 transition-all', 'group-hover/summary:bg-sky-200 group-focus-visible/summary:border-sky-700 group-active/summary:bg-sky-300', 'dark:group-hover/summary:bg-gray-800 dark:group-focus-visible/summary:border-gray-600 dark:group-active/summary:bg-gray-700', ]} > <span class="absolute inset-0 m-auto h-4 w-20 bg-[var(--sl-color-gray-1)]" /> <span class="absolute inset-0 m-auto h-4 w-20 rotate-90 bg-[var(--sl-color-gray-1)] transition-transform duration-500 group-open/details:rotate-[360deg]" /> </span> ) }</summary>
---import type { HTMLTag, Polymorphic } from 'astro/types'
type Props<Tag extends HTMLTag> = Polymorphic<{ /** The HTML tag to render for the component. By default the component will render as a `div`. */ as: Tag}>
const { as: Tag, class: className, ...attrs } = Astro.props
const Element = Tag ? Tag : 'div'---
<Element class:list={['mb-24', className]} {...attrs}> <slot /></Element>