⚠️ Don't try this at home: CSS-only image detail zoom - As hacky as possible! 🖼️🔍
Pascal Thormeier
Posted on November 6, 2021
No, seriously, don't. We'll be hacking our way through. This will involve a lot of dirty hacks and bad practices that will make most screen readers and older browsers go bonkers and/or cry bitter tears of sorrow and despair. We're going to do that on purpose, it should be bad. The... thing we're about to create is meant to serve as a bad example on what not to do. There. You've been warned. I will however include a few boxes where I'm hacking my way in and explain why you shouldn't be doing this.
I want to give a shout out to @nuritnt! While we were coaching a group of web dev students, she originally asked the question "Can you do an image detail zoom modal with CSS only? You could probably write a post about that?", so, yeah, here we are!
Now, let's get our hands dirty, shall we?
The thing we're trying to build
So, on some webshops, when clicking on a small product image, the image opens up in a zoomed version. This is especially useful when the product images are, say, sets of different parts and you want to have a look at the individual parts. Or read a small sentence on the packaging.
To make things a little more convenient, I also want to add the possibility to close the modal with the ESC key. Don't worry, we'll be able to hack that without JS.
So, click on small image opens large image in modal. ESC key and close button close it again. Sounds good.
Step 1: The image
First, we need an image. I'm going to use placeholder.com for that:
In case you don't know placeholder.com, it's an amazing website to create placeholder images with. You can determine its size via URL (for example, http://via.placeholder.com/640x480.png) and add stuff like text via GET parameters.
Wait, aha, you're going to use the trusty ol' checkbox trick, right?
Almost! Today, we're going to get even worse than that.
For those of you that don't know it, the checkbox trick was used back in the days to circumvent using JavaScript for simple style toggles. By using the sibling selector (.a ~ .b
, so any .b
that is a sibling of .a
) and the :checked
pseudo-class, we were able to toggle stuff. A really simple example could've been .some-toggle:checked ~ .menu { display: block; }
.
I'm going to use the good ol' <input type="text">
and its focus state for that instead.
I'll actually go beyond that and will use the text input as the image:
<div class="imagezoom-container" style="width: 100px; height: 100px;">
<input type="text" class="imagezoom" style="
background-image: url(http://via.placeholder.com/640x480.png?text=Image%201);
">
</div>
.imagezoom-container {
display: inline-block;
margin: 10px;
}
.imagezoom {
width: inherit;
height: inherit;
background-size: cover;
background-position: center;
border: 0 none;
/* Get rid of any text */
color: rgba(0, 0, 0, 0);
}
/* Get rid of any text selection */
.imagezoom::selection {
display: none;
}
Wow.
Please don't add images to your page like this! Screen readers will only see an input without a label, there's no possibility to add an alt
text and the image might be skewed/cropped.
I'm using the input field here to minimize DOM. Also, since we're using the focus state, we can guarantee that any click on the image (even when enlarged) will trigger a focus on the input and thus keep the modal open. Yuck!
Using the elements focus state
Now we'll style the input field image/modal for its zoomed state:
.imagezoom:focus {
/* Make the "image" fill the entire screen */
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: calc(100vw - 200px) !important;
height: calc(100vh - 100px) !important;
/* Style the background-image */
background-size: contain;
background-repeat: no-repeat;
margin: 50px 100px;
/* Remove the background-color by making it fully transparent */
background-color: rgba(0, 0, 0, 0);
/* Get rid of default focus styling */
z-index: 2;
border: 0 none;
outline: none;
}
/* The dark background */
.imagezoom-background {
display: none;
}
.imagezoom:focus ~ .imagezoom-background {
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100vw;
height: 100vh;
z-index: 1;
background-color: rgba(0, 0, 0, 0.8);
}
<div class="imagezoom-container" style="width: 100px; height: 100px;">
<input type="text" class="imagezoom" style="
background-image: url(http://via.placeholder.com/640x480.png?text=Image%201);
">
<div class="imagezoom-background"></div>
</div>
Please don't use input fields for this! Screen readers will not know that you're trying to build an image zoom. They will think they're inside a huge form with dozens of inputs instead of an image gallery. They also probably won't know that you're using it as a toggle switch of some sort.
To round things up, we'll add a close button. Conveniently enough, <button>
takes focus, therefore closing the modal.
The result
The result is average at best:
You probably need to open the pen in a separate window, especially on a mobile device. I also added a min-width to the image in order to make it at least show up on mobile.
While technically functional on mobile devices, it opens the keyboard, input fields tend to do that. A radio or checkbox instead of an input field might fix that, though. Also, the other image jumps around weirdly, when one is opened.
To get the full experience, I suggest you copy the HTML and CSS of the Pen and put in an HTML file on disk and open it there. Somehow Codepen messes with the focus, so ESC to close doesn't really work inside the Pen.
I repeat again: please, please don't do this in production. Use JavaScript instead. I need to go wash my hands...
I hope you enjoyed reading this article as much as I enjoyed writing it! If so, leave a ❤️ or a 🦄! I write tech articles in my free time and like to drink coffee every once in a while.
If you want to support my efforts, you can offer me a coffee ☕ or follow me on Twitter 🐦! You can also support me directly via Paypal!
Posted on November 6, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 6, 2021