DEV Community

Cover image for ⚠️ Don't try this at home: CSS-only image detail zoom - As hacky as possible! πŸ–ΌοΈπŸ”
Pascal Thormeier
Pascal Thormeier

Posted on • Edited on

⚠️ Don't try this at home: CSS-only image detail zoom - As hacky as possible! πŸ–ΌοΈπŸ”

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:

A grey placeholder image saying

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>
Enter fullscreen mode Exit fullscreen mode
.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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode
<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>
Enter fullscreen mode Exit fullscreen mode

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!

Buy me a coffee button

Top comments (15)

Collapse
 
grahamthedev profile image
GrahamTheDev

Oh it hurts but I love it at the same time. ❀

I have been staring at the HTML for a couple of minutes trying to work out how I would make it accessible if I came across this in the real world.

I think I have only one answer though....kill it with fire! 🀣

Collapse
 
thormeier profile image
Pascal Thormeier

Yup, kill it with fire indeed! To be honest, I had a great time digging in dirt for once lol

I think trying to make stuff as dirty and hacky as possible every once in a while helps with staying creative :)

Collapse
 
thormeier profile image
Pascal Thormeier

Oh, also: When I got that working with the input field, my first thought was "Why does this work, I don't want this to work" 🀣

Collapse
 
grahamthedev profile image
GrahamTheDev

Well I do have a whole series of things "breaking the internet" here on dev, so who am I to say anything as none of them are accessible πŸ˜‹πŸ€£

Thread Thread
 
thormeier profile image
Pascal Thormeier • Edited

To be honest, hacking that modal like this felt way too easy! The DOM is minimal, the CSS isn't exactly rocket science either. In my opinion, it's such a pity that it's so easy to make things so horribly inaccessible. Doing them right should be the easy thing.

Collapse
 
mangoman profile image
Nauman Khan

Why type "border: 0 none;"? We should get the same result if we don't type this.

Collapse
 
thormeier profile image
Pascal Thormeier

border: 0 none removes the default border some browsers give inputs. It's just to make sure that the image has no unwanted border around it, so mostly a cosmetic thing. Does that answer your question?

Collapse
 
mangoman profile image
Nauman Khan

Yes, maybe I haven't witnessed a browser with default border yet. So...

Collapse
 
nuritnt profile image
Tuğçe Nur Taş

πŸ”₯ I like your dumpsterfire 🀣πŸ”₯ thanks for the shoutout!
looking forward to your next article!

Collapse
 
thormeier profile image
Pascal Thormeier

Thank you so much for the idea to create that dumpster fire in the first place! Already working on my next one, will be a bit more artsy than this.

Collapse
 
joelbonetr profile image
JoelBonetR πŸ₯‡

You can use this approach to build the same without the need of inputs:

dev.to/joelbonetr/build-your-own-c...

Collapse
 
thormeier profile image
Pascal Thormeier

That's a very handy article, thank you! My goal here was not to deliver a clean approach at all, though, I wanted this to be bad, honestly :D

Collapse
 
thormeier profile image
Pascal Thormeier

Unfortunately, that article doesn't exist anymore, I'm getting a 404... Would it have been related to the this topic?

Collapse
 
abhishekraj272 profile image
Abhishek Raj

This was quite interesting!! 😎

Collapse
 
thormeier profile image
Pascal Thormeier

Thank you so much! Digging in dirt every now and then is so much fun! :D