Creating Scrolling Parallax Effects with CSS

I generally dislike ESPN because they're the McDonalds of sports news but they recently did a piece on Luis Suarez that was eye-catching.  The content itself was great but their use of parallax and imagery was outstanding.  They've employed this technique in a few of their features now and I've been so impressed that I implored my readers to write about how it's done.  The following is a post principally written by Stefan Judis with detail added by myself.  Enjoy!


For quite a long time now websites with the so called "parallax" effect have been really popular. In case you have not heard of this effect, it basically includes different layers of images that are moving in different directions or with different speed. This leads to a nice optical effect and keeps the attention of the visitor.

In web design, the most common way to achieve this is by simply adding a jQuery plugin to a website. Doing this unfortunately has a few disadvantages. These plugins mostly attach an event handler to the scroll event of the window object. This leads to tons of events being handled via JavaScript (handling the scroll event can easily cause performance issues and should be considered carefully). To move the mentioned layers, background positions of images get calculated and set to the depending elements, which then additionally causes a lot of DOM manipulations.

In short: parallax done with JavaScript can decrease the scrolling performance of a website quite quickly.

background-position: fixed to the Rescue

What only a few people may know, is that this effect can be achieved via CSS, too. Check out the example below:

See the Pen Parallax with background-attachment : fixed by Stefan Judis (@stefanjudis) on CodePen.

To get this parallax effect, background images have to be placed on different elements. These elements additionally need to have defined background-attachment: fixed. By defining background-attachment the behavior and positioning of any background image can be changed.

The initial value of the property is scroll, which basically means that the image position is fixed to its element. There is nothing fancy about that and we all know this behavior. The user scrolls on a website and elements are moving up and down and so do the background images.

It gets interesting by setting the background-attachment to fixed. fixed defines that the background image position is not fixed to the depending element but rather fixed to the viewport. This means that the image will stay visually on the same position no matter how much scrolling will be done. This leads to the nice visual parallax effect.

Let's have a quick check on the actual implementation:

<!-- Four containers for setting the background images -->
<div class="parallax">
  <div class="bg__foo">foo</div>
  <div class="bg__bar">bar</div>
  <div class="bg__baz">baz</div>
  <div class="bg__bazz">bazz</div>
// setting base styles to image containers
[class*="bg__"] {
  height: 50vh;

  text-indent: -9999px;

  /* fix background */
  background-attachment: fixed;

  /* center it */
  background-position: center center;

  /* Scale it nicely to the element */
  background-size: cover;

  /* just make it look a bit better ;) */
  &:nth-child(2n) {
    box-shadow: inset 0 0 1em #111;

.bg__foo {
  background-image: url(

.bg__bar {
  background-image: url(

.bg__baz {
  background-image: url(

.bg__bazz {
  height: 100vh;

  background-image: url(

A quick check regarding browser support on MDN shows almost global compatibility. It is already supported in IE9 and Android 2.1.

Sum up

I personally prefer CSS solutions to JavaScript solutions and this is a perfect example for my preference. There is no logic and no additional DOM manipulation needed, which makes the whole solution pretty nice. But there is still one thing to remember when dealing with parallax effects.

Even with this CSS solution there is a lot of stuff to do for the browser. background-attachment: fixed will lead to much more painting that needs to be done by the browser, which can affect the scrolling performance and maybe drop your FPS (remember the 60FPS goal?). So keeping an eye on the FPS meter e.g. in Chrome when doing these kind of things is always a good idea.

Stefan Judis

About Stefan Judis

Stefan is a frontend developer from Berlin. He maintains the projects grunt-phantomas and grunt-photobox. Improving performance, automating everything, visualizing datasets and drinking good coffee are the things he is doing most of the time.