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.
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 ::after
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'">
...
</details>
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:
- Little code: it's literally adding an attribute in HTML and 8 lines of CSS.
- Standard HTML: it is supported by all major browsers (basically, everything but IE and old versions of Edge.)
-
Content can be rehidden: if we want to hide the content, we can click again on the
<summary>
.
...and some not-so-good things about it:
- Works better with little content: this solution is for single paragraphs or images. It would be tricky to hide large chunks of text.
-
May require additional styling: we will need to update the CSS for the
::after
to match the style of the content inside the<details>
so there's no "jump effect" on show/hide.
Top comments (4)
I would go with a label/input configuration like this: codepen.io/t_afif/pen/dyNwNyQ where you don't need to duplicate the content.
That is a great idea, and not having to duplicate the content is a big plus.
Honestly the third codepen needs more blur there. Any kind of NSFW content would be easily seen. And obviously, some people will accidentally click on the details tag.
This gonna work great with frequently asked questions sections.