preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
433 lines (326 loc) • 16.6 kB
Markdown
# Accordion
Build vertically collapsing accordions in combination with Accordion JavaScript plugin.
[](https://www.npmjs.com/package/@preline/accordion) [](https://opensource.org/licenses/MIT) [](https://preline.co/plugins/accordion.html)
## Contents
- [Overview](#overview)
- [Installation](#installation)
- [Basic usage](#basic-usage)
- [Configuration Options](#configuration-options)
- [JavaScript API](#javascript-api)
- [Events](#events)
- [Common Patterns](#common-patterns)
- [License](#license)
## Overview
The Accordion component allows you to create collapsible content sections that can be expanded or collapsed by clicking on toggle buttons. It supports multiple accordion items within a group, with options for keeping items open, ensuring at least one item stays open, and treeview mode for hierarchical structures.
**Key Features:**
- Multiple accordion items in a single group
- Programmatic control via JavaScript API
- Event system for lifecycle hooks
- Treeview mode support
- Accessibility attributes (ARIA) built-in
## Installation
To get started, install Accordion plugin via npm, else you can skip this step if you are already using Preline UI as a package.
```bash
npm i /accordion
```
### CSS
Use [``](https://tailwindcss.com/docs/functions-and-directives#source-directive) to register the plugin's JavaScript path for Tailwind CSS scanning, then [`@import`](https://tailwindcss.com/docs/functions-and-directives#import-directive) the plugin's CSS files into your Tailwind CSS file.
```css
"tailwindcss";
/* @preline/accordion */
"../node_modules/@preline/accordion/*.js";
"./node_modules/@preline/accordion/variants.css";
"./node_modules/@preline/accordion/theme.css";
```
### JavaScript
Include the JavaScript that powers the interactive elements near the end of your `</body>` tag:
```html
<script src="./node_modules/@preline/accordion/index.js"></script>
```
### Manual Initialization
Use the `non-auto` entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.
```html
<script type="module">
import HSAccordion from "@preline/accordion/non-auto.mjs";
new HSAccordion(document.querySelector("#accordion"));
</script>
```
### Via Bundler
When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.
`/accordion` is the auto-init entry: it scans the DOM and initializes matching elements automatically.
```js
import "@preline/accordion";
```
`/accordion/non-auto` is the manual entry: use it when you want explicit control over when initialization happens, either via `autoInit()` or by creating a specific instance yourself.
```js
import HSAccordion from "@preline/accordion/non-auto";
HSAccordion.autoInit();
// Or initialize a specific element manually
const el = document.querySelector("#accordion");
if (el) new HSAccordion(el);
```
### TypeScript
This package ships with TypeScript type definitions. No additional `/` package is needed.
## Basic usage
The following example demonstrates the minimal HTML structure required for an accordion component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The example shows three accordion items within a group, with the first item open by default.
```html
<div class="hs-accordion-group">
<div class="hs-accordion active" id="hs-unstyled-heading-one">
<button class="hs-accordion-toggle" aria-expanded="true" aria-controls="hs-unstyled-collapse-one">
Accordion #1
</button>
<div id="hs-unstyled-collapse-one" class="hs-accordion-content overflow-hidden transition-[height] duration-300" role="region" aria-labelledby="hs-unstyled-heading-one">
Accordion content that expands and collapses when the toggle button is clicked.
</div>
</div>
<div class="hs-accordion" id="hs-unstyled-heading-two">
<button class="hs-accordion-toggle" aria-expanded="false" aria-controls="hs-unstyled-collapse-two">
Accordion #2
</button>
<div id="hs-unstyled-collapse-two" class="hs-accordion-content hidden overflow-hidden transition-[height] duration-300" role="region" aria-labelledby="hs-unstyled-heading-two">
Accordion content that expands and collapses when the toggle button is clicked.
</div>
</div>
<div class="hs-accordion" id="hs-unstyled-heading-three">
<button class="hs-accordion-toggle" aria-expanded="false" aria-controls="hs-unstyled-collapse-three">
Accordion #3
</button>
<div id="hs-unstyled-collapse-three" class="hs-accordion-content hidden overflow-hidden transition-[height] duration-300" role="region" aria-labelledby="hs-unstyled-heading-three">
Accordion content that expands and collapses when the toggle button is clicked.
</div>
</div>
</div>
```
**Structure Requirements:**
- `hs-accordion-group`: Required wrapper container for all accordion items
- `hs-accordion`: Required container for each accordion item
- `hs-accordion-toggle`: Required button element that triggers expand/collapse
- `hs-accordion-content`: Required container for collapsible content
- Unique `id` attributes for each accordion item and its content
- Proper ARIA attributes (`aria-expanded`, `aria-controls`, `aria-labelledby`, `role="region"`)
**Initial State:**
- To have an accordion item open by default, add the `active` class to the `hs-accordion` element
- For closed items, add the `hidden` class to the `hs-accordion-content` element
## Configuration Options
### Data Attributes
Data attributes are added directly to HTML elements to configure accordion behavior.
| Attribute | Target Element | Type | Default | Description |
| --- | --- | --- | --- | --- |
| `data-hs-accordion-always-open` | `hs-accordion-group` | boolean attribute | `false` (absent) | When present (boolean attribute), accordion items stay open when another item is opened. By default, opening one item closes others. Presence of attribute = `true`, absence = `false`. |
**Example:**
```html
<!-- Boolean attribute: presence = true, absence = false -->
<div class="hs-accordion-group" data-hs-accordion-always-open>
<!-- Multiple items can be open simultaneously -->
</div>
```
### CSS Classes (Modifiers)
CSS class modifiers use Tailwind-style syntax with `--` prefix to control behavior.
| Class Modifier | Target Element | Values | Default | Description |
| --- | --- | --- | --- | --- |
| `--stop-propagation:*` | `hs-accordion-toggle` | `"true"` \| `"false"` | `"false"` | When `"true"`, interrupts event propagation when toggle button is clicked. Useful when accordion is nested inside clickable elements. |
| `--keep-one-open:*` | `hs-accordion-group` | `"true"` \| `"false"` | `"false"` | When `"true"`, ensures at least one accordion item remains open at all times. Prevents closing the last open item. |
**Example:**
```html
<div class="hs-accordion-group --keep-one-open:true">
<!-- At least one item will always be open -->
</div>
<button class="hs-accordion-toggle --stop-propagation:true">
<!-- Click event won't bubble up -->
</button>
```
### Required CSS Classes
These classes define the structure and must be present for the accordion to function.
| Class | Required On | Purpose |
| --- | --- | --- |
| `hs-accordion-group` | Container wrapper | Groups multiple accordion items together |
| `hs-accordion` | Each accordion item container | Identifies an individual accordion item |
| `hs-accordion-toggle` | Button element | The clickable element that expands/collapses content |
| `hs-accordion-content` | Content container | The collapsible content area |
### Optional CSS Classes
| Class | Required On | Purpose |
| --- | --- | --- |
| `hs-accordion-treeview-root` | Parent container | When present, activates treeview mode for hierarchical accordion structures |
| `hs-accordion-selectable` | Elements inside treeview | When treeview mode is active, elements with this class become selectable |
**Treeview Mode Example:**
```html
<div class="hs-accordion-treeview-root">
<div class="hs-accordion-group">
<div class="hs-accordion">
<button class="hs-accordion-toggle">Parent</button>
<div class="hs-accordion-content">
<div class="hs-accordion-selectable">Selectable item</div>
</div>
</div>
</div>
</div>
```
## JavaScript API
The `HSAccordion` object is available in the global `window` object after the plugin is loaded.
### Instance Methods
These methods are called on an accordion instance.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `show()` | None | `boolean` | Opens the collapsed accordion item. Returns `false` if item is already open, `true` otherwise. If other items are open and `always-open` is not set, they will be closed. |
| `hide()` | None | `boolean` | Collapses the currently open accordion item. Returns `false` if item is already closed or if `keep-one-open` is enabled and this is the last open item, `true` otherwise. |
| `destroy()` | None | `void` | Destroys the accordion instance, removes all generated markup, classes, and event listeners. Use when removing accordion from DOM. |
### Static Methods
These methods are called directly on the `HSAccordion` class.
| Method | Parameters | Return Type | Description |
| --- | --- | --- | --- |
| `HSAccordion.getInstance(target, isInstance)` | `target`: `HTMLElement \| string` (CSS selector)<br>`isInstance`: `boolean` (optional) | `{ id: string \| number, element: HSAccordion } \| HTMLElement \| null` | Returns the accordion instance or element associated with the target. If `isInstance` is `true`, returns collection item object `{ id, element }` where `element` is the `HSAccordion` instance. If `isInstance` is `false` or omitted, returns the DOM element (`HTMLElement`). Returns `null` if accordion instance is not found. |
| `HSAccordion.show(target)` | `target`: `HSAccordion \| HTMLElement \| string` (CSS selector) | `void` | Opens the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `show()` method. Only opens if item is not already open. |
| `HSAccordion.hide(target)` | `target`: `HSAccordion \| HTMLElement \| string` (CSS selector) | `void` | Closes the accordion item identified by target. Accepts an accordion instance, DOM element, or CSS selector string. Static alternative to instance `hide()` method. Only closes if item is open and `keep-one-open` allows it. |
### Usage Examples
**Example 1: Using instance methods (public API)**
```javascript
// Create a new accordion instance
const accordion = new HSAccordion(document.querySelector('#hs-accordion'));
const showBtn = document.querySelector('#hs-show-btn');
const hideBtn = document.querySelector('#hs-hide-btn');
// Open accordion programmatically
showBtn.addEventListener('click', () => {
accordion.show();
});
// Close accordion programmatically
hideBtn.addEventListener('click', () => {
accordion.hide();
});
```
**Example 2: Using static methods**
```javascript
const showBtn = document.querySelector('#hs-show-btn');
const hideBtn = document.querySelector('#hs-hide-btn');
// Open accordion using static method (no instance needed)
showBtn.addEventListener('click', () => {
HSAccordion.show('#hs-accordion');
});
// Close accordion using static method
hideBtn.addEventListener('click', () => {
HSAccordion.hide('#hs-accordion');
});
```
**Example 3: Getting instance and using methods (recommended pattern)**
```javascript
// Get the accordion instance
const instance = HSAccordion.getInstance('#hs-accordion', true);
if (instance) {
const { element } = instance; // element is the HSAccordion instance
const showBtn = document.querySelector('#hs-show-btn');
// Use instance methods
showBtn.addEventListener('click', () => {
element.show();
});
// Clean up when removing from DOM
function removeAccordion() {
element.destroy();
}
}
```
**Example 4: Destroying accordion instance**
```javascript
const instance = HSAccordion.getInstance('#hs-accordion', true);
if (instance) {
const { element } = instance;
const removeBtn = document.querySelector('#hs-remove-btn');
removeBtn.addEventListener('click', () => {
// Clean up before removing from DOM
element.destroy();
// Now safe to remove the element
document.querySelector('#hs-accordion').remove();
});
}
```
## Events
Accordion instances emit events that can be listened to for lifecycle hooks and custom behavior.
| Event Name | When Fired | Callback Parameter | Description |
| --- | --- | --- | --- |
| `on:beforeOpen` | Before accordion item opens | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts opening. Can be used to cancel or modify behavior. |
| `on:open` | After accordion item opens | `HTMLElement` (current accordion element) | Fires after the accordion item has fully opened. Use for post-open actions like loading content. |
| `on:beforeClose` | Before accordion item closes | `HTMLElement` (current accordion element) | Fires immediately before the accordion item starts closing. Can be used to cancel or modify behavior. |
| `on:close` | After accordion item closes | `HTMLElement` (current accordion element) | Fires after the accordion item has fully closed. Use for cleanup or state updates. |
### Event Usage Example
```javascript
// Get accordion instance
const instance = HSAccordion.getInstance('#hs-accordion', true);
if (instance) {
const { element } = instance;
// Listen to open event
element.on('open', (accordionElement) => {
console.log('Accordion opened:', accordionElement);
// Perform actions after accordion opens
// e.g., load content, update UI, track analytics
});
// Listen to close event
element.on('close', (accordionElement) => {
console.log('Accordion closed:', accordionElement);
// Perform cleanup or state updates
});
// Listen to beforeOpen event (can be used to cancel)
element.on('beforeOpen', (accordionElement) => {
console.log('About to open:', accordionElement);
// Can add conditional logic here
});
// Listen to beforeClose event (can be used to cancel)
element.on('beforeClose', (accordionElement) => {
console.log('About to close:', accordionElement);
// Can add conditional logic here
});
}
```
## Common Patterns
### Pattern 1: Multiple Open Items
Allow multiple accordion items to be open simultaneously.
```html
<div class="hs-accordion-group" data-hs-accordion-always-open>
<div class="hs-accordion" id="hs-item-first">
<button class="hs-accordion-toggle">Item 1</button>
<div class="hs-accordion-content hidden">Content 1</div>
</div>
<div class="hs-accordion" id="hs-item-second">
<button class="hs-accordion-toggle">Item 2</button>
<div class="hs-accordion-content hidden">Content 2</div>
</div>
</div>
```
### Pattern 2: Always Keep One Open
Ensure at least one item is always open.
```html
<div class="hs-accordion-group --keep-one-open:true">
<div class="hs-accordion active" id="hs-item-first">
<button class="hs-accordion-toggle">Item 1</button>
<div class="hs-accordion-content">Content 1</div>
</div>
<div class="hs-accordion" id="hs-item-second">
<button class="hs-accordion-toggle">Item 2</button>
<div class="hs-accordion-content hidden">Content 2</div>
</div>
</div>
```
### Pattern 3: Programmatic Control
Control accordion from external buttons.
```html
<div class="hs-accordion-group">
<div class="hs-accordion" id="hs-accordion-first">
<button class="hs-accordion-toggle">Content</button>
<div class="hs-accordion-content hidden">Content here</div>
</div>
</div>
<button id="hs-open-all">Open All</button>
<button id="hs-close-all">Close All</button>
<script>
const instance = HSAccordion.getInstance('#hs-accordion-first', true);
if (instance) {
const { element } = instance;
document.querySelector('#hs-open-all').addEventListener('click', () => {
element.show();
});
document.querySelector('#hs-close-all').addEventListener('click', () => {
element.hide();
});
}
</script>
```
## License
Copyright (c) 2026 Preline Labs.
Licensed under the [MIT License](https://opensource.org/licenses/MIT).