Spoiler/sensitive sections in HTML and CSS

Sometimes we need to hide parts of the page from the users. It may be a spoiler or sensitive content and pictures that we want users to approve before displaying them.

There are a couple of HTML elements that will natively do what we want: <details> and <summary>. They have native collapsed/expanded states that the browser handles to show/hide the information, so we don’t need any JavaScript.

See them in action here:

They work fine, but the default style looks bland and simple. Also, the content shifts up and down when we expand and collapse the details, which is a bit annoying.

Instead of hiding the content, we could try displaying it blurred when the details are collapsed and normal when the details are open. Unfortunately, at the moment, there is no way to show the content when the details are collapsed.

With a bit of ingenuity, we can simulate that behavior… and it will only require a handful of lines of CSS code!

We want to show the content, but we cannot. Instead, we can show a pseudo-element like with the exact content of the <details>.

A CSS variable will have a value with the content of <details> (without the <summary>). Which will require a small change in the HTML:

<details class="spoiler" style="--hidden: 'Text to be hidden'">

Why a variable and not a data attribute? Because data attributes are interpreted as text. They worked fine with text content, but they didn’t play nice when we wanted to hide an image.

Then on CSS, we will read that variable and put it in the ::after pseudo-element, applying a filter on close, and hiding when the <details> is expanded:

details.spoiler::after {
content: var(--hidden);
filter: blur(4px);

details[open]::after {
display: none;

This makes our new spoiler section look like this:

It looks a lot better… but it has a big problem: it doesn’t work on Firefox! While Chrome and Safari show the ::after of the <details>, Firefox doesn't because it considers that it is inside the collapsed <details> (which, to be honest, should be the expected behavior anyway.)

To go around it, we can change the CSS a little, so instead of using the ::after of the <details>, we use the ::after of the <summary>.

details.spoiler summary::after {
content: var(--hidden);
filter: blur(4px);
display: block;

details[open] summary::after {
display: none;

The result is on this Codepen:

Now this works in all major browsers (Chrome, Firefox, and Safari) and, as we are using the ::after in the <summary>, it has the added feature that it will show the hidden content when we click on the actual blurred content (before it was only on the warning area.)

This is a way to create a spoiler section that would be great for hiding sensitive content or spoilers. And it only uses standard HTML and 8 lines of CSS! The browser handles all the logic for showing/hiding without needing any JavaScript.

Some good things about this solution:

…and some not-so-good things about it:

Originally published at https://alvaromontoro.com on April 22, 2021.

Full-Stack Software Engineer, Mobile Developer, Web technologies enthusiast. CSS aficionado. Twitter: @alvaro_montoro

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store