Create a 3D image gallery with CSS3

CSS3 enables us to visualise all sorts of new user interactions, including the rotation of the entire viewport


Ah, the image gallery; a common part of modern web design, the gallery has been through numerous guises in the life of the web. Originally whole pieces of software were available to automate the generation of web galleries, and even the likes of Photoshop included a function to automatically create a thumbnail, large version and the HTML to link the two together. On the modern web, however, a click-and-wait-for-it-to-load approach no longer meets user expectations and, with the advent of CSS3, increasingly users expect to see a bit of visual flair beyond the standard lightbox effect that has become so popular. In times gone by, the only way to render a 3D effect inside a browser was using the Flash plug-in, but those days are gone as WebKit-powered browsers such as Safari and Chrome now support native 3D animation and transformation using CSS3 alone.

In this tutorial we’re going to do exactly that – we’ll take a two-dimensional image gallery and provide a twist when the user clicks on a thumbnail – quite literally. As the click occurs, the whole window contents will rotate out of view to reveal the larger version of the image. All the animation and 3D manipulation will be handled with CSS, although we will use a little jQuery
to help us set everything up correctly.


 Source some images

We’re creating an image gallery in this tutorial, so the first thing we’ll need is some images to work with! These can be of anything you like, but if you want to follow along with the images that we used, check the CD for a link to the references we downloaded from the brilliant free photo resource

Crop and treat images

We’ll need two versions of each image – a small one at 600px wide and 338px tall, and a big one at 1,920 x 1,200px. Both sizes are 16:9 so crop them to this dimension, and at the same time we apply a slight colour treatment to make them feel part of the same set. Use the image editor of your choice and a Curves adjustment to reduce the blue tones. Save each into the images>small and images>big folders, respectively.

Add some texture

Locate a nice textured image on the web and crop a section. Use the Clone Stamp tool or the Spot Healing Brush to reduce contrast and noticeable features, then apply Filter>Other>Offset (if using Photoshop) or the equivalent to move the edges of the image to the centre. Repeat the cloning to get a perfect tiled image, then save as a JPEG in the styles folder.

Create your HTML

Our HTML is pretty simple, comprising a couple of unordered lists containing images and links to select images, and a few nested <div> tags. We’ll need all those <divs> to handle the 3D rotation later on. In the meantime, add the code below, or open up our start document from the cover disc.

Add a stylesheet

Unstyled the page looks pretty ugly, but once we’ve added some basic CSS this will all change. Let’s find somewhere to store our styles by creating a reference to an external stylesheet. Add the code shown below and create a new blank document, saving it as ‘screen.css’ in the styles folder.

Basic styles

Let’s get some simple styles in place to establish the main build. The background is going to be set to a dark colour, while the container will use the texture we created in step 3. We’ll also work in some style to the thumbnails list. Add this code to your stylesheet:

001 body {
002 margin: 0;
003 padding: 0;
004 background: #333;
005 }
006 #container {
007 position: relative;
008 width: 100%;
009 height: 600px;
010 padding-top:50px;
011 text-align: center;
012 background: transparent url(backgroundtexture.jpg) repeat top left;
013 }
014 #container ul.thumbnails {
015 position: relative;
016 margin: 0;
017 margin: auto;
018 padding: 0;
019 width: 600px;
020 height: 338px;
021 overflow: hidden;
022 -webkit-box-shadow: 0px 5px 10px #333;
023 box-shadow:0px 5px 10px #333;
024 border: 6px solid white;
025 }
026 #container ul.thumbnails li {
027 position: relative;
028 float: left;
029 margin: 0;
030 padding: 0;
031 list-style: none;
032 cursor: pointer;
033 }

Getting the captions

Each image in our thumbnail set has an accompanying caption which we want to position over the top of the image as a partially transparent bar. Using RGBA to specify the background colour of an element allows us to set the alpha (or transparency) to suit our needs. Add the code below to your stylesheet to see this in action, and experiment with values between 0 and 1 for the alpha to see what effect it has.

001 #container ul.thumbnails li span {
002 position: absolute;
003 background: rgba(0,0,0,0.5);
004 width: 520px;
005 height: 20px;
006 color: #fff;
007 padding: 40px;
008 bottom: 0;
009 left: 0;
010 }

Style the links

If you load the page now you’ll see the thumbnails are displaying correctly, with just the first shown. We need to set up the styles for the links to allow the user to switch between different thumbnails. We’ll use the CSS3 border-radius attribute to create circular buttons for each thumbnail. Add the code below to your stylesheet to implement this:

001 #container ul.thumblinks {
002 position: relative;
003 margin: 0;
004 padding: 0;
005 margin-top: 20px;
006 }
007 #container ul.thumblinks li {
008 list-style: none;
009 padding: 0;
010 margin: 0;
011 display: inline-block;
012 width: 20px;
013 height: 20px;
014 overflow: hidden;
015 margin-right:5px;
016 -webkit-border-radius:10px;
017 border-radius:10px;
018 border-right:1px solid #fff;
019 border-bottom:1px solid #fff;
020 border-top: 1px solid #aaa;
021 border-left:1px solid #aaa;
022 background: rgba(0,0,0,0.01);
023 text-align: center;
024 }
025 #container ul.thumblinks li a {
026 display:block;
027 overflow:hidden;
028 width:16px;
029 height:16px;
030 text-indent:-10000px;
031 text-decoration: none;
032 -webkit-border-radius:8px;
033 border-radius:8px;
034 margin-top: 2px;
035 margin-left: 2px;
036 }
037 #container ul.thumblinks li a:hover, #container ul.thumblinks li a.selected {
038 background: rgba(0,0,0,0.8);
039 }

Add a new font

We can add a custom font using only CSS by taking advantage of Google Web Fonts, which require no JavaScript. Let’s do that to improve the appearance of our page. Head to and choose a typeface to suit (we opted for Coda). Add thecode provided to your HTMLand the font-family example to your body CSS rules.

002 body {
003 margin: 0;
004 padding: 0;
005 background: #333;
006 font-family: 'Coda', sans-serif;
007 text-transform: uppercase;
008 }

Script to switch

Currently when we click on a circular button, the thumbnail changes, however the big version of the image does not. To remedy this, we have to apply some jQuery code along with a little JavaScript to avoid the annoying skip that happens whenever we click on a link. Finally, we can update the highlighted button at the same time – just add the code below inside the script tags at the top of your page.

Making sense of it

The code we just added grabs the href from the link and uses it to find the right list tags in the unordered thumbnails list. Once it has the right one, it grabs the big image reference and sets the big image src attribute to match. It then moves the correct list tag to the front of the stack in the thumbnails list and updates the ‘selected’ class on the links list to the selected link. Phew!

Transform into 3D

Everything is set for us to make the move to 3D inside our page, so let’s get started with the CSS code we need to achieve this. We’re creating two faces of a box – the front face is the ‘container’ div and the bottom face is ‘bottom’. The box itself is called rotate, while the 3D stage is called stage, appropriately enough. Add the code below to get going:

001 #stage {
002 -webkit-perspective: 2500;
003 position: relative;
004 }
005 #rotate {
006 -webkit-transform-origin: 0 0;
007-webkit-transform-style: preserve-3d;
008 -webkit-transition: 1s linear all;
009 -webkit-transform: rotateX(0deg) translateZ(0px);
010 }
011 #bottom {
012 position: relative; 013 width: 100%; 014 height: 600px;
015 margin: auto;
016 overflow: hidden;
017 text-align: center;
018 }

What’s going on?

It’s worth pausing to break down the code we just added. Firstly, the stage enables us to specify the perspective for our 3D scene. The rotate box code sets the point of rotation with the transform-origin attribute and also some basic properties for the animation we’ll see in action in the next few steps. We’ll add a rotation to #bottom shortly to make it the base of the box.

Rotate your #bottom

We want the image in the #bottomto fill our browser window, so we can’t specify the rotation directly in our stylesheet as we don’t know how big the user’s browser window is. Instead we’ll use some JavaScript code to work this out and set the CSS programmatically. Add the code below to the existing jQuery $(document).ready() function.

001 winheight=parseInt ($(window).height());
002 winwidth=parseInt($ (window).width());
003 if((winwidth/16*9) 004 $("#bottom img"). css({height:winheight });
005 } else {
006 $("#bottom img"). css({width:winwidth});
007 }

Rotate your #bottom 2

The code we just added sets the image so that it will always fill the browser window, but we still need to set the rotation of the. We can do this now that we know the width and height of the window. We’ll look at what this code does in a minute, but for the time being just add these two lines immediately beneath the code you added in step 14:

001 $("#container").css({height:winheight});
002 $("#bottom").css({webkitTransform: "rotateX(-90deg)
translateZ(- "+(winheight/2)+"px) translateY("+(winheight/2) +"px)",height:winheight})

How it works

As we’re constructing a box, as well as rotating the bottom face, we also need to offset it by the height of the front of the box. Additionally, we need to set the height of the face and move it forwards in Z space towards the camera by half the depth of the box. If this doesn’t make sense, imagine how you would position faces of a cube when making a paper model.

Check in your browser

We’ve got two sides of a cube set up correctly now, but before we introduce the final element, let’s make sure it renders in the browser as we’d expect. When you load the page now, you shouldn’t be able to see any sign of the #bottom. If all is as it should be, you’ll simply be able to see the #containeralong with its textured background.

Trigger the animation

When we click on the current thumbnail image, the animation should be triggered so that the cube rotates up, hiding the front of the box and revealing the cube’s base. The result will be a full-window view of the image that we’ve clicked on. Add the code below to set a new rotation value for the box.

001 $(".thumbnails").click(function(){
002 $("#rotate").css({webkitTransform:"s cale3d(1, 1, 1) rotateX(90deg) translateY(- "+(winheight)+"px)"});
003 });

Put it to the test

Test again in your WebKit-based browser, and this time, when you click on the thumbnail, you should see the full-window version animate into view. Why does it animate rather than flick straight into view? Because we added the –webkit-transition value in step 12! If you want to alter the speed of the animation, try adjusting the value 1s to 0.5s or 1.5s to speed it up or slow it down, respectively.

Allow the user to return

You’ll have noticed that once you’ve clicked on a thumbnail, you’re stuck in the full-window view. We can solve that by adding one last bit of code to remove the box rotation when the #bottom image is clicked. Add this jQuery code to put the fix in place:

001 $("#bottom img").click(function(){
002 $("#rotate").css({webkitTransform:"scale 3d(1, 1, 1) rotateX(0deg) translateZ(0px)"});
003 });

Fully test

We’ve now got a fully functioning image gallery that enables us to move between different thumbnail images and click on each to reveal a full-page version using a very slick 3D cube animation. The whole thing is rendered using only CSS3, with just a smattering of JavaScript to set up the events to trigger the animation. Test it fully in your WebKit browser to make sure that it all works as it should.

Improve usability

While it’s obvious to us as the designers of this user interaction what we need to do to trigger the animation, for the average visitor it won’t be quite so apparent. Consider adding some icons or text-based instructions to give the user some guidance on how to view the full-window version. It’s also worth considering adding a fallback for non-WebKit-based browsers.


How to create a drag-and-drop Polaroid-style image gallery