Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Wed, 03 Dec 2025 16:22:37 +0000 en-US hourly 1 https://wordpress.org/?v=6.9 225069128 The Downsides of scrollbar-gutter: stable; (and one weird trick) https://frontendmasters.com/blog/the-downsides-of-scrollbar-gutter-stable-and-one-weird-trick/ https://frontendmasters.com/blog/the-downsides-of-scrollbar-gutter-stable-and-one-weird-trick/#comments Wed, 03 Dec 2025 16:22:36 +0000 https://frontendmasters.com/blog/?p=7892 The esteemed Zach (Leatherpants), blogged about the virtues of scrollbar-gutter: stable;, suggesting it might be good in a starter stylesheet. (Hey, I’ve got one of those.) The thing that it solves is content-shifting. If you have a page that, upon first render, doesn’t have a vertical scrollbar, but more content loads later and gets one, it can shift content horizontally to make room for the scrollbar, which is a smidge annoying. That’s assuming your OS setting has always visible scrollbars enabled. (I do in my macOS settings, I prefer it.)

Also, navigating from a page that doesn’t require vertical scrolling to one that does (or vice versa) causes a layout shift to the tune of one scrollbar width.

Using scrollbar-gutter: stable; (supported everywhere) means that the browser will “reserve space” for a scrollbar and thus totally remove the layout-shifting downsides mentioned above.

You can see the visual shift when we add content that overflows vertically. But if we flip on scrollbar-gutter: stable; the content stays steady when it goes back and forth between overflowing and not.

Notice in the video above though, the shifting-fix is accomplished by putting the space of the scrollbar there. You can see it’s quite literally a white bar. This only seems to happen when the page is rendered in an <iframe> like it is on CodePen, but I still find it highly obnoxious and a downside (as there is no way I’ve found to make it not a white, or dark-in-dark-mode, bar).

Here’s that demo:

Fortunately, the “literal white bar” problem isn’t there on regularly-rendered pages (outside of an iframe), as that would be an instant deal breaker.

The remaining problem is centering.

The space that is reserved for the maybe-maybe-not scrollbar cannot be factored into centering (like margin: auto; and whatnot).

Screenshot showing a content area with a gray background, below which there is a large white space indicating the absence of a vertical scrollbar, accompanied by red arrows pointing towards the white space.
😭

So if you really need to visually center something, it’ll be fine-ish if there is a scrollbar, and noticeably not-centered-looking if there isn’t. Boooo.

To me, this is just annoying enough to not put it in a starter stylesheet.

But!

Just for fun we could look at a newfangled CSS alternative. My big idea here is that we actually can tell if the page is overflowing and has a scrollbar or not these days. We can do this via scroll-state queries.

So we make the whole page a scroll-state container then watch for when it is scrollable and push the whole page in a little bit the same width as the scrollbar.

html {
  container-type: scroll-state;
  
  --scrollbar-width: 25px;
  &::--webkit-scrollbar {
    width: var(--scrollbar-width);
  }
}

body {
  @container scroll-state(scrollable: block) {
    padding-left: var(--scrollbar-width);
  }
}

Notice I’m attempting to wrestle control over the width of the scrollbar there using those non-standard vendor prefixes. But 25px is the generally standard width of the scrollbar anyway. But that could change if a user set something like scrollbar-width: thin; or something. Makes me wish there was an env() variable or something that always reflects the width of the scrollbar at any DOM level. Anyway, if you have Chrome, you can see this approach working here:

Certainly the scrollbar-gutter approach is easier and far better supported, but it’s neat to know there are future options.

]]>
https://frontendmasters.com/blog/the-downsides-of-scrollbar-gutter-stable-and-one-weird-trick/feed/ 1 7892
Adaptive Alerts (a CSS scroll-state Use Case) https://frontendmasters.com/blog/adaptive-alerts-a-css-scroll-state-use-case/ https://frontendmasters.com/blog/adaptive-alerts-a-css-scroll-state-use-case/#respond Wed, 16 Jul 2025 13:13:36 +0000 https://frontendmasters.com/blog/?p=6397 Sometimes it’s useful to adapt the controls available to users based on whether they’ve scrolled through key positions on a page.

Here’s an example: a user scrolls through a Terms & Conditions page. If they click “agree” without having scrolled down until the end, we could prompt them with a “please confirm you’ve read these terms” before continuing. Whereas if they have scrolled down the whole way, that could imply they have read the terms, so we don’t need the additional prompt.

Implementing something like this is relatively easy with the recent CSS scroll-state queries (browser support).

The following is an example of exactly as described above. If you click the “Sign Up” button without having scrolled down until the end, you’ll see an additional prompt reminding that you might not have read the terms yet and if you’d still like to sign up. And if the “Sign Up” is clicked after the text has been scrolled to the end, the sign-up acknowledgement pops up without any confirmation prompt first.

This is a video version of the demo, because browser support is Chrome-only as this article is published.

Here’s a live demo:

The Layout

We’ll start with this basic layout:

<article>
  <!-- many paragraphs of ToS text goes here -->
  <div class="control">
    <button>Sign Up</button>
  </div>
</article>
article {
  overflow: scroll;
  container-type: scroll-state;
  .control {
    position: sticky;
    bottom: -20px;
  }
}

The sign up button’s container (.control) is a sticky element that sticks to the bottom of its scrollable container (<article>). This is so the user always has access to the sign up button, in case they prefer to drop reading the terms and sign up right away.

The scrollable container (<article>) has container-type: scroll-state. This makes it possible to make changes to its descendants based on their scroll positions.

The Scroll-State Conditional Rule

This is where we code in how the button control’s action adapts to its scroll position inside the article.

@container not scroll-state(scrollable: bottom) {
  button {
    appearance: button;
  }
}

When the container (<article> in our example) can no longer be scrolled further down, i.e. the container has already been scrolled until its bottom edge, we make a subtle change to the button in CSS that won’t visually modify it. In the example above, the button’s appearance is set to button from its default auto, keeping the button’s look the same. 

The Alerts

When the button is clicked, depending on the value of its appearance property, show the relevant alert. 

document.querySelector('button').onclick = (e) => {
  if (getComputedStyle(e.target).appearance == "auto" 
      && !confirm("Hope you've read the terms. Do you wish to complete the sign up?"))
    return;

  alert("Sign up complete");
};

If the <article> has not been scrolled down until the end, the button’s appearance value remains its default auto (getComputedStyle(e.target).appearance == "auto"). The click handler executes a confirm() prompt reminding the user they might not have read the terms fully yet, and if they’d like to continue with the sign up. If the user clicks “OK”, the alert("Sign up complete") message shows up next. 

If the article has been scrolled down to the end, the button will have an appearance value other than auto, and so the click handler executes the alert() only.  


Learn about scroll-state queries (here and/or here) to know the different kinds of scrolling scenarios that you can work with. Based on scroll states and positions, you’ll be able to change the appearance, content, or even functionality (as seen in this article) of an element or module.

]]>
https://frontendmasters.com/blog/adaptive-alerts-a-css-scroll-state-use-case/feed/ 0 6397