Build a Responsive, Filterable Portfolio, with CSS3 Twists

Tutorial Details

  • Topic: Site build
  • Difficulty: Intermediate
  • Estimated completion time: 1-2 hours

 

Final Product What You’ll Be Creating

DownloadSOURCE FILES

DemoVIEW IT ONLINE

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
lang=”en”>

<head>

<meta
http-equiv=”content-type”
content=”text/html; charset=utf-8″
/>

<meta
name=”viewport”
content=”width=device-width, initial-scale=1.0″>

<title>John Doe’s Portfolio</title>

<link
rel=”shortcut icon”
type=”image/x-icon”
href=”images/favicon.ico”>

<link
rel=”stylesheet”
href=”css/style.css”
media=”screen”>

<link
rel=”stylesheet”
href=”css/media-queries.css”
media=”screen”>

<script
src=”http://code.jquery.com/jquery-latest.min.js”></script&gt;

<script
src=”js/js.js”></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
class=”container”>

<header>

<h1
class=”title”>

John Doe

</h1>

</header>

<nav>

<ul>

<li
id=”all”>All</li>

<li
id=”web”>Web</li>

<li
id=”print”>Print</li>

<li
id=”illustration”>Illustration</li>

<li
id=”logo”>Logo</li>

</ul>

</nav>

<section
class=”work”>

</section>

<footer>&copy; 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
href=”#”>

<img
src=”images/1.png”
alt=””
/>

<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
href=”#”>

<img
src=”images/1.png”
alt=””
/>

<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:

+ expand source

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
solid
lightgray;

box-shadow: 0px
0px
3px
rgba(0, 0, 0, 0.1) inset;

}

::-webkit-scrollbar-thumb {

background:#999;

border: thin
solid
gray;

}

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
solid
#9d2e2c;

}

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
auto;

-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
0;

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
0;

}

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
!important;

box-shadow: 0
0
5px
0
rgba(0, 0, 0, 0.5);

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:

+ expand source

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):

  1. Detect nav > li press.
  2. Scale down all the thumbnails by giving them the .not-current class.
  3. Add the .current-li class to the selected category’s corresponding <li>.
  4. Remove the .not-current class only from the thumbnails of the selected category.
  5. 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();

$(‘#’
+ category).addClass(‘current-li’);

$(‘.’
+ category).removeClass(‘not-current’);

$(‘.’
+ category).addClass(‘current’);

if
(category == “all”) {

$(‘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:

+ expand source

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
and (max-width: 965px) {

.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
and (max-width: 860px) {

.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
and (max-width: 740px) {

.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
and (max-width: 610px) {

.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
and (max-width: 480px) {

.container {

width: 320px;

}

.work figure {

width: 200px;

height: 200px;

margin: 20px
60px;

}

}

Browser Compatibility

The basic scaling functionality (CSS transforms) works perfectly in most major browsers, which include:

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