Intro

TL;DR

  • Scrollbars on Windows are taking some space in the viewport.
  • vw units don't take scrollbars into account.
  • Fixed buttons in fixed modals are relative to the viewport and not the modal.

My solution: saving scrollbars width in Javascript and store it into a CSS Variable.

Jump to the solution 👇

Long version

As a Windows user, I sometimes notice when a website hasn't been tested on Windows. One of the first clue I can see are the scrollbars appearing for no reason ! By default on MacOs, the scrollbars are very subtle and are hidden while you don't scroll. They don't take any extra space in the viewport, this means 100% of the screen width is equal to 100% of the body width. But on Windows, the size of the scrollbars is subtracted from the width of the window. Usually the scrollbars are 17px wide but this value may change from one OS to another.

Here are two situations I recently came across:

1. When using vw units.

The definition from the specs of the vw units is:

The viewport-percentage lengths are relative to the size of the initial containing block. [...] However, any scrollbars are assumed not to exist.

It is clearly written that scrollbars shouldn't influence the value of this unit !

In the screenshot below, I set the width of my div to 100vw. You can see there are two issues because of my vertical scrollbar.

First, the text gets cropped because the div is going under the scrollbar. Second, because of the div being too wide, I'm having an horizontal scrollbar.

  .container {
  width: 100vw;
  background:black;
  font-size: 20px;
  color: white;
}

2. When using a fixed modal.

When I implement modals, I often set them to a specific height with an overflow: auto;. If there is too much content in the modal, the user will be able to scroll in it. But I also want my users to be able to close the modal and to always see the close button while scrolling. To do so, I need to position the close button as fixed inside my fixed modal.

Here is an example of what I want to achieve. As long as I don't have any scrollbars, I don't have any issue.

This is the code from my close button:

  .close {
  position: fixed;
  top: 40px;
  right: 40px;
}

But if there is too much content, scrollbars will appear and my close button won't be perfectly aligned because the left value of the button is relative to the viewport.

My solution

I have been playing a bit with CSS Custom properties lately (also known as CSS Variables) and I thought it would be useful to define a variable containing the size of the scrollbar. This way, I could position my elements or use the vw unit without worrying if I'm on a Mac or Windows.

First I need to calculate the actual size of the scrollbar. To do so, I create a small function to retrieve the width of the scrollbar based on temporary elements I'm creating in Javascript

  const getScrollbarWidth = () => {
  // Create a temporary div container and append it into the body
  const container = document.createElement('div');
  // Append the container in the body
  document.body.appendChild(container);
  // Force scrollbar on the container
  container.style.overflow = 'scroll';

  // Add ad fake div inside the container
  const inner = document.createElement('div');
  container.appendChild(inner);

  // Calculate the width based on the container width minus its child width
  const width = container.offsetWidth - inner.offsetWidth;
  // Remove the container from the body
  document.body.removeChild(container);

  return width;
};

// Get the scrollbar dimension
const scrollbarWidth = getScrollbarWidth();
// Set a custom property with the value we calculated
document.documentElement.style.setProperty('--scrollbar', `${scrollbarWidth}px`);

In the CSS I'm always creating a default variable in case Javascript is not enabled or it triggers an error.

  :root {
  --scrollbar: 0px;
}

Now that we have a variable containing the width of the browser scrollbar, we can use it straight into our CSS !

Here is the updated code from the first demo:

  .container {
  width: 100vw; // Fallback for old browsers
  width: calc(100vw - var(--scrollbar));
  background:black;
  font-size: 20px;
  color: white;
}

And here is the code for my close button:

  .close {
  position: fixed;
  top: 40px;
  right: 40px; // Fallback for old browsers
  right: calc(40px + var(--scrollbar));
}


I hope this post may help you in your projects !
And don't forget to test your websites on Windows computers too 🤭

Mamboleoo