Last year, I wrote an article that demonstrated how to acheive the popular parallax scrolling effect using nothing but CSS transforms. Since then, I've had plenty of feedback from developers about the technique and the issues they have run into. This post tackles many of these issues and outlines practical methods for implementing CSS parallax.
CSS Parallax feature detection
Many of the reported issues from this technique are focused on older browsers not completely supporting 3D transforms and therefore rendering layers incorrectly. To address this I suggest making parallax an enchancement, using a sensible flat implementation as a fallback. We can use CSS feature detection (@supports
) to check that the browser is capable of creating the effect before applying the relevant styles:
This will only apply the parallax effect if the browser implements CSS feature detection, the perspective
property and, to address iOS momentum scroll issues, doesn't support -webkit-overflow-scrolling: touch
. The upshot of this is, pure CSS parallax will work in: Chrome 28+, Firefox 22+, Safari 9+ (OSX only), Opera 12+ and Edge.
Limiting the effect to relatively modern browsers also narrows the scope to reasonably modern mobile/tablet hardware, so there's a realistic chance of the effect being performant. However, if like me you don't believe that parallax websites work well on small viewports, you can nest the @supports
block in a @media
query to disable the effect for small screens:
Smooth scrolling to content using CSS
A common feature with parallax sites is the ability to scroll smoothly between sections of content. When using JavaScript to implement parallax, this is an easy feature to add but it's possible to acheive the same effect using CSS.
The new scroll-behavior property allows CSS authors to decide how content scrolling should behave when a user triggers navigation to content outside the visible area. Allowed values are instant
, which jumps straight to the content or smooth
, which smooth-scrolls to the content. Adding smooth scrolling to the parallax demo is a simple as applying the following style rule:
If you open the following demo in a browser that supports scroll-behavior
(currently only Firefox will smooth-scroll between anchors) and click the navigation items, you should see content scroll into view.
Smooth scrolling using jQuery plugins
I've had many emails asking why popular jQuery smooth-scroll code snippets or plugins don't work with CSS parallax. These plugins do work with this technique — you just need to ensure the script is targeting the correct element. Most implementations work by animating the scrollTop
property on the html
and body
elements, like this:
What you need to do is target the parallax container element:
If using JavaScript to implement smooth scrolling is a requirement, I recommend you try out the CSSOM scrolling API before turning to jQuery.
Full slide scrolling
Another technique commonly found alongside parallax is detecting the users intention to scroll and forceably moving them on to the next block of content. I'm not a fan of this technique, it's scroll-jacking and, more often that not, ends up creating a frustrating user experience. That being said, I have heard arguments for this method, usually focused around storytelling and directing users through content so I'm going to show how to acheive this using just CSS.
We can use CSS scroll snap points to hijack the unsuspecting visitors scrollbar. Setting scroll-snap-coordinate: 0% 0%
on each slide and scroll-snap-destination: 0% 0%
on the parallax container will ensure slides snap to the top of the viewport. The last step is to set scroll-snap-type: mandatory
on the parallax container to keep the scrollbar locked to a snap point.
If you open the following demo in a browser that supports CSS scroll snap points and try to scroll, you should move through the content one slide at a time.
If you choose to use JavaScript to implement this yourself, remember to target the parallax wrapper element as mentioned in the jQuery smooth scrolling section.
Horiziontal scrolling
Switching parallax to scroll horizontally is trivial. Switch the scroll overflow and arrange the slides so they fill the viewport and appear side-by-side:
Unselectable content and unclickable links
This is another issue I get lots of email about; if a parallax layer scrolls over a block of content then a visitor won't be able to click through it to follow any links or make text selections.
There are two solutions to this problem. First, don't fill the entire viewport with a parallax layer if its content doesn't need that much space. For example, if a layer contains an image with fixed dimensions then set the layer width and height match the image dimensions, or better still, don't set any dimensions and let the browser handle it.
The second solution is to disable pointer events on any layer that will scroll over content. Disabling pointer events will prevent the layer from capturing user interactions allowing elements underneath to receive events:
Browser quirks
CSS parallax works in all modern browsers but, despite CSS transforms being unprefixed everywhere, there are some issues we need to work around in various browsers. These fixes are already included in the article demos.
Microsoft Edge
Edge supports the effect provided a "null" transform is applied to the parent element of the parallax wrapper. Without this transform, Edge won't paint the layers correctly.
If your markup looks like the following example, you'll need to set transform: translateZ(0)
on the <body>
element to fix the painting issues in Edge.
A bug has been raised for this issue in the Microsoft Edge and Internet Explorer channel at Microsoft Connect.
Safari
Safari also requires the translateZ(0)
trick. Without the null transform the parallax effect will render correctly but, to acheive a sliky-smooth 60 FPS, we need it to promote the parent layer of the parallax wrapper to leverage hardware acceleration.
Firefox
Firefox suffers from an overflow scroll issue when elements are transformed along the z-axis and then scaled back to correct their size. It appears that Firefox doesn't account for perspective when computing scroll box boundaries, which can result in unwanted whitespace at the end of your document. The amount of whitespace will depend on the transform and size of the parallax elements.
You can work around this issue by ensuring the last part of your document doesn't use any parallax effects. A bug has been filed and is under investigation so, hopefully, we'll have a fix soon.