Creating A Horizontal Scrolling Web Page. Bonus: With Parallax!
Tags: tutorial, beginners
Published: 2020-10-06 17:32:19

### Introduction

Hi all! I just want to say, before you delve in, that this really is a hard-and-fast explanation of how this is put together. Over the coming days I'm looking to revisit this with a video tutorial, including a more detailed explanation and a start-to-finish approach of how it's made. In the meantime though, I still wanted to share this in the hope that somebody may find it useful.

I was recently inspired by a portfolio website I discovered for a designer and developer named [Joseph Berry](https://joseph-berry.webflow.io/). His website, aside from being both modern and beautiful, really got me interested in the idea of horizontal scrolling.

Horizontal scrolling has become somewhat of a trend on the web and, in my opinion, it is particularly good at building websites that want to convey a story - this is most likely due to its likeness to the way we scroll through a book.  I also think it’s a nice surprise for a user when they expect to scroll down but, instead, the screen scrolls right. 

Jumping through the code of Mr Berry’s site also got me thinking about something else. Could I do this in CSS alone? I’ve been really intrigued lately by the philosophy of progressive enhancement and seeing what can be done with the browser’s most reliable tools, HTML and CSS. Let’s see what we can come up with.

I knew off the bat there were a number things I probably wouldn’t be able to achieve with CSS alone, but that’s OK. Let’s focus on what we can do. If you’ve had a look at the website, you’ll see that there are two key visual effects - the horizontal scrolling, and the parallax effect. These are what we’ll be looking at here.

This is what we'll be making:

![Gif of the final result](https://dev-to-uploads.s3.amazonaws.com/i/66w0br8zl1kcpnms51ya.gif)

### Let's Dive Right In

We’ll start with the HTML. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uckykylzbs01xghvuw9v.png)

This is all we need in our HTML to achieve what we need. Let's break down what's happening. 

The `<main class="viewport">` element will be home to everything horizontal. Whilst this element isn't strictly necessary, as you could use the `body` tag to the same result, using it will allow us to adjust the horizontal scrolling area down the line and allow us to more easily add other semantic HTML like a `header` and `footer`.

Following this we have our `parallax-parent` element which, unsurprisingly, will be the container for anything we want to apply perspective to. There's a `background-colors` element for our background, and some `parallax-child-containers` which will house our pages/content.


### The CSS

Our CSS isn't quite so straightforward, but it's also nothing mind-bending. The key to this entire setup is essentially one magical, almost limitless, CSS property - the `transform` property.

I've started with some basic style-resets and also, having written this is SCSS, a couple of useful variables. We'll see shortly exactly how these will be used.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/duw37bzhz7wltq6ud4d5.png)

You can, of course, create this is vanilla CSS and simply hard-code these values.

The below code is all that's needed to style our `viewport` element.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vijkwivxlvj6nq1vvomk.png)

This simply ensures that our element takes up 100% of the available screen-space, and doesn't allow for any scrollbars to appear should the content exceed this size - which it will. 

Our parallax-parent element is what will allow us to achieve the cool 3D effect found on many modern websites. You know the one - where one element moves a bit faster than the other, giving the illusion of them being closer and farther away respectively. We do this by applying  `perspective: 3px` to the element and setting it's `perspective-origin`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ogaf1fq5sommwa90e1r7.png)

The default value of `perspective-origin` is actually what you see above (`50% 50% 0`) so there's no need to code this in. I put it there originally so I could play around with the values, so I've left it there in case you want to do the same. 

The `parallax-parent` also doubles up in one extremely important way, leading me to think later that maybe `parallax-parent` wasn't the best naming for this element. It's this element which actually does the thing you came here for. 

Check out line 8 of the code - `transform: rotate(-90deg);`. This element is actually being rotated anti-clockwise so that it's top is it's left, it's left is it's bottom, it's...well, so on and so on. 

We've also set the `transform-origin: top left;` which means that the rotation will happen around the top left corner. Let me explain with a diagram.

This blue rectangle represents our `parallax-parent` - in this case being displayed on something like a desktop screen. In the top left corner you'll notice a red circle. This is our `transform-origin`. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3hfc4qm17h4kwfk2gy3g.png)

So now, when we rotate this counter-clockwise by 90 degrees, or `rotate(-90deg)` as CSS would have it, this is the point at which the rotation will occur.

And what does that look like? Well, on your screen, it will look like your element vanished! Why is that?

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/whetazhrr8tw47nk347k.png)

Well, our element has rotated its way out of the viewport and, therefor, can no longer be seen. This is where our use of `top: $height` (or `top: 100vh` for CSS) comes in to help us out to return the element back into view. You'll also note that our `height` is set as `$width` and our `width` is set as `$height`. This now adjusts our element to fit nicely back into the viewport like it was before, only this time rotated -90 degrees.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/57m5cv4vucm9onv3doh5.png)

This has now become a very important point moving forward, as how we deal with `top`, `bottom`, `left`, and `right` in CSS with our `absolute` positioned elements has been affected...and it can be somewhat confusing at first.

Lastly you'll notice that `::-webkit-scrollbar` has been used to hide the scrollbar in our element. An important note here is that this selector is not a web standard and will have different outcomes on different browsers, so be careful. 


**From the MDN docs:**

> `::-webkit-scrollbar` is only available in Blink- and WebKit-based browsers (e.g., Chrome, Edge, Opera, Safari, all browsers on iOS, and others). 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n6kaa7q8hzycp3h2cry1.png)

Our `background-colors` element incorporates a simple CSS gradient to mimic a colour change as the user scrolls. In the example website I pointed to at the beginning of the post the developer uses JavaScript to change the colour dynamically, most likely with scroll event listeners. As we said we would be using plain S/CSS only, we'll stick with a gradient.

The element has been sized to cover `$width * $pages` (or `100vw * 3 = 300vw`) and can be resized as necessary. Bare in mind, using this approach, if you want each page to have its own colour you will need to adjust the gradient accordingly.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qhnp9gwr8k398ehxy1kg.png)

The `parallax-child-container` element uses much of the same styling with regards to sizing as its parent element, the `parallax-parent`, with the `width` and `height` still being "reversed". Here I've used a SCSS function to iterate over the elements and apply a `top` equivalent to the  `nth-of-type - 1 * $width`.  Essentially this gives the first element a `top: 0`, the second a `top: 100vw`, the third a `top: 200vw`, and so on. AGain, you could easily hard code this into the CSS as even with the SCSS we need to know how many elements (or `$pages`) we have for the function to work. 

Now, all of our "pages" take up a full-screen each, much like in the example portfolio or Joseph's. 

The `parallax-child` element is where we start to return back to "vertical viewing". Let's look at the different transforms we're using on this element.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ccjtzmuz799rjlm7r9g0.png)

To keep the element central within its parent, we apply a value `50%` to both `top` and `left`, and `-50%` to both `translateX` and `translateY`. We also apply `rotate(90deg)` to bring everything back to "normal", visually speaking. Again, the default values for `transform-origin` have been left so there's no need to keep these should you be happy with them. 

Finally, our z axis gets some love! By applying `translateZ(-1px)` we see a shift "backwards" in our elements position. And, when we scroll, we notice that the element scrolls a little more slowly than before. It's very subtle, but noticeable if you focus on the gradient. If you're working against a plain background, you likely wouldn't apply this transform to these elements.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bjviuibyy3khfq3ergkp.png)

Lastly, we apply similar styling to the `parallax-background`, only this time we push it "back" much further at `-8px`. 

You may have noticed that when we apply a value to `translateZ`, we also apply a value to `scale`.  There is a useful write-up by [Google](https://developers.google.com/web/updates/2016/12/performant-parallaxing) explaining this in more detail and why this is done, but essentially it negates the resizing that takes place when we "push" or "pull" our elements backwards or forwards respectively. Imagine driving further away from a building, it get's smaller, right? The only way to make it seem the same size it was before, without driving back, is to scale it up. Voila!  

Lastly, we size the background to cover the number of pages we have, in this case 3. Once we've applied some styling to the header nested in `parallax-background` we're done!

I hope I was able to share something interesting with you here, and I’d really appreciate any feedback on either the content or the writing. I’d like to do this more often and making it worthwhile would be a massive bonus.

You can find the sourcecode for this project on [Github](https://github.com/m87wheeler/horizontal-page-parallax).