Two Browsers Walked Into a Scrollbar

Mona Lisa Overflow (image provided by Scott Jehl)

The scrollbar is a humble but productive mechanism that operates as the primary means through which one can traverse a document. But that’s not all a scrollbar can do! This modest workhorse also provides a meaningful hint at how long the document is, pulling double duty as a document progress bar too.

The scrollbar is under attack. Scrolljacking hijacks the default scrolling behavior, breaking the implied contract between document length and scrollbar height.

Moreover, touch devices have popularized hiding the scrollbar, making it invisible until an overflowing element is scrolled, trading design aesthetics for confusion on containers that don’t appear to be overflowing/scrollable at all.

Classical desktop operating systems have continued this trend, attempting to minimize the design intrusiveness of the classic scrollbar.

Before we get too far, let’s get a few definitions out of the way:

  • Obtrusive scrollbars: scrollbars that take up screen real estate. These do not overlay on top of the content, but appear next to it.
  • Unobtrusive scrollbars: scrollbars that sit on top of the content. These don’t subtract screen real estate away from the container they belong to.

Current Behavior

By default on both iOS and Android scrollbars are unobtrusive.

On Mac OS (Mojave at time of writing), scrollbars are hidden until the element is scrolled. This is the default behavior when a mouse is not connected to the machine. There are three options for this in the General pane in your System Preferences:

Mac OS General System Preferences pane with Automatically based on mouse or trackpad selected

This preference was confirmed to control how scrollbars behave in Chrome, Firefox, and Safari, and new Chromium-based Edge.

Watch the following video to see how the Mac OS user preference changes the obtrusiveness of the scrollbar:

On Windows 10, a similar preference exists in Settings → Display → Simplify and personalize Windows.

Windows 10 preference pane with Automatically hide scroll bars in Windows checked

Unfortunately even with this preference checked, it had no effect on scrollbar behavior in Firefox, Chrome, in Internet Explorer and Edge—whether Chromium or EdgeHTML based.

The Problem

Demo of the default scrollbar style on Windows 10 (in Chrome)

Windows scrollbars are not only obtrusive by default but are particularly heavy, design-wise. They’re much wider by default than their Mac OS counterparts and typically conform to operating system colors (not a page’s color palette).

For designers accustomed to Mac environments but designing multi-platform web experiences, trying to make everyone happy in a way that doesn’t place a lot of performance burden on the end user can be tricky.

Our Requirements

  1. We want desktop scrollbars to be more visually appealing. Especially important for overflow containers inside of the viewport that need to blend better with the visual design aesthetic (in my opinion, visuals are not not quite as important for page-level scrollbars, but that’s a point of contention I’m sure).
  • Minimize the real estate that a scrollbar can occupy. Windows scrollbars are obtrusive and very wide by default.
  • Respect changes to user preferences. If a user has selected non-default options for scrollbar behavior, respect those preferences when possible.
  • Avoid JavaScript heavy solutions that normalize unobtrusive scrollbars and place a performance burden on the end user (e.g. the lovely OverlayScrollbars plugin).

How far can we get with CSS?

<div class="overflowing-element"></div>
.overflowing-element {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
}

If you fancy unobtrusive scrollbars on Internet Explorer and EdgeHTML based Edge, use -ms-overflow-style: -ms-autohiding-scrollbar; and they will magically swap (easy, right?).

Side note that when iOS 13 is released, -webkit-overflow-scrolling: touch may not be required for improved scrolling physics on iOS (although you may want to keep it around for a bit for older iOS versions).

You may also want to read up on the related CSS property overscroll-behavior, which controls how the document scrolls when the overflow container content scrolls to the boundary.

Firefox

Firefox supports the unprefixed CSS properties scrollbar-color and scrollbar-width.

Note that for clarity these code examples use CSS variables, which are not supported by Internet Explorer 11.

:root {
--scrollbar-track-color: transparent;
--scrollbar-color: rgba(0,0,0,.2);

--scrollbar-width: thin; /* or `auto` or `none` */
}
.overflowing-element {
scrollbar-width: var(--scrollbar-width);
scrollbar-color: var(--scrollbar-color) var(--scrollbar-track-color);
}

Chrome/Safari/Chromium-Edge/et al

WebKit/Blink-based browsers support non-standard pseudo-elements for customization.

:root {
--scrollbar-track-color: transparent;
--scrollbar-color: rgba(0,0,0,.2);

--scrollbar-size: .375rem;
--scrollbar-minlength: 1.5rem; /* Minimum length of scrollbar thumb (width of horizontal, height of vertical) */
}
.overflowing-element::-webkit-scrollbar {
height: var(--scrollbar-size);
width: var(--scrollbar-size);
}
.overflowing-element::-webkit-scrollbar-track {
background-color: var(--scrollbar-track-color);
}
.overflowing-element::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-color);
/* Add :hover, :active as needed */
}
.overflowing-element::-webkit-scrollbar-thumb:vertical {
min-height: var(--scrollbar-minlength);
}
.overflowing-element::-webkit-scrollbar-thumb:horizontal {
min-width: var(--scrollbar-minlength);
}

However, there is a minor issue with this code. When you set a height or width on the ::-webkit-scrollbar pseudo-element, on Mac OS it will swap unobtrusive scrollbars to be obtrusive (overriding the default configuration). However, we can fix this with a little JavaScript!

CSS and a tiny bit-o-JavaScript

We can add a tiny little JavaScript feature test to detect if the default scrollbar is obtrusive or not. It looks something like this:

/*
* Scrollbar Width Test
* Adds `layout-scrollbar-obtrusive` class to body
* if scrollbars use up screen real estate.
*/

var parent = document.createElement("div");
parent.setAttribute("style", "width:30px;height:30px;");
parent.classList.add('scrollbar-test');

var child = document.createElement("div");
child.setAttribute("style", "width:100%;height:40px");
parent.appendChild(child);
document.body.appendChild(parent);

// Measure the child element, if it is not
// 30px wide the scrollbars are obtrusive.
var scrollbarWidth = 30 - parent.firstChild.clientWidth;
if(scrollbarWidth) {
document.body.classList.add("layout-scrollbar-obtrusive");
}

document.body.removeChild(parent);

We apply our layout-scrollbar-obtrusive class to the document when our scrollbars are obtrusive. We can use this to only apply width and height to scrollbars that are obtrusive, avoiding the swapping behavior described previously (and respecting user preferences!).

.layout-scrollbar-obtrusive .layout-scrollbar::-webkit-scrollbar {
height: var(--scrollbar-size);
width: var(--scrollbar-size);
}

How did we do?

On touch devices with unobtrusive scrollbars (e.g. iOS and Android), we keep the default behavior for free.

On Mac OS, we are able to respect the user’s system preferences. That means no unintended swapping between unobtrusive and obtrusive scrollbars. We only apply our style to obtrusive, visible scrollbars to meet our design requirements.

On Windows, in Firefox and Chrome there was no option for unobtrusive scrollbars but we were able to apply our CSS-only control here as well. With working demos of CSS customized scrollbars in place, we were able to get buy-in from the design team and settle on this middle ground, avoiding a heavier JavaScript solution.

Review the Demos





Mona Lisa image created by Scott Jehl

All blog posts