1. Web Design
  2. HTML/CSS
  3. JavaScript for Designers

Build a Responsive, Filterable Portfolio, with CSS3 Twists

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.
Scroll to top

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:

File Structure

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:

1
2
<!doctype html>
3
<html lang="en">
4
	<head>
5
		<meta http-equiv="content-type" content="text/html; charset=utf-8" />
6
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
7
		<title>John Doe's Portfolio</title>
8
		<link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico">
9
		<link rel="stylesheet" href="css/style.css" media="screen">
10
		<link rel="stylesheet" href="css/media-queries.css" media="screen">
11
		<script src="http://code.jquery.com/jquery-latest.min.js"></script>
12
		<script src="js/js.js"></script>
13
		<!--[if lt IE 9]>

14
			<script src="js/html5shiv.js"></script>

15
		<![endif]-->
16
	</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.
1
2
<body>
3
	<div class="container">
4
		<header>
5
			<h1 class="title">
6
				John Doe
7
			</h1>
8
		</header>
9
		<nav>
10
			<ul>
11
				<li id="all">All</li>
12
				<li id="web">Web</li>
13
				<li id="print">Print</li>
14
				<li id="illustration">Illustration</li>
15
				<li id="logo">Logo</li>
16
			</ul>
17
18
		</nav>
19
		<section class="work">
20
		</section>
21
		<footer>&copy; 2012 John Doe. Valid HTML5.</footer>
22
</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.

1
2
<figure class="illustration">
3
	<a href="#">
4
		<img src="images/1.png" alt="" />
5
		<dl>
6
		</dl>
7
	</a>	
8
</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.

1
2
<figure class="illustration">
3
	<a href="#">
4
		<img src="images/1.png" alt="" />
5
		<dl>
6
			<dt>Client</dt>
7
				<dd>Envato</dd>
8
			<dt>Role</dt>
9
				<dd>Illustration</dd>	
10
		</dl>
11
	</a>	
12
</figure>

Step 6: The Complete HTML

Here’s what our completed HTML markup looks like:

1
2
<!doctype html>
3
4
<html lang="en">
5
6
	<head>
7
		<meta http-equiv="content-type" content="text/html; charset=utf-8" />
8
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
9
		<title>John Doe's Portfolio</title>
10
		<link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico">
11
		<link rel="stylesheet" href="css/style.css" media="screen">
12
		<link rel="stylesheet" href="css/media-queries.css" media="screen">
13
		<script src="http://code.jquery.com/jquery-latest.min.js"></script>
14
		<script src="js/js.js"></script>
15
		<!--[if lt IE 9]>

16
			<script src="js/html5shiv.js"></script>

17
		<![endif]-->
18
19
	</head>
20
21
	<body>
22
23
		<div class="container">
24
25
			<header>
26
27
				<h1 class="title">
28
					John Doe
29
				</h1>
30
31
			</header>
32
33
			<nav>
34
35
				<ul>
36
					<li id="all">All</li>
37
					<li id="web">Web</li>
38
					<li id="print">Print</li>
39
					<li id="illustration">Illustration</li>
40
					<li id="logo">Logo</li>
41
				</ul>
42
43
			</nav>
44
45
			<section class="work">
46
47
				<figure class="illustration">
48
					<a href="#">
49
						<img src="images/1.png" alt="" />
50
51
						<dl>
52
							<dt>Client</dt>
53
								<dd>Envato</dd>
54
							<dt>Role</dt>
55
								<dd>Illustration</dd>	
56
						</dl>
57
					</a>	
58
				</figure>	
59
60
				<figure class="print">
61
					<a href="#">
62
						<img src="images/2.png" alt="" />
63
						<dl>
64
							<dt>Client</dt>
65
								<dd>Envato</dd>
66
							<dt>Role</dt>
67
								<dd>Print</dd>	
68
						</dl>
69
					</a>	
70
				</figure>	
71
72
				<figure class="logo">
73
					<a href="#">
74
						<img src="images/3.png" alt="" />
75
						<dl>
76
							<dt>Client</dt>
77
								<dd>Envato</dd>
78
							<dt>Role</dt>
79
								<dd>Logo</dd>	
80
						</dl>
81
					</a>	
82
				</figure>	
83
84
				<figure class="web">
85
					<a href="#">
86
						<img src="images/4.png" alt="" />
87
						<dl>
88
							<dt>Client</dt>
89
								<dd>Envato</dd>
90
							<dt>Role</dt>
91
								<dd>Web</dd>	
92
						</dl>
93
					</a>	
94
				</figure>	
95
96
				<figure class="print">
97
					<a href="#">
98
						<img src="images/5.png" alt="" />
99
						<dl>
100
							<dt>Client</dt>
101
								<dd>Envato</dd>
102
							<dt>Role</dt>
103
								<dd>Print</dd>	
104
						</dl>
105
					</a>	
106
				</figure>	
107
108
				<figure class="web">
109
					<a href="#">
110
						<img src="images/6.png" alt="" />
111
						<dl>
112
							<dt>Client</dt>
113
								<dd>Envato</dd>
114
							<dt>Role</dt>
115
								<dd>Web</dd>	
116
						</dl>
117
					</a>	
118
				</figure>	
119
120
				<figure class="print">
121
					<a href="#">
122
						<img src="images/7.png" alt="" />
123
						<dl>
124
							<dt>Client</dt>
125
								<dd>Envato</dd>
126
							<dt>Role</dt>
127
								<dd>Print</dd>	
128
						</dl>
129
					</a>	
130
				</figure>	
131
132
				<figure class="web">
133
					<a href="#">
134
						<img src="images/8.png" alt="" />
135
						<dl>
136
							<dt>Client</dt>
137
								<dd>Envato</dd>
138
							<dt>Role</dt>
139
								<dd>Web</dd>	
140
						</dl>
141
					</a>	
142
				</figure>	
143
144
				<figure class="illustration">
145
					<a href="#">
146
						<img src="images/9.png" alt="" />
147
						<dl>
148
							<dt>Client</dt>
149
								<dd>Envato</dd>
150
							<dt>Role</dt>
151
								<dd>Illustration</dd>	
152
						</dl>
153
					</a>	
154
				</figure>	
155
156
				<figure class="print">
157
					<a href="#">
158
						<img src="images/10.png" alt="" />
159
						<dl>
160
							<dt>Client</dt>
161
								<dd>Envato</dd>
162
							<dt>Role</dt>
163
								<dd>Print</dd>	
164
						</dl>
165
					</a>	
166
				</figure>		
167
168
				<figure class="web">
169
					<a href="#">
170
						<img src="images/11.png" alt="" />
171
						<dl>
172
							<dt>Client</dt>
173
								<dd>Envato</dd>
174
							<dt>Role</dt>
175
								<dd>Web</dd>	
176
						</dl>
177
					</a>	
178
				</figure>	
179
180
				<figure class="logo">
181
					<a href="#">
182
						<img src="images/12.png" alt="" />
183
						<dl>
184
							<dt>Client</dt>
185
								<dd>Envato</dd>
186
							<dt>Role</dt>
187
								<dd>Logo</dd>	
188
						</dl>
189
					</a>	
190
				</figure>	
191
192
				<figure class="illustration">
193
					<a href="#">
194
						<img src="images/13.png" alt="" />
195
						<dl>
196
							<dt>Client</dt>
197
								<dd>Envato</dd>
198
							<dt>Role</dt>
199
								<dd>Illustration</dd>	
200
						</dl>
201
					</a>	
202
				</figure>	
203
204
				<figure class="web">
205
					<a href="#">
206
						<img src="images/14.png" alt="" />
207
						<dl>
208
							<dt>Client</dt>
209
								<dd>Envato</dd>
210
							<dt>Role</dt>
211
								<dd>Web</dd>	
212
						</dl>
213
					</a>	
214
				</figure>	
215
216
				<figure class="logo">
217
					<a href="#">
218
						<img src="images/15.png" alt="" />
219
						<dl>
220
							<dt>Client</dt>
221
								<dd>Envato</dd>
222
							<dt>Role</dt>
223
								<dd>Logo</dd>	
224
						</dl>
225
					</a>	
226
				</figure>	
227
228
				<figure class="print">
229
					<a href="#">
230
						<img src="images/16.png" alt="" />
231
						<dl>
232
							<dt>Client</dt>
233
								<dd>Envato</dd>
234
							<dt>Role</dt>
235
								<dd>Print</dd>	
236
						</dl>
237
					</a>	
238
				</figure>	
239
240
			</section>
241
			
242
			<footer>&copy; 2012 John Doe. Valid HTML5.</footer>
243
		
244
		</div>
245
	
246
	</body>
247
248
</html>

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.

1
2
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,300);
3
@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow);
4
5
::selection {
6
	background: #333;
7
	color: #FFF;
8
}
9
10
::-webkit-scrollbar {
11
	width: 9px;
12
}
13
14
::-webkit-scrollbar-track {
15
	background:#eee;
16
	border: thin solid lightgray;
17
	box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1) inset;
18
}
19
20
::-webkit-scrollbar-thumb {
21
	background:#999;
22
	border: thin solid gray;
23
}

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...
1
2
body {
3
	font-family: 'Open Sans', sans-serif;
4
	background: url('../images/bg.gif');
5
	padding: 0;
6
	margin: 0;
7
	border-top: 10px solid #9d2e2c;
8
}

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);’.

1
2
.container {
3
	width: 960px;
4
	margin: 10px auto;
5
	-webkit-transform: translateZ(0);
6
}

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.

1
2
header {
3
	text-align: center;
4
	font-weight: 300;
5
	font-size: 700%;
6
}

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’.

1
2
footer {
3
	text-align: center;
4
	height: 100px;
5
	line-height: 100px;
6
	color: gray;
7
	clear: both;
8
}

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.

1
2
nav ul {
3
	list-style: none;
4
	padding: 0;
5
	margin: 50px 0;
6
	text-align: center;
7
}

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.

1
2
nav ul li {
3
	display: inline;
4
	cursor: pointer;
5
	margin-right: 10px;
6
	color: #666;
7
	
8
	transition: 0.3s;
9
	-webkit-transition: 0.3s;
10
	-moz-transition: 0.3s;
11
	-o-transition: 0.3s;
12
	-ms-transition: 0.3s;
13
}
14
15
nav ul li:hover {
16
	color: #000;
17
}

Since the last <li> is, umm, the last-child, it doesn’t need any right margin. So, we’ll remove it.

1
2
nav ul li:last-child {
3
	margin-right: 0;
4
}

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.

1
2
nav ul li:after {
3
	margin-left: 10px;
4
	content: '/';
5
	color: #bbb;
6
}
7
8
nav ul li:hover:after {
9
	color: #bbb;
10
}

Again, we’ll have to remove the slash from the last <li>.

1
2
nav ul li:last-child:after {
3
	content: '';
4
}

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.

1
2
.work {
3
	margin: 20px 0;
4
}

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-shadow which will also work as our border. We’ll also add a small transition time for our boxes to grow and scale down.

1
2
.work figure {
3
	float: left;
4
	margin: 20px;
5
	width: 200px;
6
	height: 200px;
7
	background: #9d2e2c;
8
	line-height: 200px;
9
	-webkit-filter: sepia(0.4);
10
	position: relative;
11
	padding: 0 !important;
12
	
13
	box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.5);	
14
15
	transition: 0.6s;
16
	-webkit-transition: 0.6s;
17
	-moz-transition: 0.6s;
18
	-o-transition: 0.6s;
19
	-ms-transition: 0.6s;
20
}

We’ll ensure that our image always fits the thumbnail by setting its height and width to 100%.

1
2
.work figure a img {
3
	height: 100%;
4
	width: 100%;
5
}

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, and left — 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 on figure:hover.

1
2
.work figure dl {
3
	opacity: 0;
4
	position: absolute;
5
	left: 0;
6
	right: 0;
7
	bottom: 0;
8
	top: 0;
9
	padding: 20px;
10
	margin: 0;
11
	line-height: 2.5; 
12
	background: rgba(0, 0, 0, 0.8);
13
	color: white;
14
	
15
	transition: 0.6s;
16
	-webkit-transition: 0.6s;
17
	-moz-transition: 0.6s;
18
	-o-transition: 0.6s;
19
	-ms-transition: 0.6s;
20
}

As we want it to appear on hovering on the thumbnail, we’ll set its opacity to 1 on figure:hover.

1
2
.work figure:hover dl {
3
	opacity: 1;
4
}

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.

1
2
.work figure dl dt {
3
	text-transform: uppercase;
4
	font-family: 'PT Sans Narrow';
5
	font-size: 12px;
6
	margin-bottom: -16px;
7
}

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

1
2
.work figure dl dd {
3
	margin-left: 0;
4
}

Step 15: CSScurrent’/‘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)’.

1
2
.current {
3
	-webkit-filter: sepia(0) !important;
4
5
	-webkit-transform: scale(1.05);
6
	-moz-transform: scale(1.05);
7
	-o-transform: scale(1.05);
8
	-ms-transform: scale(1.05);
9
	transform: scale(1.05);
10
11
	-webkit-backface-visibility: hidden;
12
	-moz-backface-visibility: hidden;
13
	-o-backface-visibility: hidden;
14
	-ms-backface-visibility: hidden;
15
	backface-visibility: hidden;
16
}

.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)’.

1
2
.not-current {
3
	-webkit-transform: scale(0.75);	
4
	-moz-transform: scale(0.75);
5
	-o-transform: scale(0.75);
6
	-ms-transform: scale(0.75);
7
	transform: scale(0.75);
8
9
	-webkit-filter: grayscale(1) !important;
10
}

.current-li

We’ll simply set the text color of the ‘.current-li’s to black.

1
2
.current-li {
3
	color: #000;
4
}

Step 16: The Complete CSS

Here’s what our completed CSS looks like:

1
2
/* Style */
3
4
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,300);
5
@import url(http://fonts.googleapis.com/css?family=PT+Sans+Narrow);
6
7
::selection {
8
	background: #333;
9
	color: #FFF;
10
}
11
12
::-webkit-scrollbar {
13
	width: 9px;
14
}
15
16
::-webkit-scrollbar-track {
17
	background:#eee;
18
	border: thin solid lightgray;
19
	box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1) inset;
20
}
21
22
::-webkit-scrollbar-thumb {
23
	background:#999;
24
	border: thin solid gray;
25
}
26
27
/* --------------------------

28
	Body

29
----------------------------- */
30
31
body {
32
	font-family: 'Open Sans', sans-serif;
33
	background: url('../images/bg.gif');
34
	padding: 0;
35
	margin: 0;
36
	border-top: 10px solid #9d2e2c;
37
}
38
39
/* --------------------------

40
	Container

41
----------------------------- */
42
43
.container {
44
	width: 960px;
45
	margin: 10px auto;
46
	-webkit-transform: translateZ(0);
47
}
48
49
a {
50
	text-decoration: none;
51
}
52
53
/* --------------------------

54
	Header

55
----------------------------- */
56
57
header {
58
	text-align: center;
59
	font-weight: 300;
60
	font-size: 700%;
61
}
62
63
/* --------------------------

64
	Footer

65
----------------------------- */
66
67
footer {
68
	text-align: center;
69
	height: 100px;
70
	line-height: 100px;
71
	color: gray;
72
	clear: both;
73
}
74
75
/* --------------------------

76
	Navigation

77
----------------------------- */
78
79
nav ul {
80
	list-style: none;
81
	padding: 0;
82
	margin: 50px 0;
83
	text-align: center;
84
}
85
86
nav ul li {
87
	display: inline;
88
	cursor: pointer;
89
	margin-right: 10px;
90
	color: #666;
91
92
	transition: 0.3s;
93
	-webkit-transition: 0.3s;
94
	-moz-transition: 0.3s;
95
	-o-transition: 0.3s;
96
	-ms-transition: 0.3s;
97
}
98
99
nav ul li:last-child {
100
	margin-right: 0;
101
}
102
103
nav ul li:hover {
104
	color: #000;
105
}
106
107
nav ul li:hover:after {
108
	color: #bbb;
109
}
110
111
nav ul li:after {
112
	margin-left: 10px;
113
	content: '/';
114
	color: #bbb;
115
}
116
117
nav ul li:last-child:after {
118
	content: '';
119
}
120
121
/* --------------------------

122
	Main Image Box

123
----------------------------- */
124
125
.work {
126
	margin: 20px 0;
127
}
128
129
.work figure {
130
	float: left;
131
	margin: 20px;
132
	width: 200px;
133
	height: 200px;
134
	background: #9d2e2c;
135
	line-height: 200px;
136
	-webkit-filter: sepia(0.4);
137
	position: relative;
138
	padding: 0 !important;
139
	
140
	box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.5);	
141
142
	transition: 0.6s;
143
	-webkit-transition: 0.6s;
144
	-moz-transition: 0.6s;
145
	-o-transition: 0.6s;
146
	-ms-transition: 0.6s;
147
}
148
149
.work figure a img {
150
	height: 100%;
151
	width: 100%;
152
}
153
154
.work figure dl {
155
	opacity: 0;
156
	position: absolute;
157
	left: 0;
158
	right: 0;
159
	bottom: 0;
160
	top: 0;
161
	padding: 20px;
162
	margin: 0;
163
	line-height: 2.5; 
164
	background: rgba(0, 0, 0, 0.8);
165
	color: white;
166
167
	transition: 0.6s;
168
	-webkit-transition: 0.6s;
169
	-moz-transition: 0.6s;
170
	-o-transition: 0.6s;
171
	-ms-transition: 0.6s;
172
}
173
174
.work figure:hover dl {
175
	opacity: 1;
176
}
177
178
.work figure dl dt {
179
	text-transform: uppercase;
180
	font-family: 'PT Sans Narrow';
181
	font-size: 12px;
182
	margin-bottom: -16px;
183
}
184
185
.work figure dl dd {
186
	margin-left: 0;
187
}
188
189
.current-li {
190
	color: #000;
191
}
192
193
.current {
194
	-webkit-filter: sepia(0) !important;
195
196
	-webkit-transform: scale(1.05);
197
	-moz-transform: scale(1.05);
198
	-o-transform: scale(1.05);
199
	-ms-transform: scale(1.05);
200
	transform: scale(1.05);
201
202
	-webkit-backface-visibility: hidden;
203
	-moz-backface-visibility: hidden;
204
	-o-backface-visibility: hidden;
205
	-ms-backface-visibility: hidden;
206
	backface-visibility: hidden;
207
}
208
209
.not-current {
210
	-webkit-transform: scale(0.75);	
211
	-moz-transform: scale(0.75);
212
	-o-transform: scale(0.75);
213
	-ms-transform: scale(0.75);
214
	transform: scale(0.75);
215
216
	-webkit-filter: grayscale(1) !important;
217
}
218
219
.not-current:hover dl {
220
	opacity: 0 !important;
221
}

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 the show(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.

1
2
function scaleDown() {
3
4
	$('.work > figure').removeClass('current').addClass('not-current');
5
	$('nav > ul > li').removeClass('current-li');
6
7
}

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’.

1
2
function show(category) {
3
4
	scaleDown();
5
6
	$('#' + category).addClass('current-li');
7
	$('.' + category).removeClass('not-current');
8
	$('.' + category).addClass('current');
9
10
	if (category == "all") {
11
		$('nav > ul > li').removeClass('current-li');
12
		$('#all').addClass('current-li');
13
		$('.work > figure').removeClass('current, not-current');
14
	}
15
16
}

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.

1
2
$(document).ready(function(){
3
4
	$('#all').addClass('current-li');
5
6
	$("nav > ul > li").click(function(){
7
		show(this.id);
8
	});
9
10
});

This completes our Javascript.


Step 21: The Complete JS

Our completed JS looks like this:

1
2
function scaleDown() {
3
4
	$('.work > figure').removeClass('current').addClass('not-current');
5
	$('nav > ul > li').removeClass('current-li');
6
7
}
8
9
function show(category) {
10
11
	scaleDown();
12
13
	$('#' + category).addClass('current-li');
14
	$('.' + category).removeClass('not-current');
15
	$('.' + category).addClass('current');
16
17
	if (category == "all") {
18
		$('nav > ul > li').removeClass('current-li');
19
		$('#all').addClass('current-li');
20
		$('.work > figure').removeClass('current, not-current');
21
	}
22
23
}
24
25
$(document).ready(function(){
26
27
	$('#all').addClass('current-li');
28
29
	$("nav > ul > li").click(function(){
30
		show(this.id);
31
	});
32
33
});

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]
1
2
/* Small viewports — Old monitors, netbooks, tablets (landscape), etc. */
3
4
@media only screen and (max-width: 965px) {
5
	.container {
6
		width: 840px;
7
	}
8
	.work figure {
9
		width: 170px;
10
		height: 170px;
11
	}
12
}

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]
1
2
/* Smaller viewports — more tablets, old monitors */
3
4
@media only screen and (max-width: 860px) {
5
	.container {
6
		width: 720px;
7
	}
8
	.work figure {
9
		width: 200px;
10
		height: 200px;
11
	}
12
}

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]
1
2
/* Even smaller viewports — more tablets, etc. */
3
4
@media only screen and (max-width: 740px) {
5
	.container {
6
		width: 600px;
7
	}
8
	.work figure {
9
		width: 160px;
10
		height: 160px;
11
	}
12
	.not-current {
13
		opacity: 0.5;
14
	}
15
}

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]
1
2
/* Mobile phones (Landscape) and Tablets (Portrait) */
3
4
@media only screen and (max-width: 610px) {
5
	.container {
6
		width: 460px;
7
	}
8
	header {
9
		font-size: 400%;
10
	}
11
	nav ul li {
12
	}
13
	.work figure {
14
		margin-left: 40px;
15
		margin-bottom: 60px;
16
	}
17
	.work figure dl {
18
		height: 40px;
19
		top: 200px;
20
		bottom: 0px;
21
	}
22
}

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]
1
2
/* Mobile phones (Portrait) */
3
4
@media only screen and (max-width: 480px) {
5
	.container {
6
		width: 320px;
7
	}
8
9
	.work figure {
10
		width: 200px;
11
		height: 200px;
12
		margin: 20px 60px;
13
	}
14
}


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:

  1. Caption Split Effect (Vertical)
  2. Caption Split Effect (Horizontal)
  3. Diagonal Masked Thumbnail Images

Conclusion

That’s it! We have successfully created a working, filterable, and responsive portfolio. I hope you found this tutorial useful. Thanks for reading!