Click to Reveal Spoilers Using Only CSS
Opening
Ever wanted to toggle content without JavaScript? Maybe out of principle, maybe out of spite. Enter the CSS checkbox hack, a mildly ingenious, slightly cursed way to show and hide things using nothing but HTML and CSS. No scripts, no event listeners, just an invisible checkbox doing all the heavy lifting. By pairing it with a label, we can use the :checked pseudo-class to create click-to-reveal spoilers with no JavaScript.
Drawbacks
Of course, no hack is perfect. The CSS checkbox method comes with a few quirks, like the fact that it only works if the user actually interacts with the label, no programmatic toggling here. It also doesn’t support complex animations (without a lot more pain), so don’t expect fancy fade-ins. And if you’re using multiple checkboxes, the code can get messy fast. But hey, if you’re already committing to this approach, a little chaos is probably part of the appeal.
Example #1 : Simple
Content warning: Very gay
meow :3<details>
<summary>Content warning: Very gay</summary>
meow :3
</details>
For this solution you don't even need any CSS and the checkbox trick, but in my opinion it doesn't look very nice.
Example #2 : Fancy
<style>
.spoiler-checkbox {
display: none;
}
.spoiler-label {
background: #323232;
color: white;
padding: 10px;
cursor: pointer;
display: inline-block;
}
.spoiler-label::after {
content: "Click to reveal";
}
.spoiler-content {
display: none;
color: white;
background: #333;
padding: 10px;
margin-top: 5px;
}
#spoiler1-check:checked ~ #spoiler1-label::after {
content: "miauuuu :3";
}
</style>
<input id="spoiler1-check" class="spoiler-checkbox" type="checkbox">
<label id="spoiler1-label" for="spoiler1-check" class="spoiler-label"></label>
The nice thing about this approach is that you only need to write the CSS for the classes once, making it very clean and efficient. You can reuse the same styles for multiple spoiler sections simply by adding new checkboxes with different IDs, and the styles will be applied consistently. This makes the code much more manageable, especially when you're dealing with multiple spoilers on the same page. You can also easily update the design or behavior of the spoilers globally by modifying the shared CSS classes, without needing to alter each individual spoiler block.
Example #3 : Beyond text
<style>
.image-spoiler-checkbox {
display: none;
}
.image-spoiler-label {
background: #323232;
color: white;
padding: 0;
cursor: pointer;
display: inline-block;
text-align: center;
position: relative;
}
.image-spoiler-label::after {
content: "Click to reveal image";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.image-spoiler-content {
width: 100%;
height: 100%;
filter: blur(15px);
}
#spoiler2-check:checked ~ #spoiler2-label::after {
content: "";
}
#spoiler2-check:checked ~ #spoiler2-label img{
filter: none;
}
</style>
<input id="spoiler2-check" class="image-spoiler-checkbox" type="checkbox">
<label id="spoiler2-label" for="spoiler2-check" class="image-spoiler-label">
<img src="/static/article-assets/fish.jpg" class="image-spoiler-content">
</label>
To make it even more cursed but undeniably cool, let's move on to images. With this method, you can blur an image behind a spoiler, hiding it until the user decides to click and unveil it. All of the stuff in the last example also applies. If you wanted you could also just hide it completely instead of blurring it.
In conclusion
The CSS checkbox hack is a nice way to hide and reveal content on a webpage without the need for JavaScript, and it works surprisingly well for all sorts of content. While it comes with quite a few limitations, it’s still a fun, minimalist approach that gets the job done; I actually used it to hide and show the navigation bar on the mobile version of this website.
Feel free to use any of the code here for whatever purpose, it's permissively cuck licensed. I’d love to see what other cool things people can do with using this technique, so go ahead and surprise me!
P.S. If anyone can figure out a solution for syntax highlighting code blocks entirely in a Rust backend, please, by all means, share it with the world. I’m desperately waiting for someone to make that happen... :(