Perfectly Rotate and Mask Thumbnails With CSS3
Ever seen a website showcasing image thumbnails that are slightly rotated? It's a simple effect that adds a layer of visual personality. Saying that, if you're not achieving the rotation effect with CSS, you're working too hard! Learn how to do it right!
Every few weeks, we revisit some of our reader's favorite posts from throughout the history of the site. This tutorial was first published in August of 2011.
Introduction
Image galleries with rotated thumbnails are somewhat infrequent, but it's a simple trick to add style to your webpage. However, if not done right, achieving and maintaining this effect can sometimes prove to be a major hassle!
To properly achieve this effect, your first thoughts might turn to creating rotated thumbnails in Photoshop. However, this can prove to be difficult in the long term. Disadvantages to creating rotated thumbnails in this way include:
- Angle Consistency: If the images are not all rotated at the same angle, the visual consistency of page is lost. This means maintaing a PSD of each individual thumbnail properly rotated at a consistent angle. You may, for example, open your PSD and realize you have forgotten the rotation angle of your thumbnails.
- CMS Thumbnail Generation:If you are running a CMS (such as Wordpress), your thumbnails are probably being generated automatically. Using the PSD method described above, you'll have to manually create and upload each individual thumbnail in your CMS. Also, if you already have an existing site but want to apply the rotation effect to your thumbnails, you'll have to recreate all thumbnails in Photoshop then re-upload all of them!
- Long-Term Manageability: Say you've created all your thumbnails slightly rotated, but now you want to realign or redesign your gallery. This means you'll have to regenerate all of your thumbnails. If you've been managing your thumbnails in Photoshop, this means you have to adjust and re-save each individual thumbnail all over again. Ugh.
Wouldn't it be nice if you could perform this little rotation with one line of code? Well you can! Let's learn how.



Step 1: Understanding Our Goal
A brief overview of what we are trying to accomplish reveals the following steps:
- Create an image thumbnail in Photoshop (not rotated)
- Insert the thumbnail using the
img
tag - Mask it appropriately using CSS (think Photoshop masks)
- Rotate the thumbnail within the mask using CSS3



To make sure our gallery degrades gracefully, steps 1-3 will be accomplished using CSS. Step 4 (the rotation effect) will be accomplished using CSS3.
Step 2: Understanding Thumbnail Size and Rotation Angle Increases
Before creating our thumbnails, we need to determine how large each thumbnail will appear on screen.
If our thumbnails are too small and we rotate them at too much of an angle, some of the corners will give us an empty space. Like this:



Hence, to properly fill the mask area, we can conclude that the more our thumbnail is rotated the larger it will have to be. In mathematical terms, as the rotation angle of the thumbnail increases, so too must the thumbnail's size (and vice versa).



Remember this key: the image thumbnail will always be larger than the image mask (thumbnail size > mask size)
A Warning to Those Repelled by Math
Steps 3-6 describe how to mathematically calculate proportionate sizing for the image mask and thumbnail. Understanding it is not necessary to being able to rotate images with the CSS transform
property. Rather, its purpose is to help you understand how to properly determine the sizing of your elements to allow full 360° rotation. This ensures you won't be left with ugly empty corners.
Step 3: Proportionately Equal
To avoid showing empty space in our mask, we must determine ONE of the following:
- Size of the actual thumbnail
- Size of the image mask (viewable portion of the thumbnail)
Because the thumbnail and image mask are proportionately equal in size, if we set the size of one we can calculate the size of the other.
Step 4: Determining Dimensions
In this example, we are going to set our image mask size first. By setting the size of our image mask we can use a little math to calculate the thumbnail's size:
- Determine the size of the image mask: 180x240 pixels
- Find the length of the diagonal side
- Use the Pythagorean theorem ( a2 + b2 = c2 )
- Our first side (a) is 180px
- Our second side (b) is 240px
- So, 1802 + 2402 = c2 (diagonal length)
- 90,000 = c2
- 300 = c (take the square root of each side)
- Our diagonal length (c) equals 300
- Use the Pythagorean theorem ( a2 + b2 = c2 )



The diagonal length is an important number. To allow the our thumbnail full 360° rotation, its shortest side must equal the image mask's longest side. (Even though you may not need it, provisioning for 360° rotation will allow future changes to your rotation angle without having to resize your thumbnails).



Step 5: Calculating Thumbnail Dimensions
As you can see, our thumbnail's shortest side must be equal to our mask's longest side. If it's not, we'll be left with empty space. Remember: the thumbnail's size is proportionately greater than the image mask's size.



Calculating the thumbnail's size so it properly fits is easy once we know the dimensions of the image mask. We simply take the mask's largest side (the diagonal) and let it equal the shortest side of our thumbnail.
- Mask's largest side (300) equals thumbnail's shortest side (x)
- Use the proportionate relationships to find the thumbnail's length
- 180 : 300 (mask's height : thumbnail's height)
- 240 : y (mask's length : thumbnail's length)
- Cross multiply to solve
- 180y = 72,000
- y = 400 (thumbnail's length)



We have now determined our mask and thumbnail sizes. We know if our image mask is 180x240px, than the image thumbnail inside that mask must be 300x400px to allow for 360° rotation.
A Happy Note: Because the image mask and thumbnail sizes are proportionately equal, this math would also work if we set our thumbnail size first! We would use the same Pythagorean theorem and proportional relationships to determine the proper sizes.
Step 6: Finally Some HTML
Now that we know all our sizes, let's build our rotated thumbnails by starting with some basic HTML.
1 |
|
2 |
<!DOCTYPE html>
|
3 |
<html>
|
4 |
<head>
|
5 |
<title>Rotated Thumbnails with CSS3</title> |
6 |
<link rel="stylesheet" href="styles.css" /> |
7 |
</head>
|
8 |
<body>
|
9 |
|
10 |
<div id="wrapper"> |
11 |
|
12 |
<h1>Rotated Thumbnails with CSS3</h1> |
13 |
<label>Transform Angle: </label> |
14 |
<input id="rotation_amount" value="15" /> |
15 |
<button>Update</button> |
16 |
<span id="error_message"> Number values only</span> |
17 |
<br /> |
18 |
|
19 |
<a href="1.jpg" class="mask"><img src="1.jpg" /></a> |
20 |
<a href="2.jpg" class="mask"><img src="2.jpg" /></a> |
21 |
<a href="3.jpg" class="mask"><img src="3.jpg" /></a> |
22 |
|
23 |
<a href="4.jpg" class="mask"><img src="4.jpg" /></a> |
24 |
<a href="5.jpg" class="mask"><img src="5.jpg" /></a> |
25 |
<a href="6.jpg" class="mask"><img src="6.jpg" /></a> |
26 |
|
27 |
<a href="7.jpg" class="mask"><img src="7.jpg" /></a> |
28 |
<a href="8.jpg" class="mask"><img src="8.jpg" /></a> |
29 |
<a href="9.jpg" class="mask"><img src="9.jpg" /></a> |
30 |
|
31 |
|
32 |
<div style="clear:both;"></div> |
33 |
|
34 |
</div> <!-- #/wrapper --> |
35 |
</body>
|
36 |
</html>
|
This basic HTML markup inserts the following:
-
<input>
- This will allow the user to change the rotation angle. Also, we set ourvalue
attribute to equal the same amount we will initially set in our CSS (15 in this case). -
<span>
- This error message will be hidden from view. Using jQuery, we will show it if the user enters something besides a number in the input box. -
<img>
- These are our thumbnails which can be linked to whatever you please. -
clear:both
- Clears our floated thumbnails -
class="mask"
- The class for our image mask



Step 7: CSS Basic Page Styling
Let's apply some basic styling to our page to give it structure and simplicity.
1 |
|
2 |
body { |
3 |
margin:0; |
4 |
padding:0; |
5 |
background:#eee; |
6 |
font-family:Geneva, Lucida Sans, Lucida Grande, Lucida Sans Unicode, Verdana, sans-serif; |
7 |
}
|
8 |
#wrapper { |
9 |
width:840px; |
10 |
margin:0 auto; |
11 |
background:#fff; |
12 |
border:1px solid #ccc; |
13 |
padding:25px; |
14 |
border-top:none; |
15 |
}
|
16 |
#error_message { |
17 |
color:red; |
18 |
display:none; |
19 |
font-size:.8em; |
20 |
}
|
Note here that we hid our error_message
by default as we will toggle it's visibility later on in jQuery.
Add Some CSS3 Effects
Let's add a few more details to enhance our basic styling
1 |
|
2 |
#wrapper { |
3 |
border-radius:0 0 5px 5px; |
4 |
-moz-border-radius:0 0 5px 5px; |
5 |
-webkit-border-radius: 0 0 5px 5px; |
6 |
box-shadow:0 0 5px #ccc; |
7 |
-moz-box-shadow:0 0 5px #ccc; |
8 |
-webkit-box-shadow:0 0 5px #ccc; |
9 |
}
|



Now we have our content centered with good spacing and some CSS3 effects for enhanced page styling.
Step 8: CSS Styling Image Mask
Let's apply the image mask to our thumbnails. In our HTML, we wrapped each thumbnail in an anchor tag and gave it a class of mask
which we will use in our CSS.
1 |
|
2 |
.mask { |
3 |
position:relative; |
4 |
height:180px; |
5 |
width:240px; |
6 |
float:left; |
7 |
overflow:hidden; |
8 |
margin:15px; |
9 |
border:5px solid #f6f6f6; |
10 |
box-shadow:0 0 1px #000; |
11 |
-moz-box-shadow:0 0 1px #000; |
12 |
-webkit-box-shadow:0 0 1px #000; |
13 |
}
|
Here's a description of the CSS properties we used (and why we used them):
-
position:relative
- Ourimg
tags will be positioned absolutely inside our image mask -
height
,width
- Earlier we determined our image mask's dimensions. This is where we place those sizes. -
float:left
- Floats our images so they appear in a gallery form. -
overflow:hidden
- As calculated earlier, our thumbnails will be 300x400px. However, we will only show a portion (180x240px) of them. This property acts as mask, hiding the portion of our thumbnails that extends outside the 180x240 dimensions. -
margin
- Space out our images -
border
,box-shadow
- This gives us a double border (in supporting browsers). Theborder
property gives us a thick, off-white border around the image while thebox-shadow
gives us a thin, black border around our thick, off-white border.



Step 9: CSS Sizing The Thumbnails
Set our thumbnail sizes according to the dimensions we calculated in Step 5.
1 |
|
2 |
.mask img { |
3 |
height:300px; |
4 |
width:400px; |
5 |
}
|
Step 10: CSS Centering The Thumbnails
Right now, our thumbnails are positioned relatively (from the top left corner).



We want our thumbnail to be centered horizontally and vertically within the mask.



To accomplish this, we use the following CSS rules:
1 |
|
2 |
.mask img { |
3 |
position:absolute; |
4 |
margin-top:-150px; /* half the height */ |
5 |
margin-left:-200px; /* half the width */ |
6 |
top:50%; |
7 |
left:50%; |
8 |
}
|
Here's a description of what we did:
-
position:absolute
- This positions the thumbnail absolutely within the image mask -
margin
- We set negative margins that are exactly half the height and width of the image (300x400) then set our positioning propertiestop
andleft
which pull the elements back into perfect center.



Step 11: CSS Thumbnail Rotation
We will be using the CSS3 property transform
to rotate our elements. So, our CSS will include all the browser prefixes
1 |
|
2 |
.mask img { |
3 |
-webkit-transform: rotate(15deg); |
4 |
-moz-transform:rotate(15deg); |
5 |
-o-transform:rotate(15deg); |
6 |
-ms-transform:rotate(15deg); |
7 |
transform:rotate(15deg); |
8 |
}
|
The CSS here is pretty simple. We just place our angle of rotation in parenthesis followed by "deg".
In this example we used 15 as the rotation value, but you could put a different value there. Because we calculated the sizes of our mask and thumbnail properly, we have provisioned room for full 360° rotation! A positive integer rotates the image to the right, a negative integer rotates the image to the left.



Step 12: CSS Image Hover Effect
As an additional effect, we're going to add a simple line of CSS that changes our border color when the user hovers over and image.
1 |
|
2 |
.mask:hover {border-color:#ddd;} |
Step 13: jQuery Changing Rotation Value Dynamically
As a little bonus, we are going to allow the user to enter a value in our input box that changes the CSS rotation angle. So first, let's add jQuery at the end of our page right before the closing body
tag along with a link to our custom script:
1 |
|
2 |
<script src="https://code.jquery.com/jquery-1.6.1.min.js"></script> |
3 |
<script src="js.js"></script> |
Step 14: jQuery Document Ready
Now let's setup jQuery for when the document is ready:
1 |
|
2 |
jQuery(document).ready(function($) { |
3 |
// code here
|
4 |
});
|
Step 15: jQuery Initial Function
We create a function for anytime our "update" button gets clicked.
1 |
|
2 |
$('button').click(function() { |
3 |
//code here
|
4 |
});
|
Step 16: jQuery Storing Current Rotation Value
We want to store the CSS numeric value for the current angle of rotation in a variable.
1 |
|
2 |
var rotation_angle_value = $('#rotation_amount').val(); |
This code finds the rotation_amount
id (which we assigned to the input
) and gets its current value. If you remember, we set the initial value
attribute to equal 15 (the same as in our CSS code for the rotation angle).
Step 17: jQuery Using isNaN() Function
We want to make the user's input value our new angle of rotation. However, we want to make sure the user doesn't accidentally enter a non-numeric value. That's where the isNaN()
javascript function comes in. isNaN()
stands for "is not a number". This function does exactly what its names implies: it checks to see if the value you pass to it "is not a number".
So, we will use the isNaN()
function and pass to it our rotation angle value (as entered by the user). If it's not a number, we will display our error message.
1 |
|
2 |
//check to see if the user's input value is a number or not
|
3 |
if (isNaN(rotation_angle_value)) { //not a number |
4 |
$('#error_message').show(); |
5 |
//rest of code here
|
6 |
}
|
Step 18: jQuery Updating New Rotation Angle
If the user's newly entered value is not a number, we've displayed an error message. Now we will use an else
statement for when they have entered a numeric value. First we hide the error message.
1 |
|
2 |
else { |
3 |
$('#error_message').hide(); |
4 |
}
|
Because the rotation angle is stored multiple times in our CSS (due to the various browser prefixes) we have to update ALL of those values with jQuery. So, what we will do is store the syntax of the CSS value in a variable (with the new angle value). Essentially, we are writing rotate(15deg)
and replacing the value of '15' with the value the user input.
1 |
|
2 |
var rotation_angle = 'rotate(' + rotation_angle_value + 'deg)'; |



Then we create a CSS object with relational values. We define each of our CSS properties (the browser prefixes for transform), then insert the value as the variable we just defined.
1 |
|
2 |
var updated_css = { |
3 |
'-webkit-transform' : rotation_angle, |
4 |
'-moz-transform' : rotation_angle, |
5 |
'-o-transform' : rotation_angle, |
6 |
'-ms-transform' : rotation_angle, |
7 |
'transform' : rotation_angle, |
8 |
}
|
Now we simply pass that variable storing our CSS properties and values to jQuery to update our CSS!
1 |
|
2 |
$('.mask img').css(updated_css); |
Step 19: jQuery Final jQuery Code
This is what all our jQuery looks like:
1 |
|
2 |
jQuery(document).ready(function($) { |
3 |
|
4 |
$('button').click(function() { |
5 |
//get the rotation angle value
|
6 |
var rotation_angle_value = $('#rotation_amount').val(); |
7 |
|
8 |
if (isNaN(rotation_angle_value)) { //is not a number |
9 |
$('#error_message').show(); |
10 |
}
|
11 |
else { //is a number |
12 |
$('#error_message').hide(); |
13 |
|
14 |
//store CSS value syntax with the new rotation angle value
|
15 |
var rotation_angle = 'rotate(' + rotation_angle_value + 'deg)'; |
16 |
|
17 |
//store CSS properties and values
|
18 |
var updated_css = { |
19 |
'-webkit-transform' : rotation_angle, |
20 |
'-moz-transform' : rotation_angle, |
21 |
'-o-transform' : rotation_angle, |
22 |
'-ms-transform' : rotation_angle, |
23 |
'transform' : rotation_angle, |
24 |
}
|
25 |
|
26 |
//update our CSS
|
27 |
$('.mask img').css(updated_css); |
28 |
}
|
29 |
});
|
30 |
});
|
Conclusion
I hope you've learned something, above all that committing yourself to non-flexible designs in Photoshop is often avoidable by using CSS3 techniques directly in the browser. Thanks for following along!