Skip to content

Accordion

A commonly used interface component is an Accordion.

The user sees a list of titles with an icon opposite the title text. Clicking on the title smoothly reveals additional content directly below the title. The icon rotates to indicate the open/closed state of the control.

An example accordion control written in Hyperscript is shown below.

Example

Sample Accordion Control

Description

Accordions conserve screen real estate while still allowing considerable content to be displayed when needed.

To implement this control you need:

  1. A header (with a clickable title)
  2. A body (with hidden content that can be revealed)

In the collapsed state, the height of the body is set to 0. In response to a click event in the header, the body smoothly transitions to an open state by setting a final height and a transition animation. From a programming standpoint, the key is calculating the body’s final height based on the proposed content.

If too much content is revealed then a huge vertical shift occurs on the page which is distracting. Thus it is best to set a maxHeight after which extra content simply scrolls vertically.

The source code to create the example accordion is shown next with highlights indicating the Astro used. The complete source code for each component is presented in the Code section.

UsingAnAccordion.astro
----
import AccordionGroup_A from '../../../../components/AccordionGroup_A.astro'
import AccordionItem_A from '../../../../components/AccordionItem_A.astro'
const tw - {
p: 'pb-3'
}
---
<AccordionGroup_A closeSiblings={true} styles='mx-4'>
<AccordionItem_A id='accordion-htmx' caption="HTMX" >
<p class={tw.p}>
HTMX extends html with a suite of attributes giving any element the ability to send and respond to AJAX request.
</p>
</AccordionItem_A>
<AccordionItem_A id='accordion-hyperscript' caption='Hyperscript'>
<p class={tw.p}>
Hyperscript is a front-end scripting language which leverages robust event handling to bring dynamic user interfaces to the browser.
</p>
<p class={tw.p}>
Hyperscript syntax is based on Dan Winkler's HyperTalk language made famous by Apple's HyperCard program. The syntax boasts an English-prose like grammar which eases the learning curve for new programmers.
</p>
</AccordionItem_A>
<AccordionItem_A id='accordion-tailwind' caption='Tailwind CSS'>
<p class={tw.p}>
The Tailwind CSS library provides a rich ecosystem of utility classes that simplify responsive design by using prefixes to replace media queries and emphasizing mobile first design.
</p>
<p class={tw.p}>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Commodi, unde nostrum itaque aut explicabo deserunt vel sunt alias? Atque, a eveniet ad rerum labore deleniti tempora nisi commodi officiis quis eius perferendis.
</p>
<p class={tw.p}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis consectetur illum quis ipsam quos quidem eligendi ex blanditiis nihil temporibus veniam adipisci deleniti modi, facilis amet fugiat incidunt atque neque aut qui quia reprehenderit magni fugit aliquam. Hic dolorem consectetur qui libero eius commodi natus, quod fuga tenetur, non doloremque unde at impedit vero. Beatae quas excepturi dignissimos est assumenda quidem ullam quisquam labore! Quaerat veritatis ex sint similique veniam quidem numquam doloremque officiis et accusantium! Non nesciunt ea itaque explicabo ipsa blanditiis iusto, est deserunt inventore voluptas dolorem possimus at exercitationem aliquid, distinctio similique quae! Perferendis ipsam excepturi repudiandae.
</p>
</AccordionItem_A>
<AccordionItem_A id='accordion-astro' caption='Astro'>
<p class={tw.p}>
Astro is a modern Vite-based build tool meant for speed which ships zero JavaScript by default. Initially released with support for static site generation, now Astro delivers a full suite of server rendered features.
</p>
</AccordionItem_A>
</AccordionGroup_A>

Features

  1. Clicking the header toggles the body open or closed.
  2. An indicator icon rotates 180 degrees to indicate open/closed state
  3. Body content is revealed smoothly using a Hyperscript transition on the body’s *height.
  4. A maxHeight prop prevents the body height from exceeding a set value.
  5. Tailwind classes for the header, body, and active header (when opened) can be passed in through props to customize the appearance of the entire control from outside.
  6. Content for the body is simplhy placed into the default slot of the AccordionItem_A component and thus can be styled to your liking from where the accordion is used.

Components

This control is comprised of two Astro components:

  1. AccordionGroup_A which functions as a wrapper for all the individual items, and
  2. AccordionItem_A which consists of a button for the header and a div for the body content.

All of the source code, including the scripts and styling, is found in the Code section below.

Props

Props are used to configure both components from where they are used. This strategy allows full customization without having to rewrite the source code itself.

All props accept strings unless otherwise specified.

AccordionGroup_A

interface Props {
closeSiblings?:boolean,
styles?:string
}
const {
closeSiblings = false,
styles
} = Astro.props

closeSiblings

You can designate if opening an accordion should cause all other accordions to close. When passing true to the closeSiblings prop a custom closeOpenAccordions event is fired. All accordions except the one sending this event will be then closed. This was done in the example code above.

The default value for closeSiblings is false.

styles

This prop takes a string containing any Tailwind classes of your choosing. These classes will be merged with the default styles for AccordionGroup_A (which you find in the Code section below.

TW classes passed in the styles prop only affect the whole group and thus should be mostly positioning values such as width, margins, etc.

AccordionItem_A

interface Props {
id:string,
caption:string,
prefix?:string
maxHeight:number
headerStyles?:string
bodyStyles?:string
activeHeaderStyles?:string
openDuration?:string
closeDuration?:string
}
const {
id,
caption,
prefix='- ',
maxHeight=300,
headerStyles,
bodyStyles,
activeHeaderStyles = 'bg-orange-100 dark:bg-blue-950',
openDuration='260ms',
closeDuration='400ms'
} = Astro.props

id

Each item should have a unique id to prevent naming collisions with other items.

caption

The caption is the text that appears in the accordion header.

prefix

This props accepts is a character or short string that is prepended to the caption. It acts as a tick mark. The default is -

maxHeight

This prop takes a number representing the maximum height (in pixels) of the accordion body. The default is 300.

The HS code checks if the calculated final height of the body is more than the value specified in the maxHeight prop.

If so, the body height is limited to the maxHeight value and a vertical scroll bar is shown.

headerStyles

This prop takes a string containing any Tailwind classes of your choosing. These classes will be merged with the default styles for the <button> element in the AccordionItem_A (which you find in the Code section below.

Use this prop to add or override styling of the accordion header.

bodyStyles

This prop takes a string containing any Tailwind classes of your choosing. These classes will be merged with the default styles for the <div> element of AccordionItem_A (which you find in the Code section below.

Use this prop to add or override styling of the accordion body.

activeHeaderStyles

This prop takes a string containing any Tailwind classes of your choosing. These classes will be added to the <button> element when the accordion is open.

Use this prop to add styling to the header only when it is open.

openDuration

This prop takes a string which sets the duration of the transition when the accordion body is shown. The default is '260ms'.

closeDuration

This prop takes a string which sets the duration of the transition when the accordion body is hidden. The default is '400ms'.

Styling

Both the AccordionGroup_A and AccordionItem_A components can be styled with suitable TW utility classes from outside of the component code base using props. This allows the appearance of both the group and individual components to be customized without having to modify the source code.

You can override the default styling by sending additional Tailwind classes via the above props which are then merged with the default styles and any conflicts resolved with a ‘last wins’ rule.

To improve readability, the Tailwind classes and Hyperscripts are both extracted from the markup and encapsulated into Astro tw and hs local variables within the Component Script which are then referenced in the html markup.

The advantage of this strategy is to allow long run-on strings to be formatted as concatenated, multi-line strings which aids readability and code maintenance.

Transitions

For this accordion, the transition command of Hyperscript is utilized to specify the CSS properties, duration, and timing function used to smoothly reveal the accordion body.

What makes this Hyperscript example unique is insertion of props into the script.

To make component props available to the script, the HS code is placed into a back-tick delimited JS string and stored in a local constant in the Astro Component Script (i.e. frontmatter). Dynamic values can thus be passed into the script itself with simple string interpolation creating another level of functionality.

Here is a brief code snippet of from AccordionItem_A where the maxHeight and openDuration component props are relayed into the script itself using string interpolation. Using this technique is analagous to sending parameters to the script from an outside source.

This ‘trick’ allows you to change the transition duration and other actions of the Hyperscript from outside of the component simply by sending new values to the script code via props.

Props used within scripts
`transition the #{:accordionBody}'s *height to ${maxHeight} using 'all ${openDuration} ease-in' `

The local hs constant storing the HS code is then referenced in the markup as would any variable in the component frontmatter using curly braces as shown below.

Script reference in markup
<li class={tw.wrapper}>
<!-- Accordion Header -->
<button
id={id}
class={tw.accordionHeader}
script={hs}>
<span>{prefix}{caption}</span>
<!-- TwirlIcon -->
<svg id={`${id}-twirl-icon`} class={tw.icon} fill='currentColor' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'>
<path fill-rule='evenodd' d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'></path>
</svg>
</button>
<!-- Accordion Body -->
<div id={`${id}-body`} class={tw.accordionBody}>
<slot />
</div>
</li>

The syntax for the transition command of Hyperscript must be exact or it will not work. Following the using clause you must provide a quoted string analogous to how a transition in vanilla CSS would be written. If you leave out the quotes, the transition will fail silently.

See the Code section below for all the scripts and how they are populated with the values of the component props using this technique.

Slots

Both components use a default <slot> to receive child elements.

The AccordionGroup_A default <slot> is populated with accordion items.

The AccordionItem_A default <slot> is populated with whatever markup you wish to place into the accordion body.

Code

AccordionGroup_A

This example accordion is comprised of a wrapper component (AccordionGroup_A) which contains a default slot into which multiple accordion items (AccordionItem_A) are placed.

A minimal set of default styles are provided but can be overridden or added to by passing additional TW classes to the styles prop.

Explore the Tabs below to review all the code and styling which makes up the accordion group wrapper.

Copy/paste the contents of the Everything tab to use this component in your projects.

interface Props {
closeSiblings?:boolean, //all other Accordions are sent a closeOpenAccordions event
styles?:string //override or add TW classes for stying the Group
}
const {
closeSiblings = false,
styles
} = Astro.props

AccordionItem_A

Most of the functionality of this control resides in the styling and actions of the AccordionItem_A component.

Each group should receive multiple AccordionItem_A components

Each AccordionItem_A consists of a <li> with a trigger <button> for the accordion header and a <div> which presents a default <slot> for the actual content.

Explore the Tabs below to review all the code and styling for the accordion item component.

Copy/paste the contents of the Everything tab to use this component in your projects.

interface Props {
id:string, //a unique identifier allowing multiple AccordionItems on a page without collisions
caption:string, //the text display in the Accordion header
prefix?:string //a tick mark preceeding the caption
maxHeight:number //maximum open height in px (after which the body content scrolls vertically)
headerStyles?:string //override or add TW classes to the header
bodyStyles?:string //override or add TW classes to the body
activeHeaderStyles?:string //override, add, remove TW classes for headers of opened Accordions
openDuration?:string //transition timing when showing body content
closeDuration?:string //transition timing when hiding body content
}
const {
id,
caption,
prefix='- ',
maxHeight=300,
headerStyles,
bodyStyles,
activeHeaderStyles = 'bg-orange-100 dark:bg-blue-800',
openDuration='260ms',
closeDuration='400ms'
} = Astro.props

Usage

Accordion controls are often used in FAQ or Info sections of a web site. The advantages of using an Accordion is to hide the majority of the content until the user needs to see it.

By presenting just the title in the header the user is prompted to explore the hidden content.