The web is a rather vertical place. You read a web site like you read a physical page: left to right, top to bottom. But sometimes, you want to step away from the verticality of it all and do something crazy: make a horizontal list. Or even crazier, a horizontal site!
I’d be nice if we could do something like this:
/* This isn't real */
div {
scroll-direction: horizontal;
}
Unfortunately, that’s not going to happen. It’s not even on the roadmap for CSS.
That’s too bad, as at the company I work for this would be quite useful. We do quite a few web presentations. Presentations are a very horizontal thing – usually slides have a 4:3 or 16:9 radius. This means we always have a struggle between the horizontality of presentations and the verticality of web technologies. And by we, I mean me. But if there’s one thing I like, it’s a challenge.
Another Use Case
The specific use case that led to me digging into this idea that a customer wanted to show all their products on a single slide. Of course, their product catalog was way too big to put in a single view. So we decided to split them up into three categories, each horizontally scrollable. So the three most prominent product in each category were visible and less important products were still easily accessible.
A Non-JavaScript Way
There are, no surprise, numerous ways to do this in JavaScript. Some of them are on this very site.
I was curious if it was possible to do in pure CSS. The solution ended up being fairly straightforward:
- Create a container with items
- Rotate the container 90 degrees counterclockwise so the bottom is to the right
- Rotate the items back to correct-side up
Step 1) Set up the container
Make a <div>
, and make a bunch of child elements.
In this example, our side-scrolling container will be 300px wide, with 8 items of 100×100px each. These are arbitrary sizes; they could be anything.
<div class="horizontal-scroll-wrapper squares">
<div>item 1</div>
<div>item 2</div>
<div>item 3</div>
<div>item 4</div>
<div>item 5</div>
<div>item 6</div>
<div>item 7</div>
<div>item 8</div>
</div>
The height of the container will become the “width” and vice-versa. So below, the “width” of our container will be 300px:
.horizontal-scroll-wrapper {
width: 100px;
height: 300px;
overflow-y: auto;
overflow-x: hidden;
}
Now the children:
.horizontal-scroll-wrapper > div {
width: 100px;
height: 100px;
}
Step 2) Rotating the container
Now we rotate the container -90 degrees with a CSS transform
. And there you have it: a horizontal scroller.
.horizontal-scroll-wrapper {
...
transform: rotate(-90deg);
transform-origin: right top;
}
There’s just one tiny issue: our children have rotated too, and now anything within is on its side.
Step 3) Rotate the children back upright
How would we go about getting the children upright again? Rotate them back using another, opposite CSS transform
.
.horizontal-scroll-wrapper > div {
...
transform: rotate(90deg);
transform-origin: right top;
}
Step 4) Fixing the positioning
It’s starting to look alright, but there are still some issues.
By rotating the wrapper using the top right as an anchor point, our left side has shifted by the width of the container. If you find this difficult to understand, just put your finger on the top right corner of a page and rotate it. The solution: shift it back with translateY
.
Better. But the first item is still missing, due to the same phenomenon happening to the items. We could fix this by giving the first child a top margin of its width or by translating all items the same way we did the wrapper. The easiest way I’ve found though is to add a top padding to the wrapper equal to the item width, creating a kind of buffer for the items.
.horizontal-scroll-wrapper {
...
transform:rotate(-90deg) translateY(-100px);
...
}
Demo
See the Pen Horizontal scroll (simple example) by Pieter Biesemans (@pieter-biesemans) on CodePen.
Here’s another where you can see non-square children:
See the Pen Horizontal scroll (extensive example) by Pieter Biesemans (@pieter-biesemans) on CodePen.
Compatibility
I have tested on the devices immediately available to me.
Device | OS | Browser | works? |
---|---|---|---|
Desktop | Win10 | Chrome 54 | Y |
Desktop | Win10 | Firefox 47 | Y (w scrollbars) |
Desktop | Win10 | IE11 | Y (w scrollbars) |
Desktop | Win10 | Opera 41 | Y |
Desktop | Win10 | Vivaldi 1.4 | Y |
Laptop (touch screen) | Win10 | Chrome 54 | N |
Samsung Galaxy S3 | Android 4.3 | Chrome Mobile 52 | Y |
Samsung Galaxy S6 | Android 5.0 | Chrome Mobile 52 | Y |
Nexus 6P | Android 6 | Chrome Mobile 52 | Y |
iPad2 | iOS | Chrome Mobile 52 | N |
iPad2 | iOS | Safari Mobile 9.0 | N |
iPad Air 2 | iOS | Safari Mobile 9.0 | N |
Desktop
Since the styling of scrollbars is currently only supported by WebKit/Blink, Firefox and IE still show the ugly gray ones. You could sniff this out with JavaScript and hide them completely, but that’s stuff for another tutorial.
Using the mouse scroll wheel works great on desktops. My laptop was a different matter, though. Both the touchscreen and the touchpad acted as though the div was not rotated.
Mobile
I was kind of surprised to find that Android actually understood that the container had been rotated, and let you scroll sideways by swiping left and right.
iOS on the other hand did not play nice. It acted like the container did not get rotated, so you have to swipe up and down to scroll sideways, which of course is counterintuitive. Also, swiping left and right moves the items up and down in their wrapper, which is unexpected and weird. Setting the overflow to hidden does not alleviate this issue.
Conclusion
According to Can I Use, CSS transforms are currently supported by over 93% of users (at the time of this writing, November 2016), so there’s no issue there.
Beware of using this in production, though. I have tested this on some devices, but not at all extensively or in depth.
The greatest issue is with touch inputs that requiring you to swipe up and down to go left and right. A possible solution would be to include a message on your site explaining this, but you’d have to rely on people actually reading your message. And even then it’d still be counterintuitive. Another possible solution would be to capture the touch input with JavaScript on those devices, but then you’d be better off just doing the whole thing in JavaScript and foregoing this CSS hack completely.
That’s a technique I haven’t heard of before. But even after reading the article twice I’m not quite sure what specific problem you’re trying to solve with it. Why not just use actual native horizontal scrolling, for example by preventing line breaks with
white-space: nowrap
and usinginline-block
items? That’s much, much simpler in terms of necessary layout trickery and works on any device regardless of input type.Are you jumping through all these hoops just to make scroll wheels scroll horizontally?
I was about to write a similar reply. I would stick with
overflow-x: scroll
as the demos seem a little odd to have to scroll down to go right.Interesting to see another way to solve a problem though :)
The entire point is to get native scrolling to go sideways instead of up and down. It’s weird. It’s a trick.
Yes, exactly that. And because I can.
So it doesn’t work on iOS, behaves like
overflow-x: scroll
on Android, and makes vertical scrolling scroll horizontally on (some) desktops, because, well, why not, it’s a trick!Fair enough. I tend to think that making the vertical scroll wheel scroll horizontally is just as bad as the touch device issue you pointed out in your article (making vertical swiping scroll left and right), because really, both have the same issue of subverting user expectations.
As a user, if you want to natively scroll horizontally on a non-touch device, you can hold Shift while rolling the scroll wheel. Of course close to 0 users know about this, so as a developer I’d assist them by providing navigation buttons and a scroll bar, not by messing with CSS layout in terrible ways and screwing up native scrolling for people on touch devices. ;)
Anyway, I get it – it’s a hack.
Or we could just use flexbox without wrapping. I did this recently for http://xoroshe.pl on the main banner.
Oh, I didn’t get the point of this, sorry.
You can go far away and use css multicol too (so, we don’t need calc or know the width):
https://escss.blogspot.com/2015/03/mouse-wheel-down-scroll-right.html
This is a CSS trick I cannot advocate using. The downside is too steep. Clever idea, though. Thanks, Pieter.
Hey, no prob. I never said you have to use it anyway. I wouldn’t use this in production either.
It doesn’t seem to play nice in Chrome Desktop with pen/tablet input (which is my main pointing device). It forces the scroll motion to be vertical, whereas with the default browser scroll the horizontal scrolling works perfectly.
Indeed. It behaves much the same on my touch screen laptop. That’s why I wouldn’t use this in production. But hey, the point of the exercise was just to see if I could hack it :)
Accessability is an issue as well. You probably need another mechanism for scrolling left/right for people who can’t use a mouse (or even a mouse wheel).
But it’s a great example of thinking outside the box. I myself have created a site that uses a horizontally-scrolling area; and I did it using css columns. This might be more straightforwards.
Accessibility is definitely an issue, most input devices will choke on this. I was rather surprised that Android didn’t.
I would not call this more straightforward then using columns, on the contrary. It’s more an exercise to see if I could rather than if I should.
I don’t know about you, but for every article that I read, I read the intro paragraph to get the gist of the article and this one is VERY clear on the purpose of the article. the purpose is to use this as a presentation tool starter kit for showing slides.
I think is cool the way Pieter developed the solution using just CSS as there are environments where even javascript is not EVEN allowed.
Great Pieter and thanks for sharing.
You’re welcome lcr. I hope you enjoyed it and it inspired you to dare experiment and hack things.
Why not just
overflow-x: auto
?Mouse wheel not supported.
Hi Jens, the whole point was to make the scroll wheel do something it usually doesn’t do: scroll right. It’s a CSS trick or hack if you will that you can normally only achieve with Javascript.
Yup,
I made the same exercice on a Codepen ~6 months ago, and the few browsers tested led me to the same conclusions: too much different behaviours to trust browsers for this particular case.
Hi Mehdi, I knew I couldn’t have been the first to think about something so simple. Thanks for sharing.
Hi
I had exactly this problem a few days ago and managed to do it with a bit of hacky use of white-space:nowrap;
What i got: https://jsfiddle.net/mzvaan/590grzuy/
Hi Matej, that’s what i usually do. The whole point of this exercise was to make a sidescroller that could be scrolled using the mouse wheel though. But it’s hacky and doesn’t behave very well on touch devices and such, so for now we’re stuck with
white-space: nowrap
.Hm, they done it much simpler in the top menu @ medium.com . They use white-space: nowrap, overflow-x: auto; on the parent and letter-spacing:0; on the children, then they apply the proper letter spacing inside the children.
Hi Chris, that’s what i usually do in production. The whole point of this exercise was to make a sidescroller that could be scrolled using the mouse wheel though. But it’s hacky and doesn’t behave very well on touch devices and such, so for now we’re stuck with white-space: nowrap.
Here I put a simple demo that is working on desktop and mobile:
Probably some indicator that scrolling is possible is needed
Markup:
Styles
@Chris Panayotov the idea is to scroll right via mouse scroll wheel
Ah, but that would require scrolling in a way that’s natural to the platform, like using shift + scroll-wheel, or dragging your finger in a direction that matches the scrolling. Who wants to do that?
This article fixes this obvious problem by allowing you to scroll horizontally using your vertical scroll-wheel without shift, on some platforms, but not Android, or iOS, or laptops with touchscreens. But you can definitely do it on platforms that you can shift + scroll-wheel anyways, so you save an entire button press.
I’m curious, how does this site do it?
It acted like the container did not get rotated, so you have to swipe up and down to scroll sideways, which of course is counterintuitive.
Is scrolling up and down to scroll sideways intuitive?
The whole point of this exercise was to recreate this demo in CSS only: https://css-tricks.com/examples/HorzScrolling/
But granted, scrolling down with the mousewheel to go sideways isn’t very intuitive either.
aspect ratio*
Correct. Well spotted.
I just closed a website that had horizontal scrolling (linked from css-tricks no less).
Because it had horizontal scrolling.
If you’re trying to transfer this offline, paper concept to the online world, you probably took the wrong turn somewhere along the way. No use case you describe here gets better results from horizontal scrolling.
The internet is no book and your browser is no magazine. Work with and for the medium, not against it.
Step back, and read https://en.wikipedia.org/wiki/Form_follows_function.
I understand and hear what you are saying.
But be careful not to fly to close to dogmatism: https://css-tricks.com/increasing-wariness-dogmatism/
Hi Chris,
I have encountered an issue with rotating in iOS, and it seems like iOS ignores rotation if it is a multiple of 90, if you change it to 89.9, it should work.
Hope this helps
Comment from Cleber Santana, after the buzzer:
A take on this concept, via Jari Thorup Palo:
https://codepen.io/japalo/pen/LjMraa