Accordion FAQ — Free HTML CSS JS Snippet

Accordion / FAQ · Navigation · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

toggle() closes all .open items then conditionally opens the clicked one
Single-open-at-a-time: prevents multiple answers open simultaneously
max-height: 0 to 500px CSS transition on .answer — animates unknown content height
overflow: hidden clips content during animation
Chevron SVG rotate(180deg) transition indicates expand/collapse state
.item.open box-shadow adds visual depth to the expanded item
Initial open item triggered via dispatchEvent on page load
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Accordion FAQ — Single-Open Toggle, max-height Transition & Chevron Rotation

Screenshot of the Accordion / FAQ snippet rendered live

An accordion FAQ lets users expand one question at a time to read the answer, keeping the page compact while making all questions visible. The single-open pattern — collapsing the previous item when a new one opens — is the standard for FAQs, settings panels, and any list of collapsible content.

The toggle function

toggle(btn) finds the .item via btn.closest('.item'), checks if it has .open, then closes ALL open items with querySelectorAll('.item.open').forEach(i => i.classList.remove('open')). If the clicked item was not already open, it adds .open. This single-open-at-a-time pattern prevents the FAQ from becoming an incomprehensible wall of open answers.

The max-height CSS transition

The .answer div has max-height: 0; overflow: hidden; transition: max-height 0.3s by default. When .open is added, max-height: 500px applies. CSS animates between these values. Using max-height instead of height works because height: auto cannot be transitioned — max-height on a value large enough to never clip the content effectively animates the reveal.

The chevron rotation

The question row contains an SVG chevron. .item.open .q svg { transform: rotate(180deg) } with transition: transform 0.3s rotates the chevron when the item opens. This communicates the expand/collapse state at a glance.

Initial open state

The last dispatchEvent(new MouseEvent('click')) call in the JS opens the first item on page load. Remove this line to start with all items closed.

The max-height animation technique

CSS cannot animate from height: auto to height: 0 directly. The accordion uses max-height instead: the closed state has max-height: 0; overflow: hidden. The open state applies max-height: 500px (larger than any possible content). CSS transition: max-height 0.3s ease animates between these values. The transition feels natural even though max-height: 500px is much larger than the actual content height — the ease-out timing makes it decelerate near the end, which coincidentally matches when the content is near its natural height.

Chevron rotation

The chevron uses transform: rotate(180deg) when .open is applied to the parent item. CSS transition: transform 0.25s eases the rotation. This provides visual feedback that the item is a toggle without requiring a separate open/close icon pair.

Single-open accordion behaviour

The toggle() function closes all items before opening the clicked one: document.querySelectorAll('.faq-item').forEach(el => el.classList.remove('open')). This ensures only one answer is visible at a time — the accordion pattern. Remove this close-all block to allow multiple items open simultaneously if your FAQ content benefits from side-by-side comparison.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to puzzle out the timing tricks alone — paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why max-height is used instead of height for the answer transition, and why the value is set to a fixed number like 200px rather than something dynamic. The same assistant can help optimize it — for instance asking whether a ResizeObserver-based measurement would be more robust than a hardcoded max-height for answers of very different lengths. It's just as useful for extending the component: ask it to add aria-expanded and aria-controls for full accessibility, animate multiple items open at once by removing the close-all loop, or wire it up to FAQ schema JSON-LD automatically from the same data. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a single-open accordion FAQ in plain HTML, CSS, and JavaScript — no libraries, using only class toggling and a max-height transition for the expand/collapse animation.

Requirements:
- A list of question/answer items, each with a button header (containing the question text and a chevron SVG icon) and a hidden answer panel below it.
- Clicking a question's button must first remove an "open" class from every other item, then toggle it onto the clicked item only if it wasn't already open — so at most one answer is expanded at any time, and clicking an already-open item closes it.
- The answer panel must start with max-height: 0 and overflow: hidden, and transition to a max-height value large enough to never clip the tallest expected answer when the "open" class is present — explain in a comment why height: auto cannot be transitioned and max-height is the workaround.
- The chevron icon must rotate 180 degrees with its own CSS transition when its parent item gains the "open" class, so it visually points the opposite direction when expanded.
- One item should be open by default when the page loads (not all closed).
- Do not use any animation or accordion library — the entire interaction should be driven by one small toggle function that manipulates classList.

Step by step

How to Use

  1. 1
    Click each questionClick question headers to expand answers. Clicking a new question collapses the current open item — single-open pattern.
  2. 2
    Update questions and answersIn the HTML panel, change the .q button text and .a div content for each .item.
  3. 3
    Add more itemsCopy an .item div and paste it inside .faq. The toggle function picks up any .item with a .q button.
  4. 4
    Change animation speedUpdate 0.3s on transition: max-height in the CSS panel.
  5. 5
    Allow multiple open itemsIn the JS panel, remove the querySelectorAll close-all loop to allow multiple items to be open simultaneously.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Product FAQ and help center pages
The definitive use case — see the pricing FAQ and the searchable FAQ accordion. Accordion FAQs reduce page height while making all questions scannable. Users expand only the answers they need.
Settings and preferences panels
Group related settings into collapsible sections inside a settings panel. Users can expand only the category they are changing without seeing unrelated settings.
Learn max-height animation trick
The transition animates from 0 to a large fixed value because height: auto cannot be transitioned. Edit the max-height value to understand how it clips or fails to clip content.
Landing page feature details
Show a brief feature name in the collapsed state. Expanding reveals full details. This pattern converts curiosity into engagement.
Combine with Schema FAQ markup
Wrap the accordion in FAQ Page schema JSON-LD to get Google rich results. The accordion HTML structure maps directly to Question and Answer schema types.
Keyboard accessible toggle
The button element is keyboard focusable and the toggle fires on click. Add aria-expanded and aria-controls for full ARIA accordion compliance.

Got questions?

Frequently Asked Questions

toggle() first closes all elements with .open via querySelectorAll(".item.open").forEach(i => i.classList.remove("open")). Then if the clicked item was not already open, it adds .open. Clicking an already-open item closes it without reopening.

height: auto cannot be CSS-transitioned. max-height on a value larger than the content (500px) acts as height: auto — the element shows its full height. Setting max-height: 0 with overflow: hidden collapses it. CSS transitions can animate between these two max-height values.

Remove the querySelectorAll(".item.open").forEach(i => i.classList.remove("open")) loop from toggle(). Only the final if (!isOpen) item.classList.add("open") needs to remain.

Add a <script type="application/ld+json"> with @type: FAQPage and mainEntity array. Each item in the array has @type: Question, name (the question text), and acceptedAnswer.text (the answer). Google uses this for rich results in search.

Add aria-expanded="false" to the .q button and toggle it to "true" when .open is added. Add aria-controls="answer-id" pointing to the .answer div id. Add role="region" and aria-labelledby to the answer container.

Yes. Click "JSX" for a React component. In React, manage an openIndex state. Compare each item index to openIndex to determine the .open class and chevron rotation. Set openIndex to null or to the clicked index.