In today’s tutorial we will be making a beautiful HTML5 portfolio powered by jQuery and the Quicksand plugin. You can use it to showcase your latest work and it is fully customizable, so potentially you could expand it to do much more.
The HTML
The first step is to write down the markup of a new HTML5 document. In the head section, we will include the stylesheet for the page. The jQuery library, the Quicksand plugin and our script.js will go right before the closing body tag:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Making a Beautiful HTML5 Portfolio | Tutorialzine Demo</title>
<!-- Our CSS stylesheet file -->
<link rel="stylesheet" href="assets/css/styles.css" />
<!-- Enabling HTML5 tags for older IE browsers -->
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<header>
<h1>My Portfolio</h1>
</header>
<nav id="filter">
<!-- The menu items will go here (generated by jQuery) -->
</nav>
<section id="container">
<ul id="stage">
<!-- Your portfolio items go here -->
</ul>
</section>
<footer>
</footer>
<!-- Including jQuery, the Quicksand plugin, and our own script.js -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script src="assets/js/jquery.quicksand.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>
In the body, there are a number of the new HTML5 elements. The header holds our h1 heading (which is styled as the logo), the section element holds the unordered list with the portfolio items (more lists are added by jQuery, as you will see later), and the nav element, styled as a green bar, acts as a content filter.
The #stage unordered list holds our portfolio items. You can see what these items should look like below. Each of them has a HTML5 data attribute, which defines a series of comma-separated tags. Later, when we use jQuery to loop through this list, we will record the tags and create categories that can be selected from the green bar.
<li data-tags="Print Design"> <img src="assets/img/shots/1.jpg" /> </li> <li data-tags="Logo Design,Print Design"> <img src="assets/img/shots/2.jpg" /> </li> <li data-tags="Web Design,Logo Design"> <img src="assets/img/shots/3.jpg" /> </li>
You can put whatever you want in these li items and customize the portfolio further. The Quicksand plugin will handle the animated transitions of this list, so you are free to experiment.
The jQuery
What the Quicksand plugin does, is compare two unordered lists of items, find the matching LIs inside them, and animate them to their new positions. The jQuery script we will be writing in this section, will loop through the portfolio items in the #stage list, and create a new (hidden) unordered list for each of the tags it finds. It will also create a new menu option, which will trigger the quicksand transition between the two lists.
First we need to listen for the ready event (the earliest point in the loading of the page where we can access the DOM), and loop through all the li items detecting the associated tags.
script.js – Part 1
$(document).ready(function(){
var items = $('#stage li'),
itemsByTags = {};
// Looping though all the li items:
items.each(function(i){
var elem = $(this),
tags = elem.data('tags').split(',');
// Adding a data-id attribute. Required by the Quicksand plugin:
elem.attr('data-id',i);
$.each(tags,function(key,value){
// Removing extra whitespace:
value = $.trim(value);
if(!(value in itemsByTags)){
// Create an empty array to hold this item:
itemsByTags[value] = [];
}
// Each item is added to one array per tag:
itemsByTags[value].push(elem);
});
});
Each tag is added to the itemsByTags object as an array. This would mean that itemsByTags['Web Design'] would hold an array with all the items that have Web Design as one of their tags. We will use this object to create hidden unordered lists on the page for quicksand.
It would be best to create a helper function that will handle it for us:
script.js – Part 2
function createList(text,items){
// This is a helper function that takes the
// text of a menu button and array of li items
// Creating an empty unordered list:
var ul = $('<ul>',{'class':'hidden'});
$.each(items,function(){
// Creating a copy of each li item
// and adding it to the list:
$(this).clone().appendTo(ul);
});
ul.appendTo('#container');
// Creating a menu item. The unordered list is added
// as a data parameter (available via .data('list')):
var a = $('<a>',{
html: text,
href:'#',
data: {list:ul}
}).appendTo('#filter');
}
This function takes the name of the group and an array with LI items as parameters. It then clones these items into a new UL and adds a link in the green bar.
Now we have to loop through all the groups and call the above function, and also listen for clicks on the menu items.
script.js – Part 3
// Creating the "Everything" option in the menu:
createList('Everything',items);
// Looping though the arrays in itemsByTags:
$.each(itemsByTags,function(k,v){
createList(k,v);
});
$('#filter a').live('click',function(e){
var link = $(this);
link.addClass('active').siblings().removeClass('active');
// Using the Quicksand plugin to animate the li items.
// It uses data('list') defined by our createList function:
$('#stage').quicksand(link.data('list').find('li'));
e.preventDefault();
});
// Selecting the first menu item by default:
$('#filter a:first').click();
Great! Now that we have everything in place we can move on to styling the page.
The CSS
Styling the page itself is not that interesting (you can see the CSS for this in assets/css/styles.css). However what is more interesting is the green bar (or the #filter bar), which uses the :before / :after pseudo elements to add attractive corners on the sides of the bar. As these are positioned absolutely, they also grow together with the bar.
styles.css
#filter {
background: url("../img/bar.png") repeat-x 0 -94px;
display: block;
height: 39px;
margin: 55px auto;
position: relative;
width: 600px;
text-align:center;
-moz-box-shadow:0 4px 4px #000;
-webkit-box-shadow:0 4px 4px #000;
box-shadow:0 4px 4px #000;
}
#filter:before, #filter:after {
background: url("../img/bar.png") no-repeat;
height: 43px;
position: absolute;
top: 0;
width: 78px;
content: '';
-moz-box-shadow:0 2px 0 rgba(0,0,0,0.4);
-webkit-box-shadow:0 2px 0 rgba(0,0,0,0.4);
box-shadow:0 2px 0 rgba(0,0,0,0.4);
}
#filter:before {
background-position: 0 -47px;
left: -78px;
}
#filter:after {
background-position: 0 0;
right: -78px;
}
#filter a{
color: #FFFFFF;
display: inline-block;
height: 39px;
line-height: 37px;
padding: 0 15px;
text-shadow:1px 1px 1px #315218;
}
#filter a:hover{
text-decoration:none;
}
#filter a.active{
background: url("../img/bar.png") repeat-x 0 -138px;
box-shadow: 1px 0 0 rgba(255, 255, 255, 0.2),
-1px 0 0 rgba(255, 255, 255, 0.2),
1px 0 1px rgba(0,0,0,0.2) inset,
-1px 0 1px rgba(0,0,0,0.2) inset;
}
With this our beautiful HTML5 portfolio is complete!
Conclusion
You can use this template to present your work. The best thing about it is that it’s really easy to customize. You only need to change the contents of the initial LI items of the #stage list, and specify some new tags – the script will do the rest. If the visitor doesn’t have JavaScript enabled, they will still see all your portfolio items, which is also good for SEO purposes.






41 Comments
very nice, love your ribbon style navigation
Very nice portfolio solution. If I ever get round to doing my site I will look into this one again.
Slowly but surely you are rising up as one of the greats online. Keep it up mate.
it’s so beautiful master…
so elegant ;)
Great portfolio Tutorial!
Very nice as always.
wow this is like desandros isotope plugin, really impressive! I’d love to see pushstate functionality though. It’s the new black nowadays I’ve heard
wow that’s fantastic dude keep up !
Very nice portfolio idea! Thank you for the article!
I’ve already seen this before similar to QuickSand http://razorjack.net/quicksand/ which I think has more and possibly better functionality. However, this is very nice as well and I guess another alternative with a little less coding.
Thank you for the tutorial. I’m using it on my site now :) More power to you!
Been fighting with this all night, is there a way to make it working with ANY *box plugin?
I basically tried every single one and i can’t get it do its job. Currently testing chillbox.
Also I wonder if it’s possible to have some kind of “start page” ?
I’d like to make an introduction thing
thanks in advance
You can make it work by adding a callback to the Quicksand plugin like this:
$('#stage').quicksand(link.data('list').find('li'),function(){ // FancyBox initialization code. });In this callback you will have to call the plugin with the visible elements only (given that you have enclosed the images in the LIs in an anchor element):
$('a:visible').fancybox({...});Remember to add proper rel attributes to these links, which is required by fancybox to display the images as a group.
Thanks for the FancyBox callback Martin.
I’m having trouble getting fancybox to work. I tried adding the callback to jquery.quicksand.js. But I didn’t understand what do with this line of code
$(‘a:visible’).fancybox({…});
Hi Jeff, i’m having the same problem right now, have you already solved the question?
Thank you.
Will Fancybox or another lightbox plugin work in conjunction with quicksand plugin? If so what is best way to incorporate?
nicely done. I love reading your tutorials so keep’em coming
Oh man! You guys are just amazing. This tutorial is very helpful and beautifully done.
Love the end result. Its so beautiful !
Awesome!
Your timing is simply superb Martin, It’s like you heard my nigtmares!
I’m currently trying to integrate quicksand into my new site and this is just perfect!
Many thanks.
very nice. It will be useful for my next project
Incredible article, HTML5 is awesome!
Love it!
thx, awesome tut really helpful and i love it =)
Good portfolio. Let me try to implement in my site.
What if I want to implement more than one kind of filter?
So I might want to filter on either media (painting/drawing/digital) or themes (abstract/figurative)?
The simplest solution for providing multiple filters would seem to be to prepare the tags something like this, but I’m struggling to implement this solution…
data-tags=”{Media:Logo Design,Print Design},{Theme:Figurative,Portraiture}”
For my purposes, while dynamic, the dataset is likely to remain fairly small so it wouldn’t matter much if only one kind of filter could be applied at any one time.
Nevermind, found exactly what I was looking for over at:http://blog.adamroderick.com/2010/11/multiple-filters-with-jquery-quicksand/
Great tutorial! Thanks for sharing.
Great tutorial but there needs to be a push function on the image or something. I am trying to combine another jquery code with this one (pirobox) but I havent had any luck. I’ve searched and tried everything I can think of but still no results. Would you know how to seamlessly make quicksand and pirobox work together or make a tutorial that has a combination of pirobox and quicksand. I’d appreciate :)
Thanks
This is a nice effect and could be useful in a lot of different contexts, but it is rendered virtually useless due to its inability to play nicely with any kind of *box plugin.
Very beautiful! Was looking for this for a while now. Thanks!
Fantastic tutorial, thank you for this! Gonna modify it and use it on my own site.
Im still getting a vast amount on whitespace under my container, any suggestions?
awesome designs!!!
nicely designed and well explained . thanks
Very nice! Can’t wait to use it for one of my projects.
I really like this html5 demo. I tried to play with it like changing the image contents. But why is it that the slideshow stop functioning? Any helpful advice?
Newbie to coding.
Thanks.
Its Definitely good bookmarking for future reference.
Regards
Can
Awesome tut,helped me a lot and even amazing effects thanks a lot….
Love the use of pseudo elements for the styling of banner. and good transitions on the images.
I have a question, i don’t know much about this, but i´m using your great work for a personal portfolio an i need know if it its possible to add a link in the filter menu for an external html page?
I can show you what i’m doing if you want.
Thank you
Luís