For this project I came up with 2 examples, one with only CSS and one with a bit of JavaScript. For a sighted user these are identical. A screen reader user will notice one uses a checkbox and the other uses a button to interact with.
Neither of these examples work in Internet explorer. The newer CSS properties used are not supported in Internet Explorer. In the CSS we'll make sure the functionality degrades gracefully.
After the examples we'll go over the HTML, CSS, and Javascript used in the following segments:
Links to documentation: The opening tags in HTML link to the relevant MDN web docs pages. In the CSS examples the property names, and pseudo classes and elements link to their MDN web docs pages.
The examples
The basics
Both methods are very alike, most CSS is the same, the CSS only version uses a few extra selectors.
First we make sure our wrapping div hides any overflow that might occur due to the blur effect. And we make sure it doesn't have any unwanted margin or padding. We'll set position: relative;
to aid in our positioning of the (pseudo)button.
div.spoiler {
overflow: hidden;
position: relative;
margin: 0;
padding: 0;
}
We will use the filter
property for images in a spoiler wrapper. You can combine filters, and here we use both a blur and grayscale effect. I added a transition time to make the switch a bit smoother. vertical-align: bottom;
prevents a small bottom margin that may appear beneath an image.
div.spoiler img {
vertical-align: bottom;
filter: blur(20px) grayscale(100%);
transition: 0.3s;
}
For method one we'll use a label for our pseudo button, method two uses a real button. The positioning makes sure the element is centered both vertically and horizontally. The rest is styling to make it stand out from the image. The background color assures we have adequate contrast for the text.
.spoiler-button {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 3rem;
line-height: 3rem;
width: 50%;
padding: 0 1rem;
font-size: 1rem;
margin: auto;
background-color: rgba(0,0,0,0.8);
color: #FFF;
text-align: center;
border-radius: 1rem;
cursor: pointer;
}
Now to hide our (pseudo) button in Internet Explorer. We use a media query that will only apply to Internet Explorer.
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.spoiler-button {
display: none;
}
}
Method one: spoiler protection using only CSS and a hidden checkbox
Our HTML is simple, we wrap the image we need protected in a div
. We add a checkbox
before the image (this order is important for our CSS) and add our pseudo button in the form of a label
.
Using the id and for attributes on the checkbox and label makes sure these are linked. This means the checkbox gets checked when the label is clicked. It also makes sure the label text is spoken by screen readers when the checkbox receives focus. Make sure the id is unique, perhaps by using the filename of the image.
<div class="spoiler">
<input id="spoiler-check" type="checkbox">
<img src="pathtoimage.jpg" alt="Your alt text, very important!">
<label for="spoiler-check" class="spoiler-button">Click to show image</div>
</div>
We will visually hide our checkbox, we do this by giving it zero width and height. You might be tempted to use display: none;
, don't! This makes the element inaccessible for screen readers. We will use this property when the checkbox is checked in a moment.
div.spoiler input {
width: 0;
height: 0;
}
To remove the filter effect on the image when the checkbox is checked, we use the following selector. The ~
operator targets images preceded by a checked checkbox with the same parent.
div.spoiler input:checked ~ img {
filter: none;
}
We'll also want to hide our label and checkbox like this. Notice we use the ~
operator again for the label.
div.spoiler input:checked, div.spoiler input:checked ~ label {
display: none;
}
For this method we will want to hide both our label and checkbox in Internet Explorer. For this we add an extra selector to the media query that will only apply to Internet Explorer.
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
div.spoiler input, .spoiler-button {
display: none;
}
}
Method two: spoiler protection using a button and JavaScript
Our HTML doesn't differ very much from method one. We will use a button
instead of a label
, and we won't need the checkbox anymore. A button
is not only semantically correct, it's functionality will save us some code.
<div class="spoiler">
<img src="pathtoimage.jpg" alt="Your alt text, very important!">
<button class="spoiler-button" onclick="unspoiler(this)">Click to show image</button>
</div>
The button calls a function unspoiler(this)
on a click. Providing the this
as an argument let's us use it in the function. This function adds a class to our spoiler wrapper (the parent element of the image and button). Hiding the button and removing the filter is done using CSS.
function unspoiler(spoiler) {
spoiler.parentNode.classList.add('nospoiler');
}
Using this JavaScript to add the class, we can use this class in our CSS to remove the filter effect like this.
div.nospoiler img {
filter: none;
}
And we'll hide the button like this.
div.nospoiler button {
display: none;
}
Notes on accessibility
When hiding images to prevent triggers, I suggest also providing captions. With a bit of context your visitor can make an informed decision to disable the protection.