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
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
body elements, like this:
What you need to do is target the parallax container element:
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.
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:
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.
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 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 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.