Creating A Horizontal Scrolling Web Page. Bonus: With Parallax!

_martinwheeler_

Martin Wheeler

Posted on October 6, 2020

Creating A Horizontal Scrolling Web Page. Bonus: With Parallax!

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. 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

Let's Dive Right In

We’ll start with the HTML.

Alt Text

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

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

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

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

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

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

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

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

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

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

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 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.

💖 💪 🙅 🚩
_martinwheeler_
Martin Wheeler

Posted on October 6, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related