Advertisement

Creating an Adaptive, Filterable Portfolio Using jQuery Isotope

by

Today we'll be taking a PSD Portfolio layout from ThemeForest, replicating it entirely in HTML & CSS whilst making it adaptive. We'll then go further and integrate the Isotope jQuery plugin to make it a fully functional filterable portfolio.


Step 1: Organizing the Project

We'll start off by creating a simple project structure so everything is kept organized. We'll create three folders:

  • css - for our CSS files
  • images - all images will be placed in here
  • js - for our jQuery plugins and custom scripts


Step 2: Document

Before we get stuck into our coding we'll create the index.html file which can be placed in the root of your project. We'll throw in a basic HTML5 template, linking to our CSS file in the head. Additionally, we'll need to link to some more files:

  • jQuery Library - We'll need to link to the jQuery library ready for later on when we use the Isotope plugin, we'll go ahead and throw that in now. I've gone and used the library hosted by Google (highly recommended).
  • HTML5 Shiv - Since we'll be using HTML5 elements we'll need to make sure we link to the HTML5 Shiv to allow the elements to be recognized in older IE versions.
  • Google Web Fonts - If you look at the PSD you'll notice that the font PT Sans is used. Since this font won't be locally available for many users we'll link to it using the Google Web Fonts service.
 
<!DOCTYPE html> 
<html> 
<head> 
 
	<!--Meta tags--> 
	<meta charset="utf-8"> 
 
	<!--Title--> 
	<title>Hipstar Tutorial</title> 
		 
	<!--Stylesheets--> 
	<link rel="stylesheet" href="css/styles.css"> 
 
	<!--Google Web Fonts--> 
	<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans+Narrow|PT+Sans+Caption:400,700"> 
 
	<!--jQuery--> 
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> 
 
	<!--HTML5 Shiv--> 
	<!--[if lt IE 9]> 
			<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 
	<![endif]--> 
	 
</head> 
<body> 
 
 
 
 
 
 
 
</body> 
</html>

Step 3: Adding General Styles

We'll now need some general styles in our CSS file. This will involve just a simple reset which you can add to the top of your CSS file.

 
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,and,address,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video,input,textarea,select{background:transparent;border:0;font-size:100%;margin:0;outline:0;padding:0;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}abbr[title],dfn[title]{border-bottom:1px dotted;cursor:help}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}del{text-decoration:line-through}hr{background:transparent;border:0;clear:both;color:transparent;height:1px;margin:0;padding:0}mark{background-color:#ffffb3;font-style:italic}input,select{vertical-align:middle}ins{background-color:red;color:white;text-decoration:none}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}a{text-decoration:none;} 
.clear{clear:both;display:block;height:0;overflow:hidden;visibility:hidden;width:0}.clearfix:after{clear:both;content:' ';display:block;font-size:0;height:0;line-height:0;visibility:hidden;width:0}* html .clearfix,:first-child+html .clearfix{zoom:1}

Step 4: Starting With the Header

Now we'll begin building the structure of the site! We'll start off with the header and do so by using the HTML5 Header element.

 
<header class="header clearfix"> 
	 
	 
</header>

Next we'll add some CSS for our header. We'll apply some basic styles here, but the main aspect to take into account is the positioning. Here we're using a position of fixed and then 0 on the top, left and right. This will ensure our header stays at the top of the window when scrolling - and that it fills the whole height of the browser.

 
.header { 
	height:69px; 
	background:url(../images/header.png) repeat; 
	border-bottom:1px solid #fff; 
	-webkit-box-shadow:0 0 7px rgba(0,0,0,.25); 
	-moz-box-shadow:0 0 7px rgba(0,0,0,.25); 
	box-shadow:0 0 7px rgba(0,0,0,.25); 
	position:fixed; 
	top:0; 
	left:0; 
	right:0; 
	z-index:101; 
}

Step 5: The Logo and "Hire Me" Badge

For simplicity's sake, both these parts will be carried out using images, but we'll also wrap them within anchor tags and give them a class so we can set some styles for positioning.

 
<header class="header clearfix"> 
	 
	<a href="#" class="logo"><img src="images/logo.png" alt="Hipstar"></a> 
 
	<a href="#" class="hire-me"><img src="images/hire_me.png" alt="Hire Me"></a> 
 
</header>

Both of these use similar styling, both positioned absolutely (the header being the parent) with a 0 top. The logo has left of 0 to keep it on the left and the hire-me badge a right of 20px to push it slightly off from the right.

 
.logo { 
	position:absolute; 
	top:0; 
	left:0; 
} 
 
.hire-me { 
	position:absolute; 
	top:0; 
	right:20px; 
}

You should have something that looks like this:



Step 6: The Sidebar

Once again we'll take advantage of HTML5's markup and create our sidebar using the aside element.

 
<aside class="sidebar clearfix"> 
 
 
</aside>

Onto the CSS for the sidebar, you'll notice that we've used position fixed again, technically this doesn't really apply here as the slider is going to fill the height of the page.

 
.sidebar { 
	width:149px; 
	position:fixed; 
	top:70px; 
	left:0; 
	bottom:0; 
	right:0; 
	z-index:1; 
	float:left; 
	border-right:1px solid #a13d36; 
	margin-right:-1px; 
	-webkit-box-shadow:inset -1px 0 0 #ff786b; 
	-moz-box-shadow:inset -1px 0 0 #ff786b; 
	box-shadow:inset -1px 0 0 #ff786b; 
}

Step 7: Sidebar Background!

You'll notice we've failed to define a background.. We'll apply the sidebar background, but not to the sidebar; we'll apply it to the body and repeat it on the y-axis so it runs from top to bottom. A position of left will also be used to ensure the background is placed on the left side of the page for the sidebar.

Seeing as this is a body style it may make sense to scroll up your CSS file and place it nearer the top. I also went ahead and added a font smoothening property for webkit browsers.

 
body { 
	background:#f8faf4 url(../images/sidebar_body.png) fixed left repeat-y; 
	-webkit-font-smoothing:antialiased; 
}

We'll have now completed the base of our sidebar!



Step 8: Navigation Time

Now the sidebar is made we can add the navigation to it. We'll create an unordered list and wrap it with the HTML5 Nav element.

 
<nav class="primary clearfix"> 
	<ul> 
		<li><a href="#">All</a></li> 
		<li><a href="#">Web Design</a></li> 
		<li><a href="#">Illustration</a></li> 
		<li><a href="#">Logo</a></li> 
		<li><a href="#">Video</a></li> 
		<li><a href="#">Print Design</a></li> 
	</ul> 
</nav>

Now some basic styling for the navigation, adding a background, font styles and also the font family from Google Web Fonts - PT Sans.

 
nav.primary ul li a { 
	background:url(../images/nav.png) repeat-x center top; 
	height:39px; 
	width:149px; 
	display:block; 
	border-bottom:1px solid #ba4b42; 
	font-family:'PT Sans Narrow', Helvetica, Arial, sans-serif; 
	font-size:18px; 
	color:#fff; 
	text-indent:20px; 
	text-shadow:0 -1px 0 #BA1E1A; 
	text-decoration:none; 
	line-height:39px; 
} 
nav.primary ul li a:hover { 
	color:#E9EBE5; 
	background-position:center bottom; 
}

Step 9: Creating the Main Content Area

 
<section class="main clearfix"> 
	 
	 
</section>

We'll now need to create a wrapping container which will hold the portfolio entries. Also, we'll have to add some margins and paddings to ensure the entries display in the correct place. This can be done by first moving the entire element away from the header and sidebar.

If we look at the header being 70px high we'll need 70px margin-top. The sidebar is 150px wide so we'll apply 150px margin-left. The final thing is 20px padding on the top and left to push the entries away from the header and sidebar.

 
.main { 
	padding:20px 20px 0; 
	margin:70px 0 0 150px; 
}

Step 10: Adding a Portfolio Entry

Time to start with the portfolio entries. Go ahead and create a div with a class of portfolio inside our main area, this will hold our portfolio items together. Our portfolio entry is made up of a figure (again an HTML5 element) which is used to denote a container for media of some sort.

We'll then have an image with two spans; one for the ribbon and one for the hover. You could also use pseudo-elements for these, which would lighten the markup, though reduce the browser compatibility of the page.

 
<section class="main clearfix"> 
	 
	<div class="portfolio"> 
		 
		<figure class="entry"> 
			<span class="ribbon"></span> 
			<img src="images/portfolio-image.jpg" alt=""> 
			<span class="hover"></span> 
		</figure> 
		 
	</div> 
 
</section>

Step 11: Styling the Portfolio Item

The styling for the portfolio is basic stuff. We'll float left and add 20px of margin right and bottom. To create the white border we'll simply add some padding and then a background, these will be rounded off using a simple border-radius of 5px (while remembering the prefixes). Finally, a box-shadow will be applied.

 
.entry { 
	position:relative; 
	float:left; 
	margin-right:20px; 
	margin-bottom:20px; 
	cursor:pointer; 
 
	background:#fff; 
	padding:10px; 
	width:440px; 
 
	-webkit-border-radius:5px; 
	-moz-border-radius:5px; 
	border-radius:5px; 
 
	-webkit-box-shadow:0 0 7px rgba(0,0,0,.1); 
	-moz-box-shadow:0 0 7px rgba(0,0,0,.1); 
	box-shadow:0 0 7px rgba(0,0,0,.1); 
}

As we're applying 20px of margin to the right of the entries we'll need to subtract that off the parent container (the portfolio div).

 
.portfolio { margin-right:-20px; }

Step 12: Adding the Entry Ribbon

We created the ribbon markup when adding the portfolio item (though, again, you may have used a pseudo-element), we'll now need to style it using CSS.

 
.ribbon { 
	background:url(../images/camera_ribbon.png) no-repeat; 
	width:31px; 
	height:47px; 
	position:absolute; 
	top:-4px; 
	right:11px; 
	z-index:9999; 
}

Step 13: Portfolio Hover State

The last thing to do for the portfolio is to add the hover state. This will be acheived with the span we created previously, all we need now is the styling. We'll use an image and a black background with an alpha opacity of 70%. We've also added some CSS3 transitions to allow a smooth hover effect. The last thing is to add opacity:1; for when hovering over the entry, which will fade in the hover part.

 
.hover { 
	background:rgba(0,0,0,.7) url(../images/hover.png) no-repeat center; 
	position:absolute; 
	top:10px; 
	left:10px; 
	bottom:13px; 
	right:10px; 
	opacity:0; 
	-webkit-transition:all .3s ease-in-out; 
	-moz-transition:all .3s ease-in-out; 
	-ms-transition:all .3s ease-in-out; 
	-o-transition:all .3s ease-in-out; 
	transition:all .3s ease-in-out; 
} 
 
.entry:hover .hover { opacity:1; }

Now our portfolio item is complete, you are free to add you own and change the images!



Step 14: Adaptive Goodness

Now that we have replicated the PSD into a working design, we'll go ahead and make it adaptive. We aren't using a purely flexible grid, which is why what we're practicing isn't technically responsive. We'll use CSS3 Media Queries to change and edit the layout when in different view modes. We'll use rough dimensions, giving us what can more-or-less be perceived as Tablet Portrait Orientation, Tablet Landscape Orientation, Mobile Portrait Orientation and, finally, Mobile Landscape Orientation.

Before we start editing the layout we need to do two things. Firstly we'll need to add the viewport meta tag which will allow our site to be viewed correctly on mobiles and tablets, if you'd like to find out more about this be sure to check out this article by Ian Yates. The last is to add a short bit of CSS to allow our images to be fluid:

 
img { 
	max-width:100%; 
}

Step 15: Tablet Portrait

We'll start off by targeting tablets with a portrait orientation. Only a small amount of editing is needed to be done to adjust our layout. The main one to take notice of is the entry; it will be made smaller to accommodate more entries on each line.

 
@media only screen and (min-width: 768px) and (max-width: 959px) { 
	.content { padding:20px 0 0 20px; } 
	.entry { width:258px; } 
}

Step 16: Tablet Landscape

Usually you wouldn't target the landscape orientation of a tablet unless you really have to, but because of the design of this page it will certainly help us out. We won't use min and max widths to determine the viewport size - we'll target it based on orientation. This time we're using even less code by simply changing the width of the portfolio entry.

 
@media only screen and (max-device-width: 1024px) and (orientation:landscape) {  
    .entry { width:386px; } 
}

Step 17: Mobile Portrait

We'll move onto mobiles now, starting with portrait orientation. This time we'll have to do a bit more editing. We're going to adjust the layout now, because of the screen getting smaller we're no longer able to accommodate the sidebar on the left with entries on the right. We'll move the sidebar up before the entries and have it fill the entire width of the screen.

Also, due to the header being a large size with the sidebar/navigation beneath it you may not be able to see as many of the entries. We'll remove the position: fixed and change it to absolute so the header will now scroll instead of staying at the top of the page.

 
@media only screen and (max-width: 767px) { 
	body { background-image:none; } 
 
	.header { 
		position:absolute; 
		float:left; 
		width:100%; 
	} 
 
	.logo { float:left; } 
	.hire-me { float:right; } 
 
	#wrapper { 
		float:left; 
		width:100%; 
	} 
 
	.sidebar { 
		width:100%; 
		position:relative; 
		z-index:1; 
		float:left; 
		border-right:1px solid #a13d36; 
		margin-right:-1px; 
		-webkit-box-shadow:none; 
		-moz-box-shadow:none; 
		box-shadow:none; 
	} 
 
	nav.primary li { 
		float:left; 
		width:100%; 
		background:url(../images/sidebar.png); 
	} 
 
	nav.primary ul li a {width:100%; } 
	 
	nav.primary ul li a:active, nav.primary ul li a.selected { background:url(../images/mobile_nav_active.png); } 
 
	.page { 
		float:left; 
		width:100%; 
	} 
 
	.main { 
		float:left; 
		padding:20px 20px 0; 
		margin:70px 0 0 0; 
	} 
 
	.entry { width:260px; } 
}

Step 18: Fixing Some Positioning Problems

Okay, now we've run into some problems. The sidebar has adjusted nicely to the screen layout, but portfolio entries are positioning incorrectly. We'll need to go back to our markup and add two extra divs, one with an id of wrapper which will wrap everything other than the header and another with a class of page which will wrap our main content area.

 
<div id="wrapper"> 
 
	<aside class="sidebar clearfix"> 
 
		<nav class="primary clearfix"> 
				<ul> 
					<li><a href="#" class="selected" data-filter="*">All</a></li> 
					<li><a href="#" data-filter=".web">Web Design</a></li> 
					<li><a href="#" data-filter=".ill">Ilustration</a></li> 
					<li><a href="#" data-filter=".logo">Logo</a></li> 
					<li><a href="#" data-filter=".video">Video</a></li> 
					<li><a href="#" data-filter=".print">Print Design</a></li> 
				</ul> 
			</nav> 
 
	</aside> 
 
	<div class="page"> 
 
		<section class="main clearfix"> 
			 
			<div class="portfolio"> 
				 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
				<figure class="entry"> 
					<span class="ribbon"></span> 
					<img src="images/portfolio-image.jpg" alt=""> 
					<span class="hover"></span> 
				</figure> 
 
			</div> 
 
		</section> 
 
	</div> 
	<!-- page --> 
 
</div> 
<!-- wrapper -->

After adding in the necessary markup, we'll have to add some styles. Scroll back on up to the top of your CSS file and add the following:

 
#wrapper { 
    height:auto; 
    margin:0; 
    overflow:hidden; 
    padding:0; 
} 
 
.page { 
	position:relative; 
	overflow:hidden; 
	top:0px; 
	margin:0px; 
	padding:0px; 
	border-left:1px solid #a13d36; 
}

Step 19: Mobile Landscape

Okay, finally the landscape orientation for mobile. If you place this after the mobile portrait CSS we just created it'll inherit those styles, .e.g the full width sidebar etc. All we need to do now is change the entry width.

 
@media only screen and (min-width: 480px) and (max-width: 767px) { 
	.entry { width:190px; } 
}


Step 20: Introducing Isotope

Now we've covered creating the design we can start to give it some functionality! We'll be using the brilliant isotope plugin by David DeSandro. Isotope is used to create layouts, filtering and sorting. It is commonly used on portfolios to filter pieces of work into different categories - for example Web Design, Photography and Illustration. On that note, let's get it installed!

Before we can add it to our design we'll need to download it. Head on over to http://isotope.metafizzy.co/jquery.isotope.min.js. You'll now see the Isotope javascript file, just go to File > Save and save it in the js folder we created at the beginning. We'll need to then link to the script within your HTML file.

 
<script src="js/jquery.isotope.min.js"></script>

Step 21: The Filtering Process

To allow our portfolio items to be filtered we'll need to change our markup again. We'll start off with the navigation which will filter for us. Isotope uses HTML5 Custom Data Attributes, specifically data-filter="". We set a value depending on the category; Web Design uses .web (note that we have to add a period before the category value). The anchor tag for All categorys doesn't use .all though, it uses an asterisk.

 
<nav class="primary clearfix"> 
	<ul> 
		<li><a href="#" data-filter="*">All</a></li> 
		<li><a href="#" data-filter=".web">Web Design</a></li> 
		<li><a href="#" data-filter=".ill">Ilustration</a></li> 
		<li><a href="#" data-filter=".logo">Logo</a></li> 
		<li><a href="#" data-filter=".video">Video</a></li> 
		<li><a href="#" data-filter=".print">Print Design</a></li> 
	</ul> 
</nav>

Step 22: Filtering Portfolio Items

The final part of the filter process is to edit the portfolio items. After adding data-filter values you'll need to add the corresponding values to each entry. If the work entry is in the video category you'll need to add a class of video. Do note that if your data-filter values are lowercase, you must use lowercase for the entry classes.

 
<figure class="entry video"> 
	<span class="ribbon"></span> 
	<img src="images/portfolio-image.jpg" alt=""> 
	<span class="hover"></span> 
</figure>

Step 23: Hooking the Plugin

It's time to activate Isotope which we'll do in two parts. First we need to hook the part which will sort our portfolio entries. Start off by creating a new file called "custom.js" and place it in your js folder.

 
$(window).load(function(){ 
 
	var $container = $('.portfolio'); 
	$container.isotope({ 
		filter: '*', 
		animationOptions: { 
			duration: 750, 
			easing: 'linear', 
			queue: false, 
		} 
	}); 
 
});

Step 24: Click Function

The second part will hook the navigation to allow the filtering to happen.

 
$(window).load(function(){ 
 
	var $container = $('.portfolio'); 
	$container.isotope({ 
		filter: '*', 
		animationOptions: { 
			duration: 750, 
			easing: 'linear', 
			queue: false, 
		} 
	}); 
 
	$('nav.primary ul a').click(function(){ 
		var selector = $(this).attr('data-filter'); 
		$container.isotope({ 
			filter: selector, 
			animationOptions: { 
				duration: 750, 
				easing: 'linear', 
				queue: false, 
			} 
		}); 
	  return false; 
	}); 
 
});

Step 25: Adding an Active State

Shown in the PSD is an active state for the current selected navigation anchor. We'll need to add some more markup, some more CSS and finally some more jQuery. Start off by heading on back to your HTML file and adding class="selected" to the first anchor tag.

 
<nav class="primary clearfix"> 
	<ul> 
		<li><a href="#" class="selected" data-filter="*">All</a></li> 
		<li><a href="#" data-filter=".web">Web Design</a></li> 
		<li><a href="#" data-filter=".ill">Illustration</a></li> 
		<li><a href="#" data-filter=".logo">Logo</a></li> 
		<li><a href="#" data-filter=".video">Video</a></li> 
		<li><a href="#" data-filter=".print">Print Design</a></li> 
	</ul> 
</nav>

Next you'll need to add some more CSS, so add this just below the navigation styles.

 
nav.primary ul li a:active, nav.primary ul li a.selected { 
	background:url(../images/nav_active.png); 
	border-bottom:1px solid #9e3f38; 
}

The final part is some jQuery, this will add classes when clicked to show the active button/category.

 
var $optionSets = $('nav.primary ul'), 
       $optionLinks = $optionSets.find('a'); 
  
       $optionLinks.click(function(){ 
          var $this = $(this); 
	  // don't proceed if already selected 
	  if ( $this.hasClass('selected') ) { 
	      return false; 
	  } 
   var $optionSet = $this.parents('nav.primary ul'); 
   $optionSet.find('.selected').removeClass('selected'); 
   $this.addClass('selected');  
});

Step 26: Forgetting Something Are We?

If you go ahead and click the nav buttons you'll see that the filter is working! But wait, it's not a smooth transition! Well that's because we still have to add some CSS transitions.

 
.isotope-item { 
  z-index: 2; 
} 
 
.isotope-hidden.isotope-item { 
  pointer-events: none; 
  z-index: 1; 
} 
 
.isotope, 
.isotope .isotope-item { 
-webkit-transition-duration: 0.8s; 
   -moz-transition-duration: 0.8s; 
        transition-duration: 0.8s; 
} 
 
.isotope { 
-webkit-transition-property: height, width; 
   -moz-transition-property: height, width; 
        transition-property: height, width; 
} 
 
.isotope .isotope-item { 
-webkit-transition-property: -webkit-transform, opacity; 
   -moz-transition-property:    -moz-transform, opacity; 
        transition-property:         transform, opacity; 
}


Conclusion

And there we go, another useful tutorial complete! You're free to use this method in any designs you have. Go and have some fun with it.


I hope you enjoyed this tutorial, thanks for reading!