How to Integrate Simple Parallax with Twitter Bootstrap

Thanks to the flexibility of Twitter Bootstrap, you’re not in any way limited only to the framework’s default container and grid styles. It can actually be quite easy to introduce new layout options to your site without needing to worry about disrupting Bootstrap’s functionality. With this tutorial, we’ll learn how to do that by integrating a simple scrolling parallax effect to the page.

View Demo | Download Source Files

What makes parallax work visually is a difference in scrolling speeds between elements. To start off, we’ll take advantage of HTML5’s section tag + data attribute to easily distinguish what should be affected from a syntax perspective.

<!-- Section 1 -->
<section id="intro" data-speed="6" data-type="background">
	<div class="container">
		Content goes here!
<!-- Section 2 -->
<section id="home" data-speed="4" data-type="background">
	<div class="container">
		More content goes here!
<!-- Section 3 -->
<section id="about" data-speed="2" data-type="background">
	<div class="container">
		This is the final section!

CSS will be used to define the 3 sections’ dimensions and placement. I preferred to set an automatic height and a top/bottom padding of 100px. This ensures the sections will resize to accomodate any changes in content length. Note the fixed backgrounds, as well. You’ll want to use images that either tile or are large enough fill a section’s background as it scrolls.

#intro { 
	background: url(images/intro.png) 50% 0 fixed; 
	height: auto;  
	margin: 0 auto; 
	width: 100%; 
	position: relative; 
	box-shadow: 0 0 50px rgba(0,0,0,0.8);
	padding: 100px 0;
#home { 
	background: url(images/home.jpg) 50% 0 fixed; 
	height: auto;  
	margin: 0 auto; 
	width: 100%; 
	position: relative; 
	box-shadow: 0 0 50px rgba(0,0,0,0.8);
	padding: 200px 0;
#about { 
	background: url(images/about.png) 50% 0 fixed; 
	height: auto;
	margin: 0 auto; 
	width: 100%; 
	position: relative; 
	box-shadow: 0 0 50px rgba(0,0,0,0.8);
	padding: 100px 0;
	color: #fff;

Finally, here’s the JavaScript to make all of this work (be sure to include jQuery, too). We’ve used the data-speed attribute from earlier as a dynamic part of the JS equation to determine each section’s scroll speed relative to its position within the viewport.

   // cache the window object
   $window = $(window);

     // declare the variable to affect the defined data-type
     var $scroll = $(this);
      $(window).scroll(function() {
		// HTML5 proves useful for helping with creating JS functions!
		// also, negative value because we're scrolling upwards								
		var yPos = -($window.scrollTop() / $'speed')); 
		// background position
		var coords = '50% '+ yPos + 'px';

		// move the background
		$scroll.css({ backgroundPosition: coords });	
      }); // end window scroll
   });	// end section function
}); // close out script

But let’s not forget IE, whose earlier versions might not want to play nice with HTML5’s new tags. Add this line to your initialization to help remedy:


That’s all! Try out the demo for yourself and download the source files to experiment with different speeds and section sizes.

View Demo | Download Source Files

About Michael Milstead

Michael is a front-end developer who has enjoyed building websites for the past seven years.


  • Andreas Brix says:

    Hi Michael.

    Is there any other datatypes i can use, if i want to add the parallax effect to another element, and not a background?

    Best regards.

    • Certainly, although it’s good to keep in mind that the data-* attribute is merely used as a selector that is easily remembered, similar to defining a variable in JavaScript. So, as long as your script reflects it, something like data-watermelon=”background” would work just as well, too.

      Scrolling individual elements is a bit more complex, but definitely doable. Let’s say we have a div of text. You can define it with data-type=”quote” or whatever you wish. Since it contains inline elements, your JavaScript can’t use {backgroundPosition: coords} to move it. Instead, giving it a fixed position in CSS and using {top: coords} in your JS will probably work. If the element is further down the page, you will need to apply a vertical offset to the element’s yPos to accommodate the custom scroll speed (otherwise the browser won’t place the text anywhere near where you’d expect it). This can be done directly on the element with (for example) a data-yOffset=”1500″. Then in the script, call this attribute in the place of yPos with the same sort of syntax that references the scroll speed in the previous line.

      This is of course all presented without a working demo, but it should help point you in the right direction if you’re stuck on the JS end of things. I know that was a lot, so hopefully it all made sense!


  • Danielle says:

    Hi great post exactly what i’ve been looking for. My only issue is I can’t get the background to be set to full width?

    • It’s difficult to say for sure without seeing your website, but this is the first thing I’d try:

      background-size: cover;

      Being a CSS3-only feature, you’ll want to also include these browser conditions for better compatibility:

      -webkit-background-size: cover;
      -moz-background-size: cover;
      -o-background-size: cover;

  • Andreas Brix says:

    Hi Danielle.

    Do you have a link to your website? It would be a lot easier to help you, with a link.


  • Brad says:


    Thanks a lot for this great tutorial/code! Liked it so much, I decided to make a WordPress theme out of it. Check it out!

  • Julien says:

    Hello Michael,

    Thank you very much for this tutorial, it’s awesome. Now, I only seem to have one little problem. I’m trying to add an item that can collapse on itself, but it does not see to be working (I followed the tutorial here : When I’m trying the code from this page on a virgin bootstrap page, it works. But it does not in the index.html you provide.

    Do you have an idea of what could be wrong?

    Thank you very much!

    • Julien says:

      Finally found an answer šŸ™‚

      The version of the included jquery if 1.6.1 but it needs to be 1.7+.

      Replaced the 1.6.1 file with the 1.10.1 and now everything works like a charm!

  • harp says:

    Excellent work! I have one question: the demo page/file/code is responsive in the browser, and I would like for it not to be. Or, rather I wish to define the details of its responsiveness myself. In specific – the main body container becomes inset and has a bitt of shadowing – first on the top, then as you go smaller on the sides as well. Any advice as to over-riding this is welcome! Thank you.

  • J.F. says:


    This is a great article, and I’ve been looking to learn this for a few weeks now. One question, if you don’t mind, what do you recommend to be the best size for the individual background images?



    • J.F. says:

      Hi again,

      I looked a little deeper and, I am still trying to figure out how to use my images and make them look proper. I made one into the same sized .jpg as “home.jpg” and I refreshed; for some reason it is really zoomed into my image and only shows the upper right hand corner. Any thoughts?

      Thanks again for a very helpful article

  • Thomas says:

    Just read a bit about Parallax and was confused about this, so thanks for the clarification. It’s hard to find tutorials as straightforward as this. Thanks for sharing!

  • Mark says:

    Hi Michael,

    Excellent tutorial–I never visit Untame without learning something valuable.

    My problem: I’m basing a single page site loosely on your demo, but with up to five ‘ worth of parallax background images. My problem is that as you scroll down, the images begin shifting farther and farther upward, so that by the time I pass the third image, they’ve shifted so far up out of view that only the bottom appears, along with the background color.

    What should I adjust in the jQuery to accomodate the additional sections and their background images?

  • Henning says:

    Thanks for that old, but great tutorial šŸ˜‰

    @Michael try to replace:
    var yPos = -($window.scrollTop() / $‘speed’));
    var yPos = -(($window.scrollTop() – $scroll.offset().top) / $‘speed’));

    Works for me.

  • alexgl says:

    Great thanks for your code!
    Some blocks needed offset. Add to js-code using data-offset:

    var yPos = -($window.scrollTop() / $‘speed’));
    yPos += $‘offset’));

    And example of use:

Leave a Reply

What is 3 + 2 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)
Start planning your project today. Get Started