Mac scrolling problem and perfect-scrollbar

Mac optimises the scrolling experience based on system preferences.

Scroll setting to always

The default setting is “Automatically based on mouse or trackpad”. With the default setting, only when the user scrolls, he sees the scrollbar. Let’s say, the user has the “Always” setting turned out, he sees an ugly scrollbar like this.

Ugly scrollbar

Some CSS which used to work before don’t work well. Consider the below CSS which shows the scrollbar on hovering over the list.

ul {
  width: 200px;
  max-height: 100px;
  overflow-y: hidden;
}

ul:hover {
  overflow-y: scroll;
}

The above code does not work anymore with the default settings. Even when the user hovers, he does not see the scrollbar. Only when the user starts scrolling, he sees the scrollbar. So, there is no visual cue available to the user to indicate that there is more information available. Fortunately, it is possible to replace the default scrolling experience using perfect-scrollbar.

Custom scrollbars using perfect-scrollbar

perfect-scrollbar is a pure JavaScript package which replaces the OS scrollbars. I will show how we can integrate this with React.

Consider the component below which is scrollable.

class App extends Component {
  render() {
    return (
      <div className="App">
        <ul style={{ maxHeight: 100, overflowY: "scroll", width: 100 }}>
          <li>0</li>
          <li>1</li>
          <li>2</li>
          <li>3</li>
          <li>4</li>
          <li>5</li>
          <li>6</li>
          <li>7</li>
          <li>8</li>
          <li>9</li>
          <li>10</li>
        </ul>
      </div>
    );
  }
}

This is how it looks by default. Note that there is no visual cue that the list is scrollable.

Scrollable list

To add perfect-scrollbar, add the package.

yarn add perfect-scrollbar

Import the scrollbar and the associated styles.

import Scrollbar from 'perfect-scrollbar';
import 'perfect-scrollbar/css/perfect-scrollbar.css';

Add a ref to the list.

constructor() {
  super();
  this.comp = React.createRef();
}
...
<ul
          ref={this.comp}

Wrap the list with scrollbar in componentDidMount.

componentDidMount() {
  this.ps = new Scrollbar(this.comp.current);
}

And cleanup the scrollbar in componentWillUnmount.

if (this.ps) {
  this.ps.destroy();
  this.ps = null;
}

For perfect-scrollbar to work, the list should have a relative (or absolute) position (not static) and an overflow property of hidden (not scroll).

style={{
  maxHeight: 100,
  position: "relative",
  overflowY: "hidden",
  width: 100
}}

With these changes, the list has perfect-scrollbar. When you hover over the list, there is a scrollbar. And when you move out of the list, the scrollbar disappears.

List with perfect-scrollbar

The whole code is available in CodeSandbox.

When you are using perfect-scrollbar within a Modal, attach / detach the scrollbar in componentDidUpdate. Some example code is shown below.

componentDidUpdate(prevProps, prevState) {
    if (this.state.isOpen && !prevState.isOpen) {
        this.ps = new Scrollbar(this.menuComp);
    }
    if (!this.state.isOpen && prevState.isOpen) {
        if (this.ps) {
            this.ps.destroy();
            this.ps = null;
        }
    }
}

Overall, perfect-scroll is a well designed package with no dependencies. It has been around for sometime and there is a lot of community support.

Related Posts

Leave a Reply

Your email address will not be published.