Jazz up a Static Webpage with Subtle Parallax


It's been one of the biggest web design buzz words for a while now, so today we're going to try our hand at a bit of Parallax. We're going to put together a simple responsive layout, then with the help of skrollr.js, we're going to apply a subtle parallax effect to the header.

Step 1: Our Project

This tutorial will cover a few noteworthy aspects, so pay attention:

  • Firstly, we're going to use a mobile-first fluid adaptation of the skeleton boilerplate.
  • We'll need to grab an image, I'll be using morning coffee from
  • We'll grab a copy of Alexander Prinzhorn's skrollr.js to help with our parallax effect.
  • In order to optimize my CSS I'll be using a Sass workflow. Don't panic! I won't actually be using any Sass syntax to speak of, just compiling all my stylesheets, giving me one tiny minified CSS file. If you don't want to follow that aspect of the tutorial, don't worry, you can use whatever approach you're comfortable with. However, if you haven't yet played with Sass, see this as the perfect chance to get started without actually using Sass (if that makes sense..)

Step 2: Files 'n' Folders

Okay then! Let's kick off our project with some bare bones files. This is what you'll need to begin with:

Our index.html is empty at the moment, then we have an img directory with our coffee image within it, a css directory with a reset file plus our fluid grid and an empty styles.css, patiently awaiting our input. Lastly, we have a js directory with the skrollr.js file downloaded from GitHub.

Step 3: Fleshing Out the Bones

Our index.html is going to begin with some very familiar elements:

<!DOCTYPE html>
	<meta charset="utf-8">

	<!--viewport goodness>
	<meta name="viewport" content="width=device-width, initial-scale=1">

	<title>Meaningless Pap</title>
	<link rel="stylesheet" href="css/styles.css">
	<!--[if lt IE 9]>
		<script src=""></script>

	<!-- Favicons -->
	<link rel="shortcut icon" href="" />
	<link rel="apple-touch-icon" href="" />




I won't bore you with all these bits and pieces, but it is worth noting that the viewport meta tag is present, we've given the document a (nonsensical) title, linked to some favicons - and that we've pointed to just one css file: styles.css.

Step 4: Mashing Our CSS Together

One tiny stylesheet to rule them all.

When I first talked about the files and folders I mentioned three stylesheets; normalize.css (to responsibly reset our styles), fluid_skeleton.css (which I've adapted myself from the skeleton boilerplate) and the actual styles.css. So why have we only linked to one in our HTML file?

Well, we're going to use this as an introduction to Sass - without actually using any Sass.

By using the .scss file extension instead of .css, we can pull them all into one file and minify the whole lot. Begin by renaming the CSS files like so:

  • normalize.css → _normalize.scss
  • fluid_skeleton.css → _fluid_skeleton.scss
  • styles.css → styles.scss

That was easy, right? Notice the underscores on the first two, that prevents those files from being compiled into their own css equivalents. Instead, we're going to import them into our styles.scss, just as we would using a normal css @import rule:

@import "normalize";
@import "fluid_skeleton";

Now when we compile we'll be given a styles.css file built up from all the rules in our .scss files. We only need to deploy this single file to our web server, saving our site two requests and several Kbs. Good job :)

Compiling Sass

In order to actually compile the files as described above, we're going to need a compiling application (unless you fancy using the command line?). There are a few applications which will do the job for you, I use CodeKit (because it's awesome) but CompassApp will serve you just as well, plus it's available on Windows.

Setting up our project is as easy as dragging the project folder into the compiler. CodeKit is now watching all the files within that folder. Whenever I hit "save", no matter which SCSS file I'm editing, CodeKit will recompile all the pieces, churning out our updated CSS and, if our page is open in a browser, refreshing that for us too.

Even if you're not ready to look further into Sass this is an ideal workflow.

Step 5: A Bit More Content

Let's now fill up the index.html file, after which we can really begin on the styling. As mentioned, I'm using fluid_skeleton.css as a basis for the responsive grid. It's a great, uncomplicated basis from which to kick off a simple layout. All it requires is that we use its grid structure to build our layout.

We need:

  • A band div - which will stretch the full width of the screen.
  • Within each of them we need a .container div - which holds all the grid columns.
  • Then .column divs, each one having a second class depending on the width. .eight for example, totalling sixteen all together.

For example, we can build a quick four column layout like so:

<div class="band">

	<div class="container">
		<div class="column four"></div>
		<div class="column four"></div>
		<div class="column four"></div>
		<div class="column four"></div>


The stylesheet dictates that these divs are all 100% wide, stacked neatly ontop of one another, until the viewport hits 767px wide. At that point everything shifts and these four columns will spread across the screen as nature intended.

This quick wireframe should give you an idea of how we're going to layout our structural markup.

And if you're after a quick copy/paste, this will help you out:

<header class="band">

	<div class="container">

		<div class="columns sixteen">
		</div><!--/ columns-->	
	</div><!--/ container-->		

</header><!--/ band-->

<div class="band main">

	<div class="container">
		<div class="columns twelve offset-by-two">		    
		</div><!--/ column-->

	</div><!--/ container-->

</div><!--/ main-->

<div class="band main">

	<div class="container">
		<div class="columns six offset-by-two">	
		</div><!--/ column-->
		<div class="columns six">
		</div><!--/ column-->

	</div><!--/ container-->

</div><!--/ main-->

<footer class="band main">


	<div class="container">
		<div class="columns eight">		
		</div><!--/ column-->
		<div class="columns eight last">
		</div><!--/ column-->

	</div><!--/ container-->

</footer><!--/ main-->

For more information on how Skeleton is put together take a look at Building a Responsive Layout With Skeleton: Starting Out.

Step 6: Filler Text

It's always easier to see what you're doing when you have a bit of dummy text to work with, so throw a heading and a link into the header region at the top:

<h1>Meaningless Pap</h1>

<a class="button" href="">by Ian Yates</a>

Then turn your attention to the columns and the footer. I've grabbed a load of filler text from T'Lipsum "A Yorkshire Lorem Ipsum Generator for Yorkshire Folks" (because I'm a proud Yorkshireman) beat that!

To give you an idea, the main body (our twelve column area) begins like this:

<h2>Mek 's a Brew Lad</h2>
<p><strong>A pint 'o mild. Tell thi summat for nowt th'art nesh thee chuffin' nora eeh appens as maybe. Ey up is that thine god's own county. Be reet be reet nah then michael palin bloomin' 'eck gerritetten.</strong> Ah'll learn thi soft southern pansy. Any rooad soft southern pansy nobbut a lad mardy bum shu' thi gob face like a slapped arse. Ee by gum mardy bum michael palin. God's own county. Th'art nesh thee. Appens as maybe god's own county where's tha bin.</p>

Stick some links in the footer and we're done here!

Step 7: Kicking Off the Styling

Before we go any further I want to grab some fonts. Head on over to Google Web Fonts and take a look at PT Sans and PT Serif. They're a great duo, so add them both to your collection and gather the stylesheet link.

To save time, this is the link I was given, having selected the 400 and 700 weight versions of each:

@import url(,700|PT+Serif:400,700);

You can paste this underneath our other imports in styles.scss. When compiled, the contents of this CSS file won't actually be dragged into our minified CSS, but the @import rule will remain and the font will be used all the same.

Now I'll set PT Sans as our base font, setting the font-size to 100% of the browser's default, with its line-height at 1.5em:

html {
	font: 100%/1.5em 'PT Sans', sans-serif;

Step 8: Taking Styling Further

Much of the styling we apply to this page is fairly generic. Colors and sizes for the typography, padding on the bands and so on. Pull the source files apart and you'll find the contents of styles.scss self-explanatory.

However, let's focus for a moment on the header area. In its mobile-first state, we want to give it a brown background. Only once the viewport is larger will we bring in the coffee image (to spare mobile users the bandwidth). This is what the styles for the header band look like now: {
	background: #4b371f;
	color: white;
	text-align: center;
	padding: 10% 0 15% 0;

The padding top and bottom has been applied as a percentage of the browser width. This means that the header area grows in height as the browser window expands. It's quite a nice effect and keeps the fold (wherever that is these days) proportional to the browser window size.

Now let's style the heading we have within it.

h1, h2, h3 {
	font-family: 'PT Serif', serif;
	font-weight: 700;
	text-align: center;
} h1 {
	font-size: 3em;
	line-height: 1em;
	margin-bottom: 1em;

We'll use the serif variant of PT for our headings, which pairs well with the body copy being in sans.

Lastly, we'll style our anchor to make it stand out:

a.button {
	background: #e33f0c;
	text-align: center;
	padding: .5em 1em;
	color: white;
	font-weight: bold;
	text-decoration: none;
	box-shadow: 0 0.2em 0 #ab3009;
	text-transform: uppercase;
	letter-spacing: 0.1em;
	/*transition cross-browser stuff*/
	-webkit-transition: background 0.2s ease-out;  /* Safari 3.2+, Chrome */
	-moz-transition: background 0.2s ease-out;  /* Firefox 4-15 */
	-o-transition: background 0.2s ease-out;  /* Opera 10.5–12.00 */
	transition: background 0.2s ease-out;  /* Firefox 16+, Opera 12.50+ */

a.button:hover {
	background: #ab3009;

Straightforward stuff, all em-based to ensure that it can grow and shrink if needs be - and if you're really going to embrace Sass you could make all still styling even slimmer! For now though, this is what you should have:

Step 9: Throw in a Media Query

Having built our basic mobile experience, let's now embellish things for larger screens. How you choose to arrange your media queries is up to you, but as this is a fairly uncomplicated stylesheet I'm just going to pile all my extra rules into one media query at the end of styles.scss.

Let's add some rules which will kick in on screens larger than 767px. This breakpoint is fairly arbitrary and just happened to suit the design when I first built it. What better way to choose a breakpoint?!

@media only screen and (min-width: 767px) { 


There actually aren't many styles to add here, we just want to boost the font-size used throughout the document, make our button a bit more prominent and add a background image to the header area:

@media only screen and (min-width: 767px) { 

	body {
		font-size: 1.125em; /* 18px / 16px */
		line-height: 1.7em;
	} {
		background-size: 100%;
	} h1 {
		font-size: 3.25em; /*52 / 16*/
	a.button {
		padding: .75em 1.5em;

On larger screens we should now have something like..

Step 10: Introducing skrollr.js

Currently, when you scroll down the page our header disappears from view much as you would expect. Let's now get to the basis of this whole exercise; parallax. You should already have downloaded skrollr.js from GitHub and placed it in the js folder.

Next, we need to reference it in our html, so place this link at the bottom, before the closing </body> tag:

<script type="text/javascript" src="js/skrollr.js"></script>

This is a vanilla JavaScript plaything, so we've no need to pull in jQuery beforehand. Next, we need to call skrollr, which we do like so:

<script type="text/javascript" src="js/skrollr.js"></script>
<script type="text/javascript">

There are a number of options which we can toy around with, all of which have settings we can throw in between those curly braces, but for now we'll leave things exactly as they are.

Step 11: Altering the Markup

In its current stage of development, skrollr requires us to butcher our markup a little, adding rules inline on our elements. Skrollr's maker Alexander Prinzhorn is busy with a project which separates styles from markup, but it's still in progress.

Anyway, I'm making it sound worse than it is, we're simply required to add our keyframe styling to data attributes on the elements we're targeting. For example, let's move the background image position of our header, depending on the scrollbar position.

We add our first keyframe styling like so:

<header class="band" data-0="background-position:0px 0px;">

Skrollr uses data- and then a value which represents the pixel position of the scrollbar. We then add some styling just as we would with CSS. We've literally just specified that when the scrollbar is at 0px (the top of the page) we want the background-position of this element to be 0px 0px.

We can then add as many other keyframes as we like, so let's throw another data-attribute in, directly after our first.

data-500="background-position:0px -250px;"

This then says; when the scrollbar has moved 500px down, we want the background-position to be 0px -250px (ie: 250px upwards). Skrollr will animate the process between these two keyframes, so we get a smooth transition.

Bingo! Our first parallax effect.

Step 12: A Second Effect

We've acheived a very simple parallax effect which adds an element of dynamism whenever the user is scrolling downwards. We can apply whatever CSS rules we want with skrollr, so let's give our header region one last flourish.

Remember this element, which contains the heading and the link?

<div class="columns sixteen">

We're going to add two keyframes; one for the initial state (0px) and another for when the scrollbar reaches 180px.

<div class="columns sixteen" data-0="opacity: 1" data-180="opacity: 0">

These, as you can see, dictate the opacity, another familiar CSS property. As our scrollbar moves down, before the header has quite left the page, the content within this div will smoothly fade away. A simple effect, but one which subtly influences the user experience of this page. Things like this should be used responsibly!

Step 13: Extra Settings

Skrollr comes packaged up with several extra options, depending on how confident you're feeling. All of these settings can be passed to skrollr by means of key-value pairs like so:

	<script type="text/javascript">
		forceHeight: false

For example, this instruction tells skrollr not to force the height of the document. By default skrollr will force the document to be at least as high as the highest keyframe you've defined. In our case, our furthest scrollbar position was specified as 500px, whilst our page is waaa-aay taller than that. We therefore didn't need to think about overriding the height forcing.

Another thing you may want to do is remove the smooth scrolling. Try our parallax effect once more; you see how the background image position accelerates and decelerates? That prevents any jerkiness, but you might not want the effect to appear that way. To make your tweening more linear, add this:

	<script type="text/javascript">
		smoothScrolling: false

You could also add this per element by once again leaning on the (very elegant) data-attributes:

<div data-smooth-scrolling="off">

Relative Mode

It's also worth mentioning relative mode, which allows us to set keyframes relative to the element, not the document as we have been doing. In this way, we could alter the height, margins etc. of our header (for example) without having to reassess our keyframe settings.

Using relative mode alters the syntax needed within the data-attributes, which I won't go into here, but it's well worth taking a look at.

For more idea of what's possible using skrollr's options, check out the examples and documentation.

Step 14: Mobile Support

Here's something I didn't know until I read the skrollr documentation:

Mobile browsers try to save battery wherever they can. That's why mobile browsers delay the execution of JavaScript while you are scrolling. iOS in particular does this very aggressively and completely stops JavaScript.

Therefore, in order for skrollr to work on mobile platforms, Alexander had to build a specific bit of extra magic which is pulled in after a bit of browser-sniffing. As you've seen, our effect degrades very gracefully, so I'm not bothered about smartphone users missing out on a bit of parallax. However, if you want to make sure your project supports mobile, you'll need an extra something.

  • After you've included the skrollr script, you'll need to run a wee browser check, which looks like this:
    	<script type="text/javascript">
    	// + tablets
    	(function(a) {
    		if(/android|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(ad|hone|od)|iris|kindle|lge |maemo|meego.+mobile|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))
    			//Add skrollr mobile on mobile devices.
    			document.write('<script type="text/javascript" src="js/"><\/script>');

    You'll notice that this then adds if the conditions are met (ie: a mobile device is being used), just make sure the path is correct as per your project. You'll find this extra script in the source download on GitHub.

  • Lastly, make sure the first element after the opening <body> tag has id="skrollr-body".

That's it! You don't need to understand why that works, it just does. If you're curious to learn more about mobile support, take a look at the documentation.


Skrollr isn't just a parallax..thing, it does much more than that as we've shown. With any luck though, you'll now have been able to implement a bit of razzmatazz (whatever that is) to your own static web pages. Feel free to ask any questions you may have in the comments!

Further Resources

Related Posts