Don't Forget About <details>! The Hidden Power of HTML: How <details> Transforms Static Pages into Interactive Interfaces?
In the era of complex JavaScript frameworks, we often forget about the simple and powerful capabilities of pure HTML. Today let’s talk about a tag that can radically simplify your development.
The best code is the one you don’t need to write.
<details> is a native HTML element for creating collapsible content blocks. No JavaScript, no external libraries — pure semantics and accessibility out of the box.
<details>
<summary>Click to discover the secret</summary>
<p>You don't need JavaScript anymore! 🎉</p>
</details>You don't need JavaScript anymore! 🎉
Result: an interactive element that works everywhere — from old browsers to screen readers.
Before:
// 50+ lines of code for a simple accordion
document.querySelectorAll('.faq-item').forEach(item => {
item.addEventListener('click', function() {
this.classList.toggle('active');
const content = this.querySelector('.faq-content');
content.style.maxHeight = content.style.maxHeight
? null
: content.scrollHeight + 'px';
});
});After:
<details class="faq-item">
<summary>How to cancel subscription?</summary>
<p>Go to Profile Settings → Subscriptions → Cancel</p>
</details>
<details class="faq-item">
<summary>Can I get a refund?</summary>
<p>Yes, within 14 days of purchase</p>
</details>Go to Profile Settings → Subscriptions → Cancel
Yes, within 14 days of purchase
<nav class="docs-nav">
<details open>
<summary>🚀 Quick Start</summary>
<ul>
<li><a href="#install">Installation</a></li>
<li><a href="#config">Configuration</a></li>
<li><a href="#first-app">First Application</a></li>
</ul>
</details>
<details>
<summary>📚 Guides</summary>
<ul>
<li><a href="#basics">Basics</a></li>
<li><a href="#advanced">Advanced Topics</a></li>
<li><a href="#best-practices">Best Practices</a></li>
</ul>
</details>
</nav><article class="recipe">
<h2>Roman Carbonara</h2>
<details>
<summary>📝 Ingredients (4 servings)</summary>
<ul>
<li>400g spaghetti</li>
<li>200g guanciale (or pancetta)</li>
<li>4 egg yolks + 1 whole egg</li>
<li>100g pecorino romano</li>
<li>Black pepper</li>
</ul>
</details>
<details>
<summary>👨🍳 Step-by-step recipe</summary>
<ol>
<li>Cut guanciale into cubes</li>
<li>Fry until crispy</li>
<li>Cook pasta al dente</li>
<li>Mix eggs with grated cheese</li>
<li>Combine everything off the heat</li>
</ol>
</details>
<details>
<summary>💡 Chef's secrets</summary>
<p>The key is not to let the eggs curdle. Remove the pan from heat before adding the egg mixture!</p>
</details>
</article>details {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
details[open] {
background-color: #f8f9fa;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
summary {
cursor: pointer;
font-weight: 600;
list-style: none;
position: relative;
padding-left: 2rem;
}
/* Custom arrow */
summary::before {
content: '▶';
position: absolute;
left: 0;
transition: transform 0.3s;
}
details[open] summary::before {
transform: rotate(90deg);
}/* Animated expansion */
details {
--animation-duration: 300ms;
--animation-timing: cubic-bezier(0.4, 0, 0.2, 1);
}
details summary {
display: flex;
align-items: center;
gap: 0.5rem;
}
details[open] summary {
margin-bottom: 1rem;
}
/* Smooth content appearance */
details > *:not(summary) {
animation: slideDown var(--animation-duration) var(--animation-timing);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}Wow! You've just seen how a simple <details> transforms into a beautiful interactive element!
This example demonstrates:
And all this without a single line of JavaScript!
<div class="accordion" data-exclusive>
<details name="accordion-group">
<summary>Section 1</summary>
<p>First section content</p>
</details>
<details name="accordion-group">
<summary>Section 2</summary>
<p>Second section content</p>
</details>
<details name="accordion-group">
<summary>Section 3</summary>
<p>Third section content</p>
</details>
</div>Minimal JS for exclusivity:
document.querySelectorAll('[data-exclusive] details').forEach(detail => {
detail.addEventListener('toggle', e => {
if (e.target.open) {
const parent = e.target.closest('[data-exclusive]');
parent.querySelectorAll('details').forEach(d => {
if (d !== e.target) d.open = false;
});
}
});
});// Remember open sections
document.querySelectorAll('details[data-persist]').forEach((detail, index) => {
const key = `details-state-${index}`;
// Restore state
const savedState = localStorage.getItem(key);
if (savedState === 'open') detail.open = true;
// Save on change
detail.addEventListener('toggle', () => {
localStorage.setItem(key, detail.open ? 'open' : 'closed');
});
});<details open>
<summary>📁 src/</summary>
<div class="file-tree">
<details>
<summary>📁 components/</summary>
<ul>
<li>📄 Header.jsx</li>
<li>📄 Footer.jsx</li>
<details>
<summary>📁 ui/</summary>
<ul>
<li>📄 Button.jsx</li>
<li>📄 Modal.jsx</li>
</ul>
</details>
</ul>
</details>
<details>
<summary>📁 pages/</summary>
<ul>
<li>📄 Home.jsx</li>
<li>📄 About.jsx</li>
</ul>
</details>
</div>
</details>| Approach | JS Size | Load Time | FCP | Accessibility |
|---|---|---|---|---|
| jQuery accordion | 89KB | 450ms | 1.2s | ⭐⭐⭐ |
| React component | 142KB | 680ms | 1.5s | ⭐⭐⭐⭐ |
| Vanilla JS | 12KB | 180ms | 0.8s | ⭐⭐⭐ |
| <details> | 0KB | 0ms | 0.4s | ⭐⭐⭐⭐⭐ |
Conclusion: the native solution wins in all parameters.
<details> and <summary> are examples of how the platform evolves, providing native solutions for common tasks.
By using semantic HTML instead of JavaScript hacks, we get:
Complexity is the enemy of reliability. Choose simple solutions when possible.
Next time, before reaching for jQuery or writing a React component, ask yourself: “What if just <details>?”
Want more articles about modern web development? Subscribe to EasyAdvice and discover the elegant simplicity of the web 🚀