Tutorial Details
-
Topic: Site build
-
Difficulty: Intermediate
-
Estimated completion time: 1-2 hours
Final Product What You’ll Be Creating
The inherent visual appeal of filterable portfolios (like the Tuts+ hub) has made them very popular. Today, we’ll be making one using straight-forward markup, CSS3 and a little bit of jQuery.
Step 1: The File Structure
We’ll be using the following file structure for our project:
Pull a project together based on these files (you’ll need to grab HTML5 Shiv) and let’s get started with the HTML markup in index.html.
Step 2: HTML Head
Let’s keep tempo high and rattle off a list of things we need to do to create the <head>:
-
Declare the media type and character set of the page.
-
Set our viewport’s width to be the same as the device’s width and set the initial zoom to 1 (Read more about this here)
-
Give our page a title.
-
Attach a favicon (interested in retina-proof favicons?)
-
Attach our main style sheet (style.css)
-
Attach our style sheet for handling media queries (media-queries.css)
-
Link to the latest version of jQuery.
-
Add an HTML5 Shiv for handling HTML5 compatibility issues with old browsers.
And here’s what we get:
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 |
<!doctype html>
<html <head> <meta <meta <title>John Doe’s Portfolio</title> <link <link <link <script <script <!–[if lt IE 9]> <script src=”js/html5shiv.js”></script> <![endif]–> </head> |
Step 3: HTML Basic Markup
In the body, we first add a ‘container’ to hold all our elements within a set width, centered on the page. Within that we add (get ready for another rapid fire list):
-
A <header> for our heading (‘John Doe’).
-
A basic navigation (<nav>) comprising a <ul> menu, with five items, each with its respective ID.
-
A <section> for the thumbnails with the class ‘work’.
-
A <footer> with all the copyright stuff.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 |
<body>
<div <header> <h1 John Doe </h1> </header> <nav> <ul> <li <li <li <li <li </ul> </nav> <section </section> <footer>© 2012 John Doe. Valid HTML5.</footer> </body> |
Step 4: HTML Figure and Image
We’ll be using the <figure> tag for our thumbnails and will apply the class of the respective category which it belongs to. Within the figure, we’ll add an <a> tag comprising the image (<img>) for the background of the thumbnail and a description list (<dl>) for the caption.
001
002 003 004 005 006 007 |
<figure class=”illustration”> <a <img <dl> </dl> </a> </figure> |
Step 5: HTML Caption (DL, DT, DD)
As mentioned above, we’ll be using a description list for our caption. Our description terms (<dt>) will be our small headings — Client and Role, for our descriptions (<dd>) — Envato and Illustration, respectively.
001
002 003 004 005 006 007 008 009 010 011 |
<figure class=”illustration”> <a <img <dl> <dt>Client</dt> <dd>Envato</dd> <dt>Role</dt> <dd>Illustration</dd> </dl> </a> </figure> |
Step 6: The Complete HTML
Here’s what our completed HTML markup looks like:
Let’s move on to the styling now.
Step 7: CSS Selection and Scrollbar
We’ll start by dealing with some playful elements; the selection state and the scrollbar (which are entirely optional) plus we’ll haul in some fonts.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 |
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,300);
@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow); ::selection { background: #333; color: #FFF; } ::-webkit-scrollbar { width: 9px; } ::-webkit-scrollbar-track { background:#eee; border: thin box-shadow: 0px } ::-webkit-scrollbar-thumb { background:#999; border: thin } |
In the above code, we imported two webfonts from Google — Open Sans and PT Sans Narrow. Then, we simply set a dark gray background and a white text color for user selections.
We then set a width of 9px for our scrollbar (in Webkit browsers) and gave the ‘track’ a light gray background, a thin border and a mild inset box-shadow. Then, we gave a dark gray background to the scrollbar thumb and added a thin border to it.
Note: For more information on webkit-scrollbars, see Chris Coyier’s post.
Step 8: CSS Body
We’ll give our body a light gray noise background and apply the ‘Open Sans’ font we imported earlier. We’ll also add a red top border for an enhanced finesse.
Make some noise…
001
002 003 004 005 006 007 |
body {
font-family: ‘Open Sans’, sans-serif; background: url(‘../images/bg.gif’); padding: 0; margin: 0; border-top: 10px } |
Step 9: CSS Container
Now, for our container, we’ll set a width of 960px, a 10px top and bottom margin, and center it on the page by setting the right and left margins to ‘auto‘. We’ll also force hardware acceleration on (some) mobile devices by using ‘-webkit-transform: translateZ(0);‘.
001
002 003 004 005 |
.container {
width: 960px; margin: 10px -webkit-transform: translateZ(0); } |
Step 10: CSS Header
We’ll simply increase our heading font-size, center the text and give it a font-weight of 300 for a sleeker look.
001
002 003 004 005 |
header {
text-align: center; font-weight: 300; font-size: 700%; } |
Step 11: CSS Footer
We’ll center align the text horizontally, add the top and bottom margins of 50px each, set the text color to gray, and position it below the ‘work’ section by using ‘clear: both‘.
001
002 003 004 005 006 007 |
footer {
text-align: center; height: 100px; line-height: 100px; color: gray; clear: both; } |
Let’s work on the navigation now.
Step 12: CSS Navigation
We’ll start by removing all the default styling from our <ul>. Then, we’ll add a 50px top and bottom margin and align the text to the center.
001
002 003 004 005 006 |
nav ul {
list-style: none; padding: 0; margin: 50px text-align: center; } |
Now, by using ‘display: inline‘, we’ll get all our <li>s to display in one line. We’ll set the cursor to ‘pointer’ and add a 10px right margin to each <li>. The default text color will be a light shade of gray which will turn black on hover.
We’ll also add a small transition time to animate the color changes.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 |
nav ul li {
display: inline; cursor: pointer; margin-right: 10px; color: #666; transition: 0.3s; -webkit-transition: 0.3s; -moz-transition: 0.3s; -o-transition: 0.3s; -ms-transition: 0.3s; } nav ul li:hover { color: #000; } |
Since the last <li> is, umm, the last-child, it doesn’t need any right margin. So, we’ll remove it.
001
002 003 |
nav ul li:last-child {
margin-right: 0; } |
Now, let’s add the slashes after the <li>s. We’ll accomplish this by using the ‘:after‘ pseudo-selector. By setting its ‘content‘ to ‘/’, color to light gray, and an appropriate left margin, we can produce a simple-yet-effective system of link separation. We’ll also ensure that the slashes don’t change color on hover by forcing their default color on li:hover too.
001
002 003 004 005 006 007 008 |
nav ul li:after {
margin-left: 10px; content: ‘/’; color: #bbb; } nav ul li:hover:after { color: #bbb; } |
Again, we’ll have to remove the slash from the last <li>.
001
002 003 |
nav ul li:last-child:after {
content: ”; } |
That’s all for the navigation, let’s get to the thumbnails now.
Step 13: CSS Thumbnails
First, we’ll add a 20px top and bottom margin to the ‘.work’ section.
001
002 003 |
.work {
margin: 20px } |
Next, we’ll style each ‘.work figure‘ (i.e. all our thumbnails). We’ll use ‘float: left‘ to get them lined up. We’ll then add a 20px margin, set a height and width of 200px, and add a mild sepia effect by using ‘-webkit-filter: sepia(0.4)‘. Then, we’ll set the position to relative so that we can absolute position other elements (the caption in this case) within the figure. We’ll then add a mild box-shadowwhich will also work as our border. We’ll also add a small transition time for our boxes to grow and scale down.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 |
.work figure {
float: left; margin: 20px; width: 200px; height: 200px; background: #9d2e2c; line-height: 200px; -webkit-filter: sepia(0.4); position: relative; padding: 0 box-shadow: 0 transition: 0.6s; -webkit-transition: 0.6s; -moz-transition: 0.6s; -o-transition: 0.6s; -ms-transition: 0.6s; } |
We’ll ensure that our image always fits the thumbnail by setting its height and width to 100%.
001
002 003 004 |
.work figure a img {
height: 100%; width: 100%; } |
That’s all for our basic thumbnails. Let’s work on their captions now.
Step 14: CSS Captions
Description List
As we don’t want our caption to be visible initially, we’ll set its opacity to 0. Then, we’ll absolute position it (within the figure) and make it fill the figure fully by setting all the 4 properties — top, right, bottom, andleft — to 0.
We’ll then set its line-height to 2.5 and give it a dark, translucent background. Since we’re using a dark background, we’ll set its text color to white. We’ll also add a small transition time to animate its opacity onfigure:hover.
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 |
.work figure dl {
opacity: 0; position: absolute; left: 0; right: 0; bottom: 0; top: 0; padding: 20px; margin: 0; line-height: 2.5; background: rgba(0, 0, 0, 0.8); color: white; transition: 0.6s; -webkit-transition: 0.6s; -moz-transition: 0.6s; -o-transition: 0.6s; -ms-transition: 0.6s; } |
As we want it to appear on hovering on the thumbnail, we’ll set its opacity to 1 on figure:hover.
001
002 003 |
.work figure:hover dl {
opacity: 1; } |
Description Terms
First, we’ll set their font-family to ‘PT Sans Narrow’. To make them appear a bit smaller than their descriptions, we’ll set their font-size to 80%. Then, we’ll convert our description terms (Client and Role) into uppercase using ‘text-transform:uppercase‘. We’ll also set a negative bottom margin to avoid excessive spacing between the terms and their descriptions.
001
002 003 004 005 006 |
.work figure dl dt {
text-transform: uppercase; font-family: ‘PT Sans Narrow’; font-size: 12px; margin-bottom: -16px; } |
Definition Descriptions
We don’t need to do much here – we’ll just set the margin to 0. (By default, <dd>s have a slight left margin.)
001
002 003 |
.work figure dl dd {
margin-left: 0; } |
Step 15: CSS ‘current‘/’not-current‘ Thumbnails
.current
When the thumbnails of a certain category are given the .current class (through JS), we want them to grow and get their normal tone back (i. e. remove the sepia). We’ll scale them up by using ‘transform: scale(1.05)‘, thus scaling it to 1.05 times the original size on both the x and y axes and remove the sepia we had added earlier by using ‘-webkit-filter: sepia(0)‘.
001
002 003 004 005 006 007 008 009 010 011 012 013 |
.current {
-webkit-filter: sepia(0) !important; -webkit-transform: scale(1.05); -moz-transform: scale(1.05); -o-transform: scale(1.05); -ms-transform: scale(1.05); transform: scale(1.05); -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -o-backface-visibility: hidden; -ms-backface-visibility: hidden; backface-visibility: hidden; } |
.not-current
Here, we’ll do the exact opposite of what we did to the .current thumbnails — we’ll scale them down to 75% using ‘transform: scale(0.75)‘ and make them black and white using ‘-webkit-filter: grayscale(1)‘.
001
002 003 004 005 006 007 008 |
.not-current {
-webkit-transform: scale(0.75); -moz-transform: scale(0.75); -o-transform: scale(0.75); -ms-transform: scale(0.75); transform: scale(0.75); -webkit-filter: grayscale(1) !important; } |
.current-li
We’ll simply set the text color of the ‘.current-li’s to black.
001
002 003 |
.current-li {
color: #000; } |
Step 16: The Complete CSS
Here’s what our completed CSS looks like:
Let’s start working on the JS now.
Step 17: JS The Algorithm
Here is what we’re going to do through our Javascript (in chronological order):
-
Detect nav > li press.
-
Scale down all the thumbnails by giving them the .not-current class.
-
Add the .current-li class to the selected category’s corresponding <li>.
-
Remove the .not-current class only from the thumbnails of the selected category.
-
Add the .current class to all the thumbnails of the selected category.
#2 here will be done using the scaleDown() function and #3, #4, and #5 will be done using theshow(category) function.
Step 18: JS The scaleDown() Function
Using the removeClass and addClass methods, we’ll remove the .current class from the elements which have it and add the .not-current class to all of them. We’ll also remove the .current-li class from any<li which currently has it.
001
002 003 004 |
function scaleDown() { $(‘.work > figure’).removeClass(‘current’).addClass(‘not-current’); $(‘nav > ul > li’).removeClass(‘current-li’); } |
Step 19: JS The show(category) Function
This function will be implemented each time an <li> is clicked. First, we’ll call the scaleDown() function to scale down all the thumbnails. Then, we’ll add the .current-li class to the <li which corresponds to the selected category. We’ll then change the class of the category’s thumbnails from .not-current to.current (we had applied the .not-current class to all the thumbnails in the scaleDown() function). Finally, if the selected category is ‘all‘, we’ll remove the dynamically added classes (i.e. .current and.not-current) from all the thumbnails.
Note: Since the name of the id (of the <li) and class (of the figures) of each category is the same, we’ll simply refer to the <li as ‘# + category’ and the figures as ‘. + category‘.
001
002 003 004 005 006 007 008 009 010 011 |
function show(category) { scaleDown(); $(‘#’ $(‘.’ $(‘.’ if $(‘nav > ul > li’).removeClass(‘current-li’); $(‘#all’).addClass(‘current-li’); $(‘.work > figure’).removeClass(‘current, not-current’); } } |
Step 20: JS Detecting Clicks and Implementing the show(category)Function
Finally, through the document.ready method, we’ll add the .current-li class to li#all and detect nav > li clicks. We’ll then get the id of the clicked <li and implement the show(category) function where the ‘category‘ will be ‘this.id‘ i.e. id of the clicked <li>. So, for example, if the <li> with the id #print is clicked, show(‘print’) will be implemented.
001
002 003 004 005 006 |
$(document).ready(function(){
$(‘#all’).addClass(‘current-li’); $(“nav > ul > li”).click(function(){ show(this.id); }); }); |
This completes our Javascript.
Step 21: The Complete JS
Our completed JS looks like this:
Now that our site is fully functional, let’s make it responsive.
Step 22: CSS Making it Responsive
Let’s open ‘media-queries.css’ and get going. How you choose to implement your media queries is entirely up to you. You may prefer to have media queries within your main stylesheet, you may even prefer to have them modular and inline with each and every style declaration – it’s up to you!
Style changes required for each breakpoint are described here.
965px or less
-
Decrease the width of the container to 840px
-
Decrease the height and width of the thumbnails to 170px each so as to accommodate 4 thumbnails in each row [(170px + 40px) x 4 = 210px x 4 = 840px]
001
002 003 004 005 006 007 008 009 010 |
/* Small viewports — Old monitors, netbooks, tablets (landscape), etc. */
@media only screen .container { width: 840px; } .work figure { width: 170px; height: 170px; } } |
860px or less
-
Decrease the width of the container to 720px
-
Increase the height and width of the thumbnails back to 200px each to accommodate 3 in each row [(200px + 40px) x 3 = 240px x 3 = 720px]
001
002 003 004 005 006 007 008 009 010 |
/* Smaller viewports — more tablets, old monitors */
@media only screen .container { width: 720px; } .work figure { width: 200px; height: 200px; } } |
740px or less
-
Decrease the width of the container to 600px
-
Decrease the opacity of the .not-current to 50% (since we’re mainly working for mobile devices now)
-
Decrease the height and width of the thumbnails to 160px each to accommodate 3 in each row [(160px + 40px) x 3 = 200px x 3 = 600px]
001
002 003 004 005 006 007 008 009 010 011 012 013 |
/* Even smaller viewports — more tablets, etc. */
@media only screen .container { width: 600px; } .work figure { width: 160px; height: 160px; } .not-current { opacity: 0.5; } } |
610px or less
-
Decrease the width of the container to 460px
-
Set the top and bottom margin of the thumbnails to 20px and left and right margin to 60px
-
Increase the height and width of the thumbnails back to 200px each to accommodate 1 in each row [(200px + 120px) x 1 = 320px x 1 = 320px]
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 |
/* Mobile phones (Landscape) and Tablets (Portrait) */
@media only screen .container { width: 460px; } header { font-size: 400%; } nav ul li { } .work figure { margin-left: 40px; margin-bottom: 60px; } .work figure dl { height: 40px; top: 200px; bottom: 0px; } } |
480px or less
-
Decrease the width of the container to 320px
-
Set the top and bottom margin of the thumbnails to 20px and left and right margin to 60px
-
Increase the height and width of the thumbnails back to 200px each to accommodate 1 in each row [(200px + 120px) x 1 = 320px x 1 = 320px]
001
002 003 004 005 006 007 008 009 010 011 |
/* Mobile phones (Portrait) */
@media only screen .container { width: 320px; } .work figure { width: 200px; height: 200px; margin: 20px } } |
Browser Compatibility
The basic scaling functionality (CSS transforms) works perfectly in most major browsers, which include:
-
IE 9+ (use http://www.useragentman.com/IETransformsTranslator/ for lower versions)
-
Firefox 14+
-
Chrome 21+
-
Safari 5.1+
The filter effects (sepia and grayscale) work only in Webkit browsers.
jsFiddles
I’ve made three jsFiddles for you to try out and experiment with:
Conclusion
That’s it! We have successfully created a working, filterable, and responsive portfolio. I hope you found this tutorial useful. Thanks for reading!
Tutorial provided by: webdesign.tutsplus.com