Accessible accordion/collapse with keyboard navigation and ARIA support
Only one section can be open at a time. Without animation - content shows/hides instantly.
Web accessibility means that websites, tools, and technologies are designed and developed so that people with disabilities can use them.
Specifically, it means that people can perceive, understand, navigate, and interact with the web.
The web is an increasingly important resource in many aspects of life: education, employment, government, commerce, health care, recreation, and more.
It is essential that the web be accessible to provide equal access and opportunity to people with diverse abilities.
Web accessibility benefits people with disabilities, but also:
Accordion has CSS Grid animations by default (grid-template-rows: 0fr → 1fr).
To disable animations, use the data-no-animation attribute.
Web accessibility means that websites, tools, and technologies are designed and developed so that people with disabilities can use them.
Specifically, it means that people can perceive, understand, navigate, and interact with the web.
The web is an increasingly important resource in many aspects of life: education, employment, government, commerce, health care, recreation, and more.
It is essential that the web be accessible to provide equal access and opportunity to people with diverse abilities.
Web accessibility benefits people with disabilities, but also:
Animation speed can be set using CSS custom properties (variables).
You can set them globally in CSS or inline using the style attribute.
This section opens and closes slower (0.6s instead of default 0.3s).
This section opens and closes faster (0.15s instead of default 0.3s).
This section uses a custom easing function for a "bounce" effect.
Multiple sections can be open simultaneously.
ARIA (Accessible Rich Internet Applications) is a set of attributes that define ways to make web content and web applications more accessible to people with disabilities.
All functionality must be available from a keyboard. Users with motor disabilities often use a keyboard instead of a mouse.
Focus should always be visible and should follow a logical navigation order. Focus trap is used in modal dialogs and offcanvas panels.
Accordion with a single toggle button that toggles all sections at once. The button changes text and icon based on state (open/closed).
HTML (HyperText Markup Language) is the standard markup language for creating web pages.
CSS (Cascading Style Sheets) is the language used to describe the presentation of a document written in HTML.
JavaScript is a programming language that enables you to implement complex features on web pages.
Standalone button that toggles visibility of panel(s) via CSS selector.
This is a panel that can be shown or hidden using a standalone toggle button.
This pattern is useful for simple show/hide functionality outside of accordion groups.
Standalone panel has CSS Grid animation by default. To disable animations, use the data-no-animation attribute.
This panel uses CSS Grid for smooth animation when opening/closing.
Animation works without needing to know the content height in advance.
One button can control multiple panels simultaneously using a class selector.
Accordion without borders, only separators between items.
Content of the first item in bordered variant.
Content of the second item in bordered variant.
Content of the third item in bordered variant.
Accordion with more spacing and shadow between items.
Content of the first item in separated variant.
Content of the second item in separated variant.
Smaller padding and font size for a more compact look.
Content in compact style.
More content in compact style.
Larger padding and font size for a more prominent look.
Content in large style with more padding.
More content in large style.
<div data-accordion>
<div data-accordion-item>
<button data-accordion-trigger>
Section 1
</button>
<div data-accordion-panel>
<div>
Section 1 content
</div>
</div>
</div>
<div data-accordion-item>
<button data-accordion-trigger>
Section 2
</button>
<div data-accordion-panel>
<div>
Section 2 content
</div>
</div>
</div>
</div>
<!-- Animations are default, no need to add any attribute -->
<div data-accordion>
<div data-accordion-item>
<button data-accordion-trigger>Section 1</button>
<div data-accordion-panel>
<!-- Wrapper div is REQUIRED for proper animation -->
<div>
<div>
Section 1 content
</div>
</div>
</div>
</div>
</div>
Important for animation:
• Animations are default - automatically enabled
• Required structure: panel > wrapper (overflow) > content (padding)
• First div: technical wrapper with overflow: hidden (NO padding)
• Second div: content container (has padding)
• Animation: CSS Grid (grid-template-rows: 0fr → 1fr)
• Works without knowing content height in advance
• Speed: --a11y-accordion-duration (default: 0.3s)
• Easing: --a11y-accordion-easing (default: ease)
• Automatic support for prefers-reduced-motion
<!-- Inline using style attribute -->
<div data-accordion style="--a11y-accordion-duration: 0.6s;">
<!-- accordion items -->
</div>
<!-- Custom easing -->
<div data-accordion
style="--a11y-accordion-duration: 0.4s; --a11y-accordion-easing: cubic-bezier(0.68, -0.55, 0.265, 1.55);">
<!-- accordion items -->
</div>
<!-- Or globally in CSS -->
<style>
:root {
--a11y-accordion-duration: 0.5s;
--a11y-accordion-easing: ease-in-out;
}
</style>
<!-- Disable animations -->
<div data-accordion data-no-animation>
<!-- accordion without animations -->
</div>
<div data-accordion data-accordion-multiple>
<!-- accordion items -->
</div>
<div data-accordion data-accordion-multiple>
<!-- Toggle control at top (created automatically) -->
<div data-accordion-controls="top"></div>
<!-- Accordion items -->
<!-- Toggle control at bottom (created automatically) -->
<div data-accordion-controls="bottom"></div>
</div>
Toggle button behavior:
• Created automatically with dynamic texts "Expand All" / "Collapse All"
• Text in <span> changes: "Expand All" / "Collapse All"
• Aria-label changes: "Expand all panels" / "Collapse all panels"
• Toggle icon (plus/minus) changes based on state
• Aria-expanded attribute reflects current state (true/false)
• Aria-controls contains IDs of all panels
• If multiple toggle buttons exist (top and bottom), all are synchronized on click
<div data-accordion data-accordion-multiple>
<div data-accordion-controls="top">
<button
data-accordion-toggle-all
data-text-for-show="Expand All"
data-text-for-hide="Collapse All"
data-aria-label-for-show="Expand all sections"
data-aria-label-for-hide="Collapse all sections">
<svg aria-hidden="true" width="16" height="16">
<path class="toggle-icon-expand" d="M8 3v10M3 8h10"/>
<path class="toggle-icon-collapse" d="M3 8h10" style="display: none;"/>
</svg>
<span>Expand All</span>
</button>
</div>
<!-- accordion items -->
</div>
Important:
• Button must contain a <span> element for dynamic text
• Icons use classes .toggle-icon-expand and .toggle-icon-collapse
• Attribute data-accordion-toggle-all is required
• data-aria-label-for-show/hide are optional (if not set, text from data-text-for-show/hide is used)
<!-- With ID selector -->
<button data-collapse-toggle="#panel-1">
Open Panel
</button>
<div id="panel-1" data-no-animation>
<div>
Panel content...
</div>
</div>
<!-- With class selector (controls multiple panels) -->
<button data-collapse-toggle=".info-panel">
Toggle All Info Panels
</button>
<div class="info-panel" data-no-animation><div><div>Panel 1</div></div></div>
<div class="info-panel" data-no-animation><div><div>Panel 2</div></div></div>
<!-- Animations are default, no need to add any attribute -->
<button data-collapse-toggle="#panel">
Open Panel
</button>
<div id="panel">
<!-- First div: overflow wrapper (overflow: hidden, grid-row: 1/span 2) -->
<div>
<!-- Second div: content wrapper (padding) -->
<div>
Panel content...
</div>
</div>
</div>
<!-- Disable animations -->
<div id="no-animation-panel" data-no-animation>
<div>
<div>
Panel without animation
</div>
</div>
</div>
<!-- Same text for aria-label and visible text -->
<button
data-collapse-toggle="#detail-panel"
data-text-for-show="Show Details"
data-text-for-hide="Hide Details">
<span>Show Details</span>
</button>
<!-- Different texts for aria-label and visible text -->
<button
data-collapse-toggle=".info-panel"
data-text-for-show="Show All"
data-text-for-hide="Hide All"
data-aria-label-for-show="Show all info panels"
data-aria-label-for-hide="Hide all info panels">
<span>Show All</span>
</button>
Behavior:
• Button must contain a <span> element for dynamic text
• Text in <span> changes based on data-text-for-show/hide
• Aria-label changes based on data-aria-label-for-show/hide (if set)
• If data-aria-label-* are not set, text from data-text-for-show/hide is used for aria-label as well
• aria-expanded is automatically set to true/false
import { initAccordions, initCollapses } from './a11y-kit/a11y-accordion.js';
// Initialize after DOM is ready
document.addEventListener('DOMContentLoaded', () => {
initAccordions(); // Everything (collapse + accordion groups)
// OR
initCollapses(); // Only standalone collapse toggles
});
import { Collapse, Accordion } from './a11y-kit/a11y-accordion.js';
// Only standalone collapse toggles
const collapse = new Collapse();
// Everything (collapse + accordion groups)
const accordion = new Accordion();
npm install accessible-kit
// Import only what you need (best for tree-shaking)
import { initCollapses, initAccordions, Collapse, Accordion } from 'accessible-kit/accordion';
// Initialize after DOM is ready
document.addEventListener('DOMContentLoaded', () => {
initAccordions(); // Auto-init all accordions
// OR create instances manually
const accordion = new Accordion();
});