Howdy Folks, Welcome to this another wonderful tutorial on creating a simple yet beautiful parallax scrolling effect using pure CSS only. No Javascript libraries used ;)

Before jumping right in, first, take a look at the finished product at Codepen--> https://codepen.io/thecodingpie/pen/JjGJMMB

You can get the full source code at the end. Or you can just download the completed project from GitHub --> https://github.com/the-coding-pie/Parallax-Effect

The parallax effect does not always work on mobile devices. So if you are using a mobile device to see the finished demo, then some of you may encounter some difficulties seeing the demo at its full glory.

Parallax Scrolling Effect

Parallax scrolling effect is an illusion where the background content (eg: an image) is moved at a different speed than the foreground content while scrolling. 

That's the theory part. We are going to achieve this effect using only a few CSS styles namely perspective, transform-style, transform, and z-index. If you wonder what they are, don't worry at all you will understand everything by the end of this tutorial. Trust me it's so simple :)

Let's Build it :)

This is my file structure:

file structure

  • First, create a folder named "Parallax Effect". This is our root folder that holds everything. Then open it inside any text editor you like. Here I am going to use visual studio code.
  • Then create an "index.html" file.
  • Then create two folders:
    • One named "css" --> Inside this folder create a file named "styles.css"
    • Then another folder named "img" --> This holds our image. You can grab the image I used from here. After downloading it, rename the image to "bg" then put it inside "img" folder you created.

That's it. Now you should have a folder structure similar to above. Now the HTML part...

HTML

This is the structure of our "index.html" file:

parallax layout

And this is the code. Copy/Type this inside "index.html":

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="css/styles.css">
  <title>Parallax</title>
</head>
<body>

  <header>
    <h1>Life Is Beautiful :)</h1>
  </header>
  <section class="paragraph">
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas omnis accusamus unde deserunt. Sapiente laboriosam sequi cupiditate sint debitis veritatis exercitationem dignissimos est enim molestias accusamus, suscipit nostrum asperiores minus!
    Eligendi ab fugiat dignissimos ipsam minima necessitatibus provident repellendus neque odit numquam aliquam expedita suscipit doloribus nesciunt facilis, molestiae quibusdam! Odit excepturi ab iure, aspernatur nemo aut repudiandae? Doloribus, earum?
    Eius blanditiis maxime impedit harum quasi pariatur atque beatae omnis expedita dolorum, vero explicabo numquam mollitia libero ut ratione, sit, amet assumenda voluptatem deserunt magnam rem facilis. Dolor, porro non?
    Tempore atque fugit quaerat neque esse? Unde doloremque odit distinctio mollitia quae quasi itaque atque dolor voluptatibus eligendi nemo labore repudiandae, sit maiores at eius. Nulla ipsa voluptatibus laboriosam culpa!
    Iusto ipsum aliquam obcaecati a ullam fuga dolorem dolores, eius, beatae corporis repudiandae quibusdam ab natus? Voluptates unde quaerat quas, placeat, blanditiis hic totam harum tempore assumenda ullam voluptatibus quod.
  </section>

</body>
</html>

Inside the <body>, we have a <header> and a <section class="paragraph">. Inisde <header>, there is an <h1> element. And inside <section class="paragraph">, we have some dummy "lorem ipsum" text. 

Open it with "Live Server" and take a look at it in the browser.

Now comes the most important part, the CSS part... 

CSS

Open the "styles.css" file and type the following step by step. Here I am going to follow the style like code first then explain it then again give code then explain it and so on and so forth:

* {
  padding: 0;
  margin: 0;
}

The above code removes the default padding and margin given by the browser.

/* this style for "html" is must, otherwise nothing will work */

html {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

This code is a must. Otherwise, nothing will work. Here we are grabbing the html element and set its width: 100% and height: 100% and overflow: hidden.

body {
  width: 100%;
  height: 100%;
  perspective: 1px;
  transform-style: preserve-3d;
  overflow-x: hidden;
  overflow-y: scroll;
  font-family: sans-serif;
  color: #fff;
}

Now we are targeting the body element and set its height: 100% and width: 100%

Then we are setting the perspective: 1px. The perspective property is used to give a 3D-positioned element some perspective. This property gives us the 3D depth we want. This defines how far the object is away from the user. So a lower value will result in a more intensive 3D effect. One more important thing is that when giving this property to an element, it is the Child elements that get the perspective view, not the element itself.

Then the transform-style: preserve-3d. The transform-style property sets whether children of an element are positioned in the 3D space or are flattened in the plane of the element. This property allows the element to sit inside the 3D space we created. This property is not inherited, so it must be set manually for the child elements.

Then we are setting overflow-x: hidden and overflow-y: scroll and some other basic settings.

header {
  width: 100vw;
  min-height: 100vh;
  position: relative;
  transform-style: preserve-3d;
}

Here, we are selecting the header element and setting its width and height to 100vw and 100vh. "vw" stands for view-width and "vh" stands for view-height. It is the width and height of the device screen.

Then we are setting position: relative. Because we are going to absolutely position everything in relative to our header element. If you want to learn more about positioning, here is a short video by Red Stapler --> https://www.youtube.com/watch?v=3PDQDRJq5Ls

As I told earlier, we need to manually set transform-style: preserve-3d to all the children we want. That's what we are doing in the last line.

header h1 {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

To position the h1 in the exact middle of the header tag, we set position: absolute, then push it from top: 50% and left: 50%. Then give transform: translate(-50%, -50%).

/* placing the actual background image using pseudo selector */
header::before {
  content: "";
  min-height: 100vh;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background-image: url(../img/bg.jpg);
  background-size: cover;
  transform: translateZ(-1px) scale(2);
  z-index: -1;
}

Now here is where we set our background image. We use the ::before pseudo-selector to do this. First we set content: "", then set min-height: 100vh, then set position: absolute and set top: 0, left: 0, right: 0. This positions the image top-left. 

Now give the image through the background-image property. Then set the background-size to cover.

Then push the image along the z-axis -1px through transform: translateZ(-1px). When we do so, the image will be pushed away from the user. So its size will decrease. So we need to scale it up. That's what we are doing on the second-last line. The scale value will depend on the values of perspective and the translateZ. Finally, set the z-index: -1, so that the background image will be less important than the forward content.

.paragraph {
  position: absolute;
  top: 100vh;
  background-color: #111;
  line-height: 2;
  padding: 70px;
  font-size: 1.2rem;
}

This final piece of code is for styling our <section class="paragraph"> and the content inside it. This is pretty self-explanatory and this is also less important too. 

That's it! Now if you take a look at the finished web page in the web browser, you can see the parallax effect at its full glory :)

Here is the final code:

The code for "index.html":

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="css/styles.css">
  <title>Parallax</title>
</head>
<body>

  <header>
    <h1>Life Is Beautiful :)</h1>
  </header>
  <section class="paragraph">
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas omnis accusamus unde deserunt. Sapiente laboriosam sequi cupiditate sint debitis veritatis exercitationem dignissimos est enim molestias accusamus, suscipit nostrum asperiores minus!
    Eligendi ab fugiat dignissimos ipsam minima necessitatibus provident repellendus neque odit numquam aliquam expedita suscipit doloribus nesciunt facilis, molestiae quibusdam! Odit excepturi ab iure, aspernatur nemo aut repudiandae? Doloribus, earum?
    Eius blanditiis maxime impedit harum quasi pariatur atque beatae omnis expedita dolorum, vero explicabo numquam mollitia libero ut ratione, sit, amet assumenda voluptatem deserunt magnam rem facilis. Dolor, porro non?
    Tempore atque fugit quaerat neque esse? Unde doloremque odit distinctio mollitia quae quasi itaque atque dolor voluptatibus eligendi nemo labore repudiandae, sit maiores at eius. Nulla ipsa voluptatibus laboriosam culpa!
    Iusto ipsum aliquam obcaecati a ullam fuga dolorem dolores, eius, beatae corporis repudiandae quibusdam ab natus? Voluptates unde quaerat quas, placeat, blanditiis hic totam harum tempore assumenda ullam voluptatibus quod.
  </section>

</body>
</html>

Code for "styles.css":

* {
  padding: 0;
  margin: 0;
}

/* this style for "html" is must, otherwise nothing will work */

html {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

body {
  width: 100%;
  height: 100%;
  perspective: 1px;
  transform-style: preserve-3d;
  overflow-x: hidden;
  overflow-y: scroll;
  font-family: sans-serif;
  color: #fff;
}

header {
  width: 100vw;
  min-height: 100vh;
  position: relative;
  transform-style: preserve-3d;
}

header h1 {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* placing the actual background image using pseudo selector */
header::before {
  content: "";
  min-height: 100vh;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background-image: url(../img/bg.jpg);
  background-size: cover;
  transform: translateZ(-1px) scale(2);
  z-index: -1;
}

.paragraph {
  position: absolute;
  top: 100vh;
  background-color: #111;
  line-height: 2;
  padding: 70px;
  font-size: 1.2rem;
}

Conclusion

I hope you enjoyed this tutorial. If you had any doubts, then please comment them below. Thank you ;)