This README and the corresponding demo offer some solutions to a bug in pure-CSS parallax websites, which is caused by certain system scrollbars. For reference, the page layout looks something like this:
<body>
<div class="parallax-page">
<div class="parallax-group">
<div class="parallax-bg-foo"></div>
<div class="parallax-bg-bar"></div>
</div>
<div>
Non-parallax content
</div>
<div class="parallax-group">
<div class="parallax-bg-foobar"></div>
</div>
</div>
</body>
I'm a big fan of Keith Clark's Pure CSS parallax technique and have been using it with some success on one of my own sites. Recently, however, I noticed that the background parallax elements, even in his own demo, were not sitting flush on the left side of the screen. They left a small gap, the size of which depends on how far back in the 3D space they were rendered.
Unable to reproduce this problem later, I discovered that the culprit was a
particular kind of system scrollbar, which OSX uses by default whenever a
USB mouse is plugged in. (It can also be turned on manually, by going to
System Preferences > General
and setting Show scroll bars
to Always
.)
The scrollbar changes the width of the main view, without changing the 3D perspective, causing 3D-rendered elements to shift out of alignment with the rest of the viewport. This is most noticable as a gap on the left side of the screen, but these elements are also out of alignment with each other.
You can see this clearly in the demo I made by checking "Show Center".
As of at least Safari 14.1.2, Safari now treats 3D space the same as other modern browsers. This means that fixes 1a and 2a are aligned across all modern browsers, and the extra padding of fixes 2a and 2b, which accounted for browser inconsistencies, is no longer needed.
You can safely use Fix 1a and get proper alignment on all browsers.
Also consider using
sass-parallax
, a small
CSS/Sass library that handles this as well as several other edge cases.
This fix takes the intuitive method of changing the
perspective-origin
and
transform-origin
properties. These are set to right
, based on a tip from Keith Clark's
original article that this prevents unwanted horizontal scrolling in webkit
browsers. But I've not been able to reproduce that problem so maybe it was
fixed. In any case, this pushes the content to the right, into the scrollbar,
and away from the left of the viewport. So let's just push them left instead:
.parallax-page {
perspective-origin: left;
}
%parallax-bg {
transform-origin: left;
}
It works! The background elements are sitting flush against the viewport and are all in alignment. So what's the problem?
If you try Fix 1a in Safari, you'll see that the red strip of the document body
is still there, only now on the right side of the screen. If you want to
achieve the same effect in Safari, you'll need to set these properties to
center
instead.
.parallax-page {
perspective-origin: center;
}
%parallax-bg {
transform-origin: center;
}
This is because Safari is the only browser that calculates the size of its 3D
space by the outer width of the parallax-page
element, not the inner
width, encroached on by the scrollbars. This might make more sense than the
alternative, since an origin of center
(the default value for both
properties) would solve our problem, but as it is, Safari is the odd one out.
So let's try something else. The problem isn't really the perspective-origin
,
since this would have no effect if parallax-group
child elements were just
sitting flush with their parallax-page
parent. Here's how we do that:
.parallax-group {
margin-left: calc(100% - 100vw);
margin-right: calc(100% - 100vw);
padding-left: calc(100vw - 100%);
padding-right: calc(100vw - 100%);
}
Because the parallax-parent
is the size of the viewport, the difference
between 100vw
and 100%
is the width of the scrollbar. In this case, we are
giving the immediate children of this element negative margins equal to that
width. A positive padding of the same width keeps all non-parallax elements
from slipping into these negative margins.
This makes the parallax-group
elements one scrollbar's-width wider than their
parent, but it aligns their center with the center of the inner width of the
3D space (not counting the scrollbar). This ensures that all parallax elements
maintain vertical alignment.
Except on Safari, where they don't. The problem is the same as before: Safari
calculates the 3D space based on the outer width of the parallax-page
element. So we need to decrease the margin offset to one that will find the
center of paralax-page
's outer width. With a perspective-origin
set to
right
, that looks like this:
.parallax-group {
margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw);
padding-left: calc(50vw - 50%);
padding-right: calc(50vw - 50%);
}
Unfortunately, there doesn't seem to be any one alignment that will work across Safari and all other browsers. Everything is thwarted by the fact that you cannot measure the width of a CSS 3D space; only the inner width of its element (perhaps an argument for making the two the same, as most browsers have done).
There is one fix, however, that works across all Webkit broswers, including Safari:
::-webkit-scrollbar {
display: none;
}
The scrollbar is gone, so there is no problem calculating the 3D space. Combined with Fix 1a above, this would align parallax elements accross all browsers, under all conditions.
Unfortunately, it means removing the scrollbar: an important navigation tool for many people. Since, in most cases, the scrollbar will only appear if the user explicitely told their system to put it there, I am against this solution.
I recommend using Fix 2a. It aligns elements on the majority of
browsers, and even though they are out of alignment in Safari, they at least
overflow the parallax-page
so that there is no gap for the document body to
peek through—usually a much more noticeable problem than alignment issues!
Remember too that this issue will only arise for Safari users who are also using a USB Mouse, or who have scrollbars manually turned on. The only downside to this approach is that parallax elements get slightly bloated accross all browsers. If this really bugs you, you can always use Fix 1a and target Safari with PHP or some clever Javascripting to correct for its alignment issues.
It came to my attention, shortly after making this, that a newer version of Keith Clark's parallax demo did not seem to have this problem. There is no visible strip on that page when scrollbars are turned on.
The reason is that, in this demo, Keith is achieving the affect by pulling his "foreground" elements up out of the 3D space, whereas in the previous demo (and my own), we are pushing the "background" elements back. The foreground parallax elements are also leaving a gap, but because of the skewed perspective, that gap is on the right side of the screen, hidden under the scrollbar!
I like this solution, and I think it's practical in some cases, but here are some arguments against it:
-
This approach is not great for creating the slow-scrolling, far-back background image effect, the kind most associated with parallax layouts. You could achieve the same difference in scroll speed between foreground and background elements, but this would also increase the overall scroll speed of the page. In Keith's demo, and in other cases where the difference between foreground and background elements is not very high, that's not such a big deal. But in general, I think that content in the "apparent foreground" of the page (text content, mostly) should scroll at the browser-default rate.
-
The scrollbar still pushes these foreground elements out of vertical alignment on all browsers except Safari. This is not noticeable in Kieth's demo either, but in cases where you have lots of centered content, this could become a problem.