Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Tue, 19 Mar 2024 22:44:44 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 225069128 Chill Scroll Snapping: Article Headers https://frontendmasters.com/blog/chill-scroll-snapping-article-headers/ https://frontendmasters.com/blog/chill-scroll-snapping-article-headers/#respond Tue, 19 Mar 2024 22:44:42 +0000 https://frontendmasters.com/blog/?p=1295 CSS has a feature called scroll snapping. A lot of the demos and examples, rightfully so, focus around things that benefit highly from it. For instance, an image slider, carousel, or grid of things that just beg to be aligned after scrolling.

But you don’t have to be in such a strict and rigid situation for scroll snapping to just be a nice touch. Instead you can think: is there anything on this page that would look or feel nice if when you scrolled nearby it would snap into place?

Maybe you have one of those “full viewport filling” headers to a page. If you added a scroll snapping point to the first element after that (e.g. .article-header + :first-child) then you could help users scroll right to it, whooshing past the giant header. Like:

Love it? Hate it? It’s just an idea. Some people really bristle against things that seem to take scrolling control away from them.

We could go a bit more chill.

What if just the headers of an article had scroll snapping points? That looks something like this, by the way:

html {
  scroll-snap-type: y mandatory;
}

main {
  h2, h3 {
    scroll-snap-stop: normal;
    scroll-snap-align: start;
  }
}

Now you’ve got these headers that just so happen to line up very nicely at the top of the screen when you get close to them:

Gotta tell ya there: don’t hate it.

Here’s a demo where the headers are scroll snap points, plus the header itself, plus the very first thing after the header. I’ve also used a smidge of scroll-margin-block-start to push the snap point a little bit away from the header giving it some breathing room.

main {
  max-width: 60ch;
  margin-inline: auto;
  h1,
  h2,
  > h1 + * {
    scroll-snap-stop: normal;
    scroll-snap-align: start;
  }
  > h1 + *,
  h2 {
    scroll-margin-block-start: 0.5rem;
  }
}

html {
  scroll-snap-type: y mandatory;
}

My main point isn’t “do exactly this”, it’s just that thinking about scroll snapping points in a design doesn’t have to be relegated to carousels. There is likely smaller and more chill opportunities to slap in snap point just to make something feel a bit more polished.

]]>
https://frontendmasters.com/blog/chill-scroll-snapping-article-headers/feed/ 0 1295
Scroll-Locked Dialogs https://frontendmasters.com/blog/scroll-locked-dialogs/ https://frontendmasters.com/blog/scroll-locked-dialogs/#comments Mon, 19 Feb 2024 19:01:09 +0000 https://frontendmasters.com/blog/?p=938 I just wrote about the <dialog> element, with some basic usage and things to watch for. It’s a great addition to the web platform.

Here’s another interesting thing we can do, connecting it to another one of my favorite new things on the web platform: :has(). (You can see I’ve been pretty into it lately.) I was reading Locking scroll with :has() from Robb Owen, and he gets into how you might want to prevent the page from scrolling when a modal is open.

To prevent from losing the user’s place in the page whilst that modal is open – particularly on mobile devices – it’s good practice to prevent the page behind it from scrolling. That’s a scroll lock.

I like that. The user can’t interact with what’s behind the modal anyway, as focus is trapped into a modal (that’s what a modal is). So, might as well make sure they don’t inadvertently scroll away. Without doing anything, scrolling is definitely not locked.

My first thought was actually… I wonder if overscroll-behavior on the dialog (and maybe the ::backdrop too?) would prevent that, but some quick testing didn’t seem to work.

So to scroll lock the page, as Robb did in his article, we can do by hiding the overflow on the body. Robb did it like this:

body:has(.lock-scroll) {
  overflow: hidden;
}

Which would then lock if the body had a <dialog class="lock-scroll"> in it. That’s fine, but what I like about <dialog> is that it can sorta always be in the DOM, waiting to open, if you like. If you did that here, it would mean the scroll is always locked, which you certainly don’t want.

Instead, I had a play like:

body {
  ...

  &:has(dialog[open]) {
    overflow: hidden;
  }
}

So this just locks scrolling only when the dialog is open. When you call the API like showModal, it toggles that open attribute, so it’s safe to use.

This works, see:

But… check out that content shift above. When we hide the overflow on the body, there is a possibility (depending on the browser/platform/version/settings and “overlay” scrollbars) that the scrollbars are taking up horizontal space, and the removal of them causes content to reflow.

Fortunately, there is yet another modern CSS feature to save us here, if we determine this to be a problem for the site we’re working on: scrollbar-gutter. If we set:

html {
  scrollbar-gutter: stable;
}

Then the page will reserve space for that scrollbar whether there is scrolling or not, thus there will be no reflow when the scrollbar pops in and out. Now we’re cookin’ — see:

I don’t think scrollbar-gutter is a home run, sadly. It leaves a blank strip down the side of the page (no inherited background) when the scrollbar isn’t there, which can look awkward. Plus, “centered” content can look less centered because of the reserved space. Just one of those situations where you have to pick which is less annoying to you 🫠.

Demo:

]]>
https://frontendmasters.com/blog/scroll-locked-dialogs/feed/ 2 938
How to fix the invisible scrollbar issue in iOS browsers https://frontendmasters.com/blog/how-to-fix-the-invisible-scrollbar-issue-in-ios/ https://frontendmasters.com/blog/how-to-fix-the-invisible-scrollbar-issue-in-ios/#respond Tue, 13 Feb 2024 20:45:25 +0000 https://frontendmasters.com/blog/?p=825 The page scrollbar in web browsers serves a useful function: The vertical position of the scrollbar thumb tells the user where they are in the page (their scroll position), while the size (height) of the scrollbar thumb tells them roughly how long the page is. Because scrollbars are useful, they should be clearly visible.

On Apple’s platforms, most notably on iOS, the page scrollbar is placed inside the viewport and laid on top of web content. In some cases, this can result in a poor contrast between the scrollbar thumb and the page background beneath it. One website that has this problem is WebKit Blog. The page scrollbar on that website in iOS browsers is almost completely invisible.

WebKit Blog article in Safari on iPhone
When scrolling a WebKit Blog article in mobile Safari, the scrollbar is not visible

I assure you that there is indeed a scrollbar present in the above screenshot, but don’t worry if you can’t spot it. Even a person with perfect vision could probably not say with certainty that they can see a scrollbar in that image. Let’s zoom in to take a closer look at this supposed scrollbar, and maybe we’ll manage to actually see it this time.

Close-up of scrollbar thumb
Even in this close-up, the scrollbar is only barely visible

The contrast ratio between the scrollbar (#fbfbfb) and page background (#f7f7f7) is 1.03:1. This is only a smidgen above the theoretical minimum of 1:1 (no contrast), which is what you get if you compare a color to itself. For all intents and purposes, the scrollbar is invisible.

Observe how the scrollbar “disappears” as soon as the user scrolls past the page header

The invisible scrollbar problem is not specific to iOS. It affects Apple’s other platforms as well. However, macOS users can fix this issue by setting the “Show scroll bars” option to “Always” in system settings (in the Appearance section), which causes the scrollbar to be placed in a separate “scrollbar gutter” outside of the viewport in the browser. There is no such option on iOS, so it’s up to the website to ensure that the page scrollbar is clearly visible.

Websites that are affected by the invisible scrollbar problem can fix this issue by adjusting how they apply CSS background colors to the page. In the future, it will be possible to fix this issue by coloring the scrollbar via the CSS scrollbar-color property, which currently isn’t supported in Safari. Apple has expressed support for this property, but the implementation in WebKit is blocked on a limitation in lower-level frameworks on Apple’s platforms.

html {
  /* A scrollbar with a gray thumb and a transparent track */
  /* Not yet supported in Safari and other WebKit-based iOS browsers */
  scrollbar-color: gray transparent;
}

Note that the non-standard CSS ::-webkit-scrollbar pseudo-elements are not supported on iOS, so they cannot be used as a fallback for the scrollbar-color property on that platform.

What is causing the invisible scrollbar

On WebKit Blog, the header and footer have a dark background, while the main content on the page has a light background. The website achieves this by applying a dark background color to the <body> element and a light background color to the <main> element.

body {
  background-color: #00253d; /* a dark color */
}

main {
  background-color: #f7f7f7; /* a light color */
}

Since the header and footer don’t have their own background color, they are transparent (in CSS, the background-color property defaults to the transparent value), so the <body> element’s dark background is visible through them, resulting in the following appearance:

<body>
  <header>
    <!-- has dark background color -->
  </header>
  <main>
    <!-- has light background color -->
  </main>
  <footer>
    <!-- has dark background color -->
  </footer>
</body>

The page scrollbar in web browsers on Apple’s platforms can have a light and dark appearance. The operating system automatically chooses the scrollbar appearance based on the background colors of the web page. Since WebKit Blog sets a dark background color on the <body> element, the system assumes that the page is mostly dark and chooses the light scrollbar appearance, resulting in an invisible scrollbar against the <main> element’s light background color.

How to fix this issue

The fix is rather simple. In order to get a dark scrollbar appearance, the page should avoid setting a dark background color on the <html> or <body> elements. In the case of WebKit Blog, the dark background color should be applied directly to the <header> and <footer> elements, while the light background color can be applied to the <main> element or the <body> element (either works).

body {
  background-color: #f7f7f7; /* a light color */
}

header,
footer {
  background-color: #00253d; /* a dark color */
}

I should note that this fix can affect the overscroll effect in some browsers. On WebKit blog, when the user scrolls to the top of the page, the overscroll animation creates an impression that the page header is being stretched. In Safari on macOS, this effect is preserved after applying the fix, but in Chrome it isn’t (I filed a Chromium issue for this).

Safari on the left, Chrome on the right

Some people may see this as a (minor) degradation in aesthetics, but I think that an invisible scrollbar is a much bigger problem for users, and websites should prioritize usability over elegance.

]]>
https://frontendmasters.com/blog/how-to-fix-the-invisible-scrollbar-issue-in-ios/feed/ 0 825
Heads Up on Custom Scrollbars. Chrome is Supporting the Standard Now, which Overrides The Old Pseudo Elements https://frontendmasters.com/blog/heads-up-on-custom-scrollbars-chrome-is-supporting-the-standard-now-which-overrides-the-old-pseudo-elements/ https://frontendmasters.com/blog/heads-up-on-custom-scrollbars-chrome-is-supporting-the-standard-now-which-overrides-the-old-pseudo-elements/#comments Wed, 31 Jan 2024 14:08:41 +0000 https://frontendmasters.com/blog/?p=696 There was quite a long period of time (say 2011-2024) where if you wanted to style scrollbars in CSS, your best bet was using the pseudo elements ::-webkit-scrollbar and friends (there were about 7 of them). That got you custom scrollbars in Safari and Chrome and offshoots. Firefox never supported those. They were never really standardized. But around 2018, Firefox started supporting scrollbar-color and scrollbar-width, where were (are) standardized.

These two groups of selectors didn’t interfere with each other, because browsers either supported one or the other. So for a bunch of years there, you could safely do something like:

.custom-scrollbars {
  /* For Safari, Chrome, and offshoots */
  &::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }
  &::-webkit-scrollbar-thumb {
    background: gray;
    border-radius: 8px;
  }
  &::-webkit-scrollbar-track {
    background: transparent;
  }

  /* For Firefox and offshoots */
  scrollbar-color: gray transparent;
  scrollbar-width: thin;
}

Now that Chrome 121 has dropped to stable, that’s changed. The presence of one of the new properties like scrollbar-color overrides the usage of the pseudo elements. So if you were styling like the above, all the sudden it’s likely that your scrollbars looked a bit different, because the styling possibilities are much more limited with the standardized properties.

For instance, the scrollbar-width property only really does auto, which ends up about 16px across, or thin which is 16px across. If you were doing super chunky scrollbars that went bigger than that, you’d see them get smaller. If you were doing super thin scrollbars, which may have looked more visually appropriate in constrained areas (although likely an accessibility issue), you’d see them get bigger.

As general advice, it’s probably best to transition yourself over to only using the more limited but standardized properties.

But if you’d like to stick it out with fancier but not standardized pseudo elements, you’ll need to ensure the new standardized properties do not apply in browsers supporting both (Chrome). So you could do:

.custom-scrollbars {
  /* Non-Standard, But More Styling-Capable Properties */
  &::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }
  &::-webkit-scrollbar-thumb {
    background: gray;
    border-radius: 8px;
  }
  &::-webkit-scrollbar-track {
    background: transparent;
  }

  /* Standardized Properties */
  @supports not selector(::-webkit-scrollbar) {
    scrollbar-color: gray transparent;
    scrollbar-width: thin;
  }
}

(Demo)

Looks like Bramus updated a great article on Scrollbar styling, showing off the @supports method.

Read that article for a couple of other nuanced little bits (with work arounds) like:

Note that setting the width or height of ::-webkit-scrollbar will force render an overlay scrollbar, effectively turning it into a classic scrollbar.

]]>
https://frontendmasters.com/blog/heads-up-on-custom-scrollbars-chrome-is-supporting-the-standard-now-which-overrides-the-old-pseudo-elements/feed/ 1 696
Draw on Scroll https://frontendmasters.com/blog/draw-on-scroll/ https://frontendmasters.com/blog/draw-on-scroll/#respond Mon, 29 Jan 2024 23:14:01 +0000 https://frontendmasters.com/blog/?p=685 Clever idea idea from Rauno Freiberg then explained by Brad Woods.

To create a line drawn on scroll effect, we render a line down the page. Then, use a clip-path to show only part of it. As the user scrolls, we translate the clip-path down the page.

]]>
https://frontendmasters.com/blog/draw-on-scroll/feed/ 0 685