Getting started
Components
Extra
Accordion
Collapsible content sections for organized information.
To use this component copy our js
code:
class Accordion {
constructor(container, items, options) {
// Save container element and accordion items
this.container = container;
this.items = items;
// Merge default options with user-defined options
this.options = {
always_open: false, // Allow multiple panels open at the same time
active_classes: "active", // CSS classes for active (open) state
inactive_classes: "inactive", // CSS classes for inactive (closed) state
on_open: () => {}, // Callback when a panel is opened
on_close: () => {}, // Callback when a panel is closed
on_toggle: () => {}, // Callback when a panel is toggled
...options,
};
// Initialize accordion
this.init();
}
init() {
if (this.items?.length > 0) {
// Initialize each accordion item
this.items.forEach((item) => {
// Open item if it is marked as active
if (item.active) this.open(item.id);
// Add click event to toggle item on trigger click
item.trigger.addEventListener("click", () => this.toggle(item.id));
});
}
}
// Find item by its id
get_item(id) {
return this.items.find((i) => i.id === id);
}
// Open a specific item
open(id) {
const item = this.get_item(id);
// If multiple panels are not allowed, close all others
if (!this.options.always_open) {
this.items.forEach((i) => {
if (i !== item) this.close(i.id);
});
}
// Add active classes and remove inactive ones
item.trigger.classList.add(...this.options.active_classes.split(" "));
item.trigger.classList.remove(...this.options.inactive_classes.split(" "));
// Update accessibility attribute
item.trigger.setAttribute("aria-expanded", "true");
// Show the panel
item.panel.classList.remove("hidden");
item.active = true;
// Rotate back icon if exists
if (item.icon) item.icon.classList.remove("rotate-180");
// Run user-defined on_open callback
this.options.on_open(this, item);
}
// Close a specific item
close(id) {
const item = this.get_item(id);
// Remove active classes and add inactive ones
item.trigger.classList.remove(...this.options.active_classes.split(" "));
item.trigger.classList.add(...this.options.inactive_classes.split(" "));
// Hide the panel
item.panel.classList.add("hidden");
item.trigger.setAttribute("aria-expanded", "false");
item.active = false;
// Rotate icon if exists
if (item.icon) item.icon.classList.add("rotate-180");
// Run user-defined on_close callback
this.options.on_close(this, item);
}
// Toggle a specific item (open if closed, close if open)
toggle(id) {
const item = this.get_item(id);
item.active ? this.close(id) : this.open(id);
// Run user-defined on_toggle callback
this.options.on_toggle(this, item);
}
}
// Initialize all accordions on the page
export const init_accordions = (options = {}) => {
document.querySelectorAll("[data-accordion]").forEach((container) => {
// Read options from container attributes
const always_open = container.getAttribute("data-accordion") === "open";
const active_classes =
container.getAttribute("data-active-classes") || "active";
const inactive_classes =
container.getAttribute("data-inactive-classes") || "inactive";
// Collect all accordion items inside this container
const items = [];
container
.querySelectorAll("[data-accordion-trigger]")
.forEach((trigger) => {
// Ensure the trigger belongs to the current accordion (not nested one)
if (trigger.closest("[data-accordion]") === container) {
const id = trigger.getAttribute("data-accordion-trigger");
items.push({
id, // Target panel ID
trigger, // Button element
panel: document.querySelector(id), // Target panel element
icon: trigger.querySelector("[data-accordion-icon]"), // Optional icon
active: trigger.getAttribute("aria-expanded") === "true", // Initial state
});
}
});
// Create a new accordion instance
new Accordion(container, items, {
always_open,
active_classes,
inactive_classes,
...options,
});
});
};
Then init accordions:
init_accordions();
Params
Available parameters for use in init_accordions
:
{
alwaysOpen: false, // Allow multiple panels open at the same time
activeClasses: "active", // CSS classes for active (open) state
inactiveClasses: "inactive", // CSS classes for inactive (closed) state
onOpen: () => {}, // Callback when a panel is opened
onClose: () => {}, // Callback when a panel is closed
onToggle: () => {}, // Callback when a panel is toggled
};
Usage
Text