title: Add Responsive-Friendly Enhancements to `details` with `details-utils`-zachleat.com
url: https://www.zachleat.com/web/details-utils/
hash_url: ce44e844633f17f25d53f44c1ebd35e7

<p>I use <code>&lt;details&gt;</code>. I use <code>&lt;details&gt;</code> a lot. It is one of my favorite HTML elements.</p><p>Over time I’ve collected a bunch of add-on utilities to enhance <code>&lt;details&gt;</code> with new features and functionality. They’ve been super useful in <em>a bunch</em> of long-standing production implementations at Netlify:</p><p>I’ve decided to finally package those <code>&lt;details&gt;</code> helpers up and formally release them as a web component!</p><h2 id="lessdetails-utilsgreater" tabindex="-1"><code>&lt;details-utils&gt;</code> <a class="direct-link" href="#lessdetails-utilsgreater" aria-hidden="true">#</a></h2><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br>  <br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre><p>At time of writing, this web component adds five new responsive-friendly enhancements to one or more <code>&lt;details&gt;</code> elements nestled inside:</p><ul><li>Force open/closed</li><li>Click outside to close</li><li>Close on <code>esc</code></li><li>Animate open/closed</li><li>Toggle root element <code>class</code></li></ul>
<h3 id="force-openclosed" tabindex="-1">Force open/closed <a class="direct-link" href="#force-openclosed" aria-hidden="true">#</a></h3><p>In this example, the <code>&lt;details&gt;</code> is forced open when viewport is wider than <code>48em</code>.</p><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">force-open</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width: 48em)<span class="token punctuation">"</span></span> <span class="token attr-name">force-restore</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span> <span class="token attr-name">open</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre><p>I’ve gotten <em>a lot</em> of mileage out of the above example, specifically to drive navigation that is always visible at a certain breakpoint (think a collapsible menu at small viewport versus sidebar, e.g. <a href="https://www.11ty.dev/docs/"><code>11ty.dev/docs/</code></a>).</p><p>Alternatively, <code>force-close</code> is also available. The optional <code>force-restore</code> attribute will restore previous state when the <code>force-open</code> or <code>force-close</code> media queries do not match.</p><p>The media query is optional, and using it as a bare attribute allows control of the state pre and post JavaScript.</p><pre class="language-html"><code class="language-html"><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">force-open</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span><br><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">force-close</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span> <span class="token attr-name">open</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre>
<h3 id="click-outside-to-close" tabindex="-1">Click outside to close <a class="direct-link" href="#click-outside-to-close" aria-hidden="true">#</a></h3><p>If you click anywhere on the document (outside of the <code>&lt;details&gt;</code> content), the <code>&lt;details&gt;</code> will be closed. This is useful when you want to absolutely position the <code>&lt;details&gt;</code> content (maybe to make a little custom dropdown 😱)</p><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">close-click-outside</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre><p>You can scope this with a media query as well:</p><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">close-click-outside</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width: 48em)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre><p>Add your own bonus close button inside of the content (to complement <code>&lt;summary&gt;</code>):</p><pre class="language-css"><code class="language-css"><br><span class="token selector">details-utils:not(:defined) [data-du-close-click]</span> <span class="token punctuation">{</span><br>  <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">close-click-outside</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-details<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>summary</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>summary</span><span class="token punctuation">&gt;</span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-details<span class="token punctuation">"</span></span> <span class="token attr-name">data-du-close-click</span><span class="token punctuation">&gt;</span></span>Close<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre>
<h3 id="close-on-esc" tabindex="-1">Close on <code>esc</code> <a class="direct-link" href="#close-on-esc" aria-hidden="true">#</a></h3><p>Closes the <code>&lt;details&gt;</code> when the <code>esc</code> key is hit on the keyboard. Media query is optional.</p><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">close-esc</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(max-width: 767px)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre>
<h3 id="animate-openclose" tabindex="-1">Animate open/close <a class="direct-link" href="#animate-openclose" aria-hidden="true">#</a></h3><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">animate</span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre><p>Animates the height of the content when opening and closing the <code>&lt;details&gt;</code>. Ignored automatically if <code>(prefers-reduced-motion)</code> is detected.</p><p>Just a full disclosure, the configuration around this one is pretty limited (re: easing and timing). Also this doesn’t support media query scoping yet (not for any technical reason, just haven’t run into this use case yet). Open to contributions here!</p>
<h3 id="toggle-class-on-root-element" tabindex="-1">Toggle <code>class</code> on root element <a class="direct-link" href="#toggle-class-on-root-element" aria-hidden="true">#</a></h3><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details-utils</span> <span class="token attr-name">toggle-document-class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-class-name<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>details</span><span class="token punctuation">&gt;</span></span>…<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details</span><span class="token punctuation">&gt;</span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>details-utils</span><span class="token punctuation">&gt;</span></span></code></pre><p>Adds a <code>class</code> to your <code>&lt;html&gt;</code> element when the <code>&lt;details&gt;</code> is open and removes it when the <code>&lt;details&gt;</code> is closed.</p><h2 id="enjoy!" tabindex="-1">Enjoy! <a class="direct-link" href="#enjoy!" aria-hidden="true">#</a></h2><p>Wiring up and combining each of these enhancements to <code>&lt;details&gt;</code> really can go a long way in building a lot of complex user interface elements in a pretty straightforward way. In my humble opinion, the super long list of things I’ve built using this is proof of that. I hope you can get some useful mileage out of them too!</p>
