
The Accordion That Finally Worked: How the New ::details-content Pseudo-Element Solved My Styling Nightmares
Stop reaching for heavy JavaScript libraries just to animate a simple drawer; discover how the new native pseudo-element finally makes styling the details tag a breeze.
The Accordion That Finally Worked: How the New ::details-content Pseudo-Element Solved My Styling Nightmares
I’ve spent an embarrassing amount of time over the last decade trying to make the humble accordion behave. It’s one of those UI components that seems like it should be a "solved problem," yet we always end up reaching for a 15kb JavaScript library just because the native HTML <details> element refused to play nice with CSS transitions.
For years, the <details> and <summary> tags were a bit of a "look but don't touch" situation. You could use them for basic functionality, but the moment a designer asked for a smooth height animation or a specific internal layout, you were back to writing custom state logic in React or Alpine.js.
That just changed. With the introduction of the ::details-content pseudo-element, we finally have a direct line to the "stuff" inside the drawer.
The "Extra Div" Tax
If you’ve ever tried to style the contents of a <details> element, you know the struggle. You couldn't style the container that holds the content separately from the <details> wrapper itself. This led to what I call the "Extra Div Tax."
To get any decent padding or layout control, your HTML usually looked like this:
<details>
<summary>Click to see my secrets</summary>
<div class="content-wrapper">
<!-- You needed this div just to apply padding or flexbox -->
<p>Finally, some room to breathe.</p>
</div>
</details>Without that inner <div>, applying padding to the <details> tag would often mess up the spacing around the <summary>, making the whole thing look broken.
Enter ::details-content
The ::details-content pseudo-element targets the internal container that holds everything *except* the <summary>. It’s essentially the browser providing that "content-wrapper" for you automatically.
Now, we can keep our HTML lean and mean:
<details class="modern-accordion">
<summary>The Lean Way</summary>
<p>No extra wrapper div required here. Just pure, unadulterated content.</p>
</details>And the CSS becomes much more intuitive:
/* Style the outer shell */
.modern-accordion {
border: 1px solid #ddd;
border-radius: 8px;
}
/* Style the clickable header */
.modern-accordion summary {
padding: 1rem;
cursor: pointer;
background: #f9f9f9;
}
/* Target the hidden content directly! */
.modern-accordion::details-content {
padding: 1rem;
background: white;
border-top: 1px solid #ddd;
}The Holy Grail: Animating to "Auto"
The biggest headache with accordions has always been animation. You can't transition height: 0 to height: auto. It just snaps. We used to hack this with max-height, but that always felt like a brittle workaround because you had to guess how tall the content might be.
Combined with the new interpolate-size property (or using calc-size), ::details-content makes native animations a reality.
Here is how you can finally animate an accordion with zero JavaScript:
/* Enable size interpolation for the document */
:root {
interpolate-size: allow-keywords;
}
.modern-accordion::details-content {
/* Set the starting point */
height: 0;
overflow: hidden;
transition: height 0.3s ease-out, padding-block 0.3s ease-out;
}
/* When the parent is open, animate the pseudo-element */
.modern-accordion[open]::details-content {
height: auto;
padding-block: 1rem;
}Why this matters: When the [open] attribute is toggled on the <details> element, the browser now knows it can transition the height of the ::details-content pseudo-element. No ResizeObservers, no "measuring the DOM" with JS, just pure CSS.
Layout Freedom
Since ::details-content acts like a real box in the CSS box model, you can change its display property. Want the inside of your accordion to be a CSS Grid? Easy.
.grid-accordion::details-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}Before this, changing the display on the <details> tag itself usually broke the way the <summary> was rendered (often turning it into a grid item alongside its own content, which is almost never what you want).
A Quick Reality Check (Browser Support)
I’d love to tell you this works everywhere, but we aren't quite there yet. ::details-content is a relatively new addition (landing in Chrome/Edge 131).
If you're building for a production environment that needs to support older browsers, you'll still need a fallback. The good news? The fallback is just the old "Extra Div" method. You can write your styles so that they progressively enhance:
/* Fallback for the old way */
.content-wrapper {
padding: 1rem;
}
/* New way for modern browsers */
@supports selector(::details-content) {
.content-wrapper {
padding: 0; /* Strip the wrapper padding */
}
details::details-content {
padding: 1rem;
}
}Why I'm Excited
I’ve always felt that the more we can move away from "JS-for-layout," the better the web becomes. Using JavaScript to animate a drawer isn't just a performance tax; it's a maintenance tax. Every time you add a library, you’re adding something that can break, needs updating, or blocks the main thread.
::details-content feels like the CSS Working Group finally looked at a decade of StackOverflow questions and said, "Yeah, we should probably fix that." It’s a small change that removes a massive amount of friction.
Give it a try in the latest Chrome. It’s one of those features that, once you use it, makes you wonder how we ever survived without it.


