Folding Image Effect - CSS Only!

A tutorial by Ian Field-Richards

Tags: HTML CSS3 Intermediate

Download (zip) View Demo


There's a current trend sweeping the net right now, especially on mobile device concepts I've seen that replicates this effect. I thought it would be a nice way to spend a Saturday afternoon by working out how you'd do it in purely CSS.

Disclaimer: This should validate in HTML4.01, but given the CSS3 functions evident here I'd say you're looking at HTML5 and only modern browsers. I've tested this in Chrome, Safari and Firefox and it works just fine. But I'm guessing that only IE9 and 10 will even attempt this... basically you're gonna have to do the hard work there if you want to take this further ;)

Here's an example of what we're going to look at. Hovering each image here will move the image up in a folded paper style action, to reveal a tag underneath. Well deal with a slightly simpler version for this demo (ie, without the wood effect background, just to make it more manageable).

Images here are being randomly generated by the awesome http://lorempixel.com/. Wood effect background courtesy of gnrbishop.deviantart.com

There's also a whole bunch of box-shadow effects going here to do the depth effects, shadows and multi-stroke borders. But don't panic yet! I'll explain all.


Step One: Setting Up the HTML

For the sake of this example, I'm going to be breaking this down on ONE IMAGE ONLY. Once we've got the code for that worked out we can duplicate it into multiple sets. Let's call this "crawling before we can walk".

The principle here is that we're wrapping a <a href> tag around a set of three <span> elements. These Spans will be what animates. Why not DIVs? Because HTML 4.01 will not allow BLOCK elements inside an INLINE element. However, HTML5 will allow this. So if you're working this into a HTML5 page, DIVs might make more sense when reading the code back. Your choice.

So, enough waffle already! Let's create the HTML:

  1. <a href="#">
  2. <span></span>/* top fold */
  3. <span></span>/* middle fold */
  4. <span"></span>/* bottom fold */
  5. </a>

Now we add some classes...

  1. <a href="#" class="blindsWrapper">
  2. <span class="partOne"></span>/* top fold */
  3. <span class="partTwo"></span>/* middle fold */
  4. <span class="partThree"></span>/* bottom fold */
  5. </a>

we'll create these CSS classes in the next step, but to clarify; these are the classes that will control the wrapper for the image and each of the individual folds.

Finally lets add the label that the Image is going to reveal...

  1. <a href="#" class="blindsWrapper">
  2. <span class="partOne"></span>/* top fold */
  3. <span class="partTwo"></span>/* middle fold */
  4. <span class="partThree"></span>/* bottom fold */
  5. <i>Image Label</i>/* Image Label */
  6. </a>

Now lets look at the CSS


Step Two: The Wrapper CSS

Now we're going to build the CSS classes that control this effect. The first block is what controls the wrapper and background images for the <a>.

  1. .blindsWrapper {
  2. display: inline-block;
  3. position:relative;
  4. vertical-align: top;
  5. padding: 0px;
  6. /* set your image spacing grid here */
  7. margin: 0px 10px 20px 10px;
  8. /* set your image size here */
  9. width: 250px; height: 210px;
  10. /* set the reveal background here */
  11. background: rgba(0,0,0,0.1);
  12. /* Border elements */
  13. border-radius:10px;
  14. box-shadow: 0 1px 1px rgba(255,255,255,1), inset 0 -1px 4px rgba(0,0,0,0.2);
  15. /* set the base z-index */
  16. z-index: 1;
  17. /* This sets the mouseover animation */
  18. -webkit-transition: all .15s ease-in-out;
  19. -moz-transition: all .15s ease-in-out;
  20. }

At this point we're creating an inline element so it flows left to right inline like the example block above. You could achieve the same effect with BLOCKs and FLOATs.

We're also establishing how the child elements should animate in the page.


Step Three: The Image Fold CSS

Now we're going to create the three classes that control the image slices for the folds.

Each of the folds needs to have slightly different effect applied to them to help enhance the 3D effect. The top fold has a bright gradient highlight, the middle is darkened as if the shadow from the top fold is being cast over it and the bottom fold has several box-shadow effects applied to it to help with the depth.

For the hover effect we're going to add perspective(), rotate() and scale(). At this point however we'll be purely putting the hooks in for that effect.

Firstly, create a set of classes which all have the following values. These will be picked up by each <span> and it's much more efficient to group these together.

  1. .partOne,
    .partTwo,
    .partThree {
  2. /* set the defaults */
  3. position: relative;
  4. display: block;
  5. /* add your image */
  6. background-image: url(*your image here*);
  7. background-repeat: no-repeat;
  8. /* set the borders */
  9. border: 5px solid white;
  10. z-index: 2;
  11. /* these set any effect to null. Important to get smooth transitions */
  12. -webkit-transform:perspective(0) rotateX(0) scale(1);
  13. -moz-transform:perspective(0) rotateX(0) scale(1);
  14. transform:perspective(0) rotateX(0) scale(1);
  15. /* this sets the mouseOut animation */
  16. -webkit-transition: all .15s ease-in-out;
  17. -moz-transition: all .15s ease-in-out;
  18. }

Having done this, we're going to set the individual values for each fold <span>.

Firstly, we need to work out what a third of our intended image height is. In this case I've chosen a nice 250px value. Unfortunately this doesnt split into three well. Oh well, Maths never was my strong point, right? So what we're left with is a top and bottom fold of 67px and a middle fold of 66px.

  1. .partOne {
  2. /* set the image height to be a third of your total size */
  3. height: 67px;
  4. /* set the borders */
  5. border-radius: 10px 10px 0px 0px;
  6. /* turn OFF the bottom border */
  7. border-bottom: 0px solid white !important;
  8. /* move the image to the top left of the span */
  9. background-position: 0 0;
  10. /* These box-shadow effects will give the hover color change and a gradient highlight animation something to key into */
  11. box-shadow: 0 1px 5px rgba(0,0,0,0), inset 0 -5px 15px rgba(255,255,255,0), 0px -1px 0 silver, -1px 0 0 silver, 1px 0 0 silver;
  12. }

  1. .partTwo {
  2. /* set the image height to be a third of your total size */
  3. height: 66px;
  4. /* set the borders */
  5. border-radius: 10px 10px 0px 0px;
  6. /* turn OFF the bottom and top border */
  7. border-top: 0px solid white !important;
  8. border-bottom: 0px solid white !important;
  9. /* move the image up the same value as the span above */
  10. background-position: center -67px;
  11. /* setting the middle span to be higher than the other images */
  12. z-index: 3 !important;
  13. /* These box-shadow effects will give the hover color change and a gradient highlight animation something to key into */
  14. box-shadow: 0 1px 5px rgba(0,0,0,0), inset 0 0px 0px 66px rgba(0,0,0,0), -1px 0 0 silver, 1px 0 0 silver;
  15. }

  1. .partThree {
  2. /* set the image height to be a third of your total size */
  3. height: 67px;
  4. /* set the borders */
  5. border-radius: 0px 0px 10px 10px;
  6. /* turn OFF the top border */
  7. border-top: 0px solid white !important;
  8. /* move the image up the same value as both spans above */
  9. background-position: center -134px;
  10. /* These box-shadow effects will give the hover color change and a gradient highlight animation something to key into */
  11. box-shadow: 0 10px 5px rgba(0,0,100,0), 0 16px 10px rgba(0,0,0,0), 0 4px 0 0 rgba(200,200,200,1), 0 0 0 1px silver;
  12. }

Now we have the default states set, you should see the block as a single correct image *yay*. So now we need to add the hover effects. This is going to be triggered by hovering the <a> wrapper. Basically what's happening here is that when you're hovering the <a> the CSS is setting a value change on the child elements .partOne etc. Doing it in this way ensures that the transition animations have something to go from and to. You could achieve the efect by swapping classes with jQuery, but you'd lack the CSS animations, and hey, it would involve adding unnecessary jQuery overheads.

Having set up blank perspective(), rotate() and scale() values in the parent class, now we're going to swap those for some real values and apply the affect.

The top and Bottom folds will transform the same way, the middle acting in a mirror image. Because the perspective() effect changes the relative size of the top and bottom of each slice, we'll need to apply the scale() attribute to fix this. Mainly i found this was a trial and error value, ie I just played around until it looked right. But what you're looking for is that the top of fold one is the same width hovered, as in the unhovered state.

We also need to shift the top value to account for this shifting. Again, I played around with the values on each fold until they matched up correctly.

  1. .blindsWrapper:hover .partOne {
  2. /* set the borders */
  3. box-shadow: 0 1px 5px rgba(0,0,0,0.1), inset 0 -5px 15px rgba(255,255,255,0.5), 0px -1px 0 #74ceeb, -1px 0 0 #74ceeb, 1px 0 0 #74ceeb;
  4. /* Animate To effect */
  5. -webkit-transform:perspective(300px) rotateX(40deg) scale(1.07);
  6. -moz-transform:perspective(300px) rotateX(40deg) scale(1.07);
  7. transform:perspective(300px) rotateX(40deg) scale(1.07);
  8. /* move the top image up */
  9. top: -8px;
  10. }

  1. .blindsWrapper:hover .partTwo {
  2. /* set the borders */
  3. box-shadow: 0 1px 5px rgba(0,0,0,0.35), inset 0 0px 0px 66px rgba(0,0,0,0.3), -1px 0 0 #74ceeb, 1px 0 0 #74ceeb;
  4. /* Animate To effect */
  5. -webkit-transform:perspective(300px) rotateX(-40deg) scale(1.07);
  6. -moz-transform:perspective(300px) rotateX(-40deg) scale(1.07);
  7. transform:perspective(300px) rotateX(-40deg) scale(1.07);
  8. /* move the top image up */
  9. top: -15px;
  10. }

  1. .blindsWrapper:hover .partThree {
  2. /* set the borders */
  3. box-shadow: 0 10px 5px rgba(0,0,150,.1), 0 16px 10px rgba(0,0,0,.15), 0 4px 0 0 #74ceeb, 0 0 0 1px #74ceeb;
  4. /* Animate To effect */
  5. -webkit-transform:perspective(300px) rotateX(40deg) scale(1.07);
  6. -moz-transform:perspective(300px) rotateX(40deg) scale(1.07);
  7. transform:perspective(300px) rotateX(40deg) scale(1.07);
  8. /* move the top image up */
  9. top: -32px;
  10. }

Finally we need to add the class that's going to style our image label:

  1. .blindsWrapper i {
  2. display: block;
  3. position: absolute;
  4. bottom: 8px;
  5. right: 12px;
  6. font-size: 18px;
  7. font-weight: bold;
  8. text-decoration: none;
  9. color: rgba(0,0,0,0.3);
  10. text-shadow: 0 1px 0 rgba(255,255,255,0.65);
  11. }

And that's it! hopefully your image will now animate with a groovy fold effect and will look like the example here:


Taking It Further

The code so far is just dealing with a single image example. But in reality that's not really practical. Most people will want to have a set of images that this applies to. We need to make some simple changes to make this happen

Firstly we need to REMOVE the background-image element from the .partOne, .partTwo, .partThree classes.

Then we need to add image specific classes to the HTML. This allows us to build each individual element separately. In the example at the top of the page, I have six images, so lets add six unique classes to reflect that. I'm calling them .imgOne thru .imgSix. Place them after .blindsWrapper.

  1. <a href="#" class="blindsWrapper imgOne">
  2. <span class="partOne"></span>/* top fold */
  3. <span class="partTwo"></span>/* middle fold */
  4. <span class="partThree"></span>/* bottom fold */
  5. <i>Image Label</i>/* Image Label */
  6. </a>

Now to add the extra CSS. This is done in blocks which require the new controlling class of .imgOne thru .imgSix to influence all three of the fold <span>.

  1. .imgOne .partOne,
    .imgOne .partTwo,
    .imgOne .partThree {
  2. background-image: url(*your image here*);
  3. }
  4. .imgTwo .partOne,
    .imgTwo .partTwo,
    .imgTwo .partThree {
  5. background-image: url(*your image here*);
  6. }
  7. .imgThree .partOne,
    .imgThree .partTwo,
    .imgThree .partThree {
  8. background-image: url(*your image here*);
  9. }
  10. .imgFour .partOne,
    .imgFour .partTwo,
    .imgFour .partThree {
  11. background-image: url(*your image here*);
  12. }
  13. .imgFive .partOne,
    .imgFive .partTwo,
    .imgFive .partThree {
  14. background-image: url(*your image here*);
  15. }
  16. .imgSix .partOne,
    .imgSix .partTwo,
    .imgSix .partThree {
  17. background-image: url(*your image here*);
  18. }

The great thing about this method is that you don't have to create crazy jQuery to handle each individual hover state, the CSS does that for you. It's a fantastically lightweight way of approaching it.

That's great but…

OK, so the obvious flaw in this approach is that only absolutely modern browsers will run it. CSS3 webkit is fine for fun things like this, but how about using this in anger on a site viewed by all browsers? Well, technically this should degrade gracefully enough so that even though it's not animating and added the 3D effect, it should shift the images up so you can see the image label. (but it should be noted that the box-shadow effects I'm using to create multi-stroke borders will not show either).

Hopefully it won't be long before the web in general catches up with CSS3 properly, the latest versions of Internet Explorer are showing promising signs!

Hope you dig this, feel free to ask me any questions or send feedback using the form below, or if you use it, please link me your results!

Ian. June, 2012


Download the example files here: Download Tutorial (zip)

Ian lives and works in the UK. He is currently deviantARTs User Interface Director.

Please enter your name

Ya gotta say something!
Send me a hug at the
very least!