A Bit Of Access

Providing image spoiler protection

Published by

Using spoiler protection is not only a good way to prevent spoilers. It is also a good method to hide images which may be shocking to your visitors. In this article we'll go about making spoiler protection for images, with and without JavaScript.

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:

  1. Method 1: using only CSS and a hidden checkbox
  2. Method 2: using a button and JavaScript

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

A pollen-covered bumblebee on a purple flower against a blurry green background
The spoiler protection using only CSS. The picture features a bumblebee.
A small bright green grasshopper on a yellow flower with purple rimmed petals
The spoiler protection using JavaScript. The picture features a grasshopper.

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.

Relevant documentation: