Accordion Basic
Pure-CSS collapsible sections: smooth max-height animation, rotating chevron, multi-open by default or exclusive mode with a single parameter.
This effect is free — the Navigation category contains 36 effects total, including 4 free. Effect.Labs has 811 vanilla effects. Explore the category →
Usage examples
20 · Max : 100limit pour la pagination.1navigation, particles, loadersHow it works
The open/close mechanism relies on the CSS max-height trick: each .accordion-content starts at max-height: 0; overflow: hidden. When the .active class is added to the parent item, the rule .accordion-item.active .accordion-content { max-height: 200px } kicks in and the browser interpolates between the two values via transition: max-height 0.4s. The pre-existing overflow: hidden clips content cleanly throughout the transition.
The chevron is an inline SVG animated by a single CSS rule: .accordion-item.active .accordion-icon { transform: rotate(180deg) } with transition: transform 0.3s. The .accordion-header button gets a subtle background fade on hover (transition: background 0.3s) — both transitions are independent and composable.
The toggleAccordion(header, type) function is global (not wrapped in an IIFE), called directly from the onclick attribute of each button. It walks up to the item (header.parentElement) then to the accordion (.parentElement). In 'single' mode, it first removes .active from all other items before toggling the current one — only one section stays open. In 'basic' mode, it simply toggles the item without touching neighbors.
The .accordion-single CSS variant is an optional layer: adding that class to .accordion gives each item an indigo border (rgba(99,102,241,0.2)) and a glowing box-shadow on the active item. No extra JS required — just a class change in HTML.
Accessibility
- prefers-reduced-motion not implemented: the
max-heightandtransformtransitions run regardless of system preference. Add@media (prefers-reduced-motion: reduce) { .accordion-content, .accordion-icon { transition: none; } }to fix. - aria-expanded missing: buttons do not expose their open/closed state to screen readers. For WCAG 4.1.2 compliance, add
aria-expanded="false"by default and toggle it totrueintoggleAccordion. - Keyboard:
<button>elements are natively focusable and activatable via Space / Enter — no extra JS required. - Contrast: white text (
#ffffff) on a dark background (rgba(255,255,255,0.05)) — ratio above 7:1, WCAG AA and AAA compliant. - Inline onclick: event handlers are on native
<button>elements — accessible without CSS and in the default tab order.
Browser compatibility
Relies only on CSS transitions (max-height, transform) and class manipulation — available since 2013.
Without JavaScript, buttons are inactive and sections stay in their initial state (first one pre-opened via the <code>.active</code> class in HTML). No fatal JS errors.
The code
Copy the three blocks into your page. No dependencies.
<div class="component-demo">
<div class="accordion" id="accordionBasic">
<div class="accordion-item active">
<button class="accordion-header" onclick="toggleAccordion(this, 'basic')">
Section 1
<svg class="accordion-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6">
</path>
</svg>
</button>
<div class="accordion-content">
<div class="accordion-body">
Contenu de la première section avec animation de hauteur fluide.
</div>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header" onclick="toggleAccordion(this, 'basic')">
Section 2
<svg class="accordion-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6">
</path>
</svg>
</button>
<div class="accordion-content">
<div class="accordion-body">
Deuxieme section avec du contenu different.
</div>
</div>
</div>
<div class="accordion-item">
<button class="accordion-header" onclick="toggleAccordion(this, 'basic')">
Section 3
<svg class="accordion-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6">
</path>
</svg>
</button>
<div class="accordion-content">
<div class="accordion-body">
Troisieme et dernière section.
</div>
</div>
</div>
</div>
</div>
.component-demo {
width: 100%;
min-height: 200px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
}
.accordion {
width: 100%;
max-width: 350px;
}
.accordion-item {
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
margin-bottom: 8px;
overflow: hidden;
}
.accordion-header {
width: 100%;
padding: 16px 20px;
background: transparent;
border-width: medium;
border-style: none;
border-color: currentcolor;
border-image: initial;
color: white;
font-size: 0.95rem;
font-weight: 500;
font-family: inherit;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
transition: background 0.3s;
}
.accordion-header:hover {
background: rgba(255, 255, 255, 0.05);
}
.accordion-icon {
transition: transform 0.3s;
}
.accordion-item.active .accordion-icon {
transform: rotate(180deg);
}
.accordion-content {
max-height: 0px;
overflow: hidden;
transition: max-height 0.4s, padding 0.3s;
}
.accordion-item.active .accordion-content {
max-height: 200px;
}
.accordion-body {
padding: 0px 20px 16px;
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
line-height: 1.6;
}
.accordion-single .accordion-item {
border: 1px solid rgba(99, 102, 241, 0.2);
}
.accordion-single .accordion-item.active {
border-color: rgba(99, 102, 241, 0.5);
box-shadow: rgba(99, 102, 241, 0.15) 0px 4px 20px;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal.active {
transform: translateX(-50%) translateY(0px);
}
.customizer-toggle.active {
transform: rotate(180deg);
}
.customizer-overlay.active {
opacity: 1;
visibility: visible;
}
.customizer-panel.active {
right: 0px;
}
.bg-option.active {
border-color: var(--primary, #6366f1);
box-shadow: rgba(99, 102, 241, 0.3) 0px 0px 0px 2px;
}
.font-option.active {
background: rgba(99, 102, 241, 0.1);
border-color: var(--primary, #6366f1);
}
.option-btn.active {
background: rgba(99, 102, 241, 0.15);
border-color: var(--primary, #6366f1);
color: var(--primary, #6366f1);
}
.demo-card:hover .favorite-btn, .category-card:hover .favorite-btn, .favorite-btn.active {
opacity: 1;
}
.favorite-btn.active {
color: rgb(251, 191, 36);
}
.code-panel-overlay.active {
opacity: 1;
visibility: visible;
}
.code-panel.active {
opacity: 1;
visibility: visible;
transform: translate(-50%, -50%) scale(1);
}
.code-tab.active {
color: rgb(167, 139, 250);
background: rgba(167, 139, 250, 0.1);
}
.code-tab.active::after {
content: "";
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
height: 2px;
background: rgb(167, 139, 250);
}
.code-editor.active {
display: flex;
flex-direction: column;
}
function toggleAccordion(header, type) {
const item = header.parentElement;
const accordion = item.parentElement;
if (type === 'single') {
// Close all other items in single mode
accordion.querySelectorAll('.accordion-item').forEach(otherItem => {
if (otherItem !== item) {
otherItem.classList.remove('active');
}
});
}
item.classList.toggle('active');
}
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| type in toggleAccordion(this, type) | 'basic' | Pass 'single' to allow only one section open at a time; 'basic' allows multiple open simultaneously. |
| .accordion-item.active .accordion-content { max-height } | 200px | Maximum expanded height. Increase if your content exceeds 200 px (code blocks, long forms). |
| .accordion-content { transition: max-height } | 0.4s | Open/close animation duration. 0.25s = snappy, 0.6s = slow and smooth. |
| .accordion-icon { transition: transform } | 0.3s | Chevron rotation duration — adjust in sync with the content transition. |
| .accordion-item { background } | rgba(255,255,255,0.05) | Background of each section. Replace with a brand color or transparent for a borderless accordion. |
| .accordion-single class on .accordion | absent | Enables the indigo border variant + glow on the active item. No extra JS required, HTML attribute only. |
| .active class on .accordion-item at load | first item | Pre-opens the marked item on initial render — remove it from all items to start fully collapsed. |
FAQ
Why does the animation stop before all the content is visible?
The max-height is fixed at 200px in the CSS. If your content exceeds that, the section opens to 200 px and clips the rest. Increase max-height on .accordion-item.active .accordion-content to the actual height of your tallest content block.
Can multiple independent accordions coexist on the same page?
Yes. The toggleAccordion function walks up via parentElement to the direct parent accordion — each .accordion is isolated. Multiple instances work side by side without conflict, even with different modes ('basic' and 'single' mixed on the same page).
Does the effect respect prefers-reduced-motion?
No, not in the base version. CSS transitions run regardless of the system preference. To fix this, add: @media (prefers-reduced-motion: reduce) { .accordion-content, .accordion-icon { transition: none; } }