Using Pseudo-Elements to Efficiently Stack Images and Create a 3D Effect

By: Jay Sitter
Rows of picture frames on wall

On a recent web development project, we were tasked with adding some 3D effects to an image by layering several images in front of it using mix-blend-mode. To do this, these “effects” images would need to be in front the main image (i.e., on a higher stacking order). Wanting to create as few extraneous or non-semantic elements in the DOM as possible, we looked toward using background-image on the ::after and ::before pseudo-elements of the existing elements.

Here’s an approximation of the markup we already had:

<div class="artwork">
  <picture class="artwork__image">
    <img src="">
  </picture>
</div>

Including pseudo-elements, this gives us a total of six selectors on which to put a background-image:

  • .artwork
  • .artwork::before
  • .artwork::after
  • .artwork__image
  • .artwork__image::before
  • .artwork__image::after

We need three of these layers to be in front of .artwork__image, so let’s try putting some backgrounds on these and see which are available:

We can see that there are three layers in front of .artwork__image:

  • .artwork::after
  • .artwork__image::before
  • .artwork__image::after

Because we needed four layers for our use case, we had to hold our nose and add an extra, meaningless element:

<div class="artwork">
  <div class="artwork__lighting">
    <picture class="artwork__image">
      <img src="">
    </picture>
  </div>
</div>

Now we’ve got nine total layers:

  • .artwork
  • .artwork::before
  • .artwork::after
  • .artwork__lighting
  • .artwork__lighting::before
  • .artwork__lighting::after
  • .artwork__image
  • .artwork__image::before
  • .artwork__image::after

Let’s see what order they’re in:

Sorted from back to front, that looks like this:

  • .artwork
  • .artwork::before
  • .artwork__lighting
  • .artwork__lighting::before
  • .artwork__image
  • .artwork__image::before
  • .artwork__image::after
  • .artwork__lighting::after
  • .artwork::after

So let’s reorganize the toggles to better reflect the hierarchy of these pseudo-elements:

That gives us the four pseudo-elements we need. We can put our background images on these selectors:

  • .artwork__image::before
  • .artwork__image::after
  • .artwork__lighting::after
  • .artwork::after

for this markup:

<div class="artwork">
  <div class="artwork__lighting">
    <picture class="artwork__image">
      <img src="">
    </picture>
  </div>
</div>

Cool.

Stacking Order

Here’s some documentation on the specification for stacking order, which goes into more detail (but for me is more difficult to comprehend than just building examples myself):

DockYard is a digital product agency offering custom software, mobile, and web app development consulting. We provide exceptional professional services in strategy, user experience, design, and full stack engineering using Ember.js and Elixir. With a nationwide staff, we’ve got consultants in key markets across the United States, including Seattle, Los Angeles, San Francisco, Denver, Chicago, Austin, New York, and Boston.