Stop Hand-Rolling an Exclusive Accordion. <details name> Does It
You've probably shipped an accordion with a hand-rolled 'close the other panels' loop in JavaScript. The `name` attribute on <details> does it natively — no JS, no ARIA plumbing.

You're building an FAQ page. Ten questions, each collapsed by default, and — this part's in every design spec — only one answer open at a time. Click a second question, the first one closes. Standard accordion behavior, the kind you've built before you finish reading the ticket.
So you reach for <details> and <summary>, because it's the native disclosure widget and you'd rather not hand-roll aria-expanded toggling again. Ten of them render fine. Then you click the second one open while the first is still open, and both sit there, expanded, taking up the whole viewport. <details> never promised mutual exclusivity — every element manages its own open state, full stop.
So you write the loop everyone writes.
It works. It's also more surface area than it looks: a querySelectorAll scoped to a class you have to remember to apply consistently, an event listener per item, a forEach over every item each time any panel opens, whether or not anything needs to close, and a bug waiting for the first time someone copy-pastes an FAQ block without re-running the script — say, into content injected after the page loads. Miss the re-init and you're back to every panel opening independently, silently, with no error to tell you why.
And that's the simple version. The moment product wants nested accordions, or two independent FAQ groups on the same page that shouldn't interact, this script needs scoping logic, and now it's a small state machine you're maintaining for something that reads, in the design file, as "an accordion."
Since late 2023 (Chrome 120, Safari 17.2; Firefox followed in 130, September 2024), <details> takes a name attribute. Give a group of them the same name, and the browser enforces the mutual exclusivity itself — open one, the others in that group close, automatically, with zero JavaScript:
That's the whole implementation. It behaves like a radio group built out of disclosure widgets: same name value ties them together, opening one closes every other member of the group, and it works the instant you add another <details name="faq"> anywhere on the page — no re-running a script, no re-scoping a selector.
It's Baseline-available across current Chrome, Edge, Firefox, and Safari, which means you can rely on it in a production FAQ page today. Where support is missing, the fallback isn't broken — it's just every <details> reverting to independent behavior, exactly like today, so nothing crashes on an older browser. That's the quiet advantage of building on a native element instead of a div with ARIA bolted on: the un-enhanced state is still a working, keyboard-accessible disclosure widget, not a blank div waiting on JS that never ran.
I expected the grouping to require the elements to be siblings, or at least share a parent container — that's how the radio-input-plus-CSS trick works, and it's how most accordion libraries assume you'll structure the DOM. It doesn't. The name attribute groups by value alone (within one DOM tree — a shadow root starts its own groups). Two <details name="settings"> elements in completely different sections of the page, with unrelated markup between them, are still one exclusive group.
The spec allows it but advises against leaning on it: authors "should keep those related elements together in a containing element," because panels that close each other from disparate corners of a page have a relationship that's hard to discover — especially with a screen reader. It's mostly the thing to watch for: reuse a name by accident — paste a component, forget to change the group id — and two accordions you meant to be independent start closing each other's panels, and there's no console warning telling you why.
Native grouping replaces the sibling-closing loop, not everything you might want an accordion to do. You'd still reach for a script if you need to:
- Open a panel from outside a click — a "Contact support" button that opens the relevant answer still means setting
.open = trueyourself (the group then closes the others). Plain anchor links are the exception: in current browsers, following a#idthat points at content inside a closed<details>expands it natively, and thenamegroup closes whichever panel was open. - Persist state — remembering which panel was open across a reload is your
localStoragewrite, same as any other piece of UI state. - React to which one opened — analytics on "which FAQ gets clicked most" still listens for the
toggleevent; checkevent.newState === "open", because the sibling the browser auto-closes fires its owntoggletoo (withnewState: "closed").
None of that is the accordion's core behavior, though — it's things you'd bolt onto any accordion, native or not. The mutual-exclusivity plumbing, the part that used to be your bug to maintain, is gone.
Try both versions side by side below: the naive JS-loop accordion and the name-grouped native one, with a toggle to break the name value and watch two "independent" groups start fighting over the same state.
Runs right in your browser — poke at it and watch the concept react live.
<details> picked up name grouping the same way <dialog> picked up native modal semantics and <input type="date"> picked up a whole calendar widget: the platform absorbing a pattern that used to be userland plumbing. The tell that it's worth checking MDN before you write the loop is exactly this shape of problem — "state that needs to stay consistent across a group of elements" — because that's precisely the kind of thing HTML has been quietly annexing from JavaScript for a few years now.
Next time you're about to write a forEach that closes siblings, checks a data- attribute, or manages "only one of these is open at a time," it's worth thirty seconds on MDN before the twentieth line of the script. Sometimes the browser already shipped it.
What's the last bit of state-management JavaScript you wrote that turned out to already be a native HTML attribute? I've got a second one of these for popover's popovertarget — tell me yours first.
Think it clicked? Take the 7-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
- The Modal Was Open. So Was Everything Behind It.
- Stop Using outline: none. Use :focus-visible Instead.
- You built your modal with a
<div>and a focus trap library. The native<dialog>does all of that.
🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Keep reading
One post a day, in your inbox
Each one with a runnable playground and a quiz. No pitch, no digest, unsubscribe in one click.
0 comments
Sign in to join the discussion, like comments, and save articles for later.