Luke Grech
Luke Grech

Reputation: 185

CSS Div Fade Scroll Styling

I can not seem to figure out how to style a scrollable div like the image below. Tutorials and examples that I found all fade the div out to the background colour, but I could not find one like this, does anyone have a solution? Does not need to have the inner divs and title as below, just needs to keep the white box unfaded, Thanks!

enter image description here

Upvotes: 7

Views: 18546

Answers (1)

Terry
Terry

Reputation: 66208

You can use CSS mask-image for browsers that support it (which is pretty common nowadays). Depending on your needs, JS might be needed (for example, if you wan to conditionally toggle the fading depending if the user has scrolled to the very top/bottom of the element).

To create a "bottom fadeout" effect, you can use this following CSS rule:

mask-image: linear-gradient(to bottom, black calc(100% - 48px), transparent 100%);

It basically says:

  • Create a linear gradient from the top going towards the bottom
  • When we are 48px away from the bottom, start changing color from black (see through when masking) to transparent (opaque when masking)

However, this only creates the bottom gradient. To mask both the top and the bottom, you will need to combine two gradients into one:

mask-image: linear-gradient(to bottom, transparent 0%, black 48px, black calc(100% - 48px), transparent 100%);

Now you ask: how do we hide the mask? Easy: if the color stops are spaced 0px apart, the mask will be black throughout, which will make it completely see through.

The JS part is simply toggling these color stops using classes. We use CSS custom properties that can be overridden by adding/removing a class. JS is used to sniff out the scroll position and toggle these classes. See proof-of-concept below:

function setClasses(el) {
  const isScrollable = el.scrollHeight > el.clientHeight;
  
  // GUARD: If element is not scrollable, remove all classes
  if (!isScrollable) {
    el.classList.remove('is-bottom-overflowing', 'is-top-overflowing');
    return;
  }
  
  // Otherwise, the element is overflowing!
  // Now we just need to find out which direction it is overflowing to (can be both).
  // One pixel is added to the height to account for non-integer heights.
  const isScrolledToBottom = el.scrollHeight < el.clientHeight + el.scrollTop + 1;
  const isScrolledToTop = isScrolledToBottom ? false : el.scrollTop === 0;
  el.classList.toggle('is-bottom-overflowing', !isScrolledToBottom);
  el.classList.toggle('is-top-overflowing', !isScrolledToTop);
}

document.querySelector('#content').addEventListener('scroll', (e) => {
  const el = e.currentTarget;
  setClasses(el);
});

setClasses(document.querySelector('#content'));
* {
  box-sizing: border-box;
}

body {
  padding: 0;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background-color: #2a9d8f;
  font-family: Arial, sans-serif;
}

.box {
  width: 50vw;
  height: 90vh;
  background-color: #fff;
  border-radius: 16px;
  padding: 32px;
  display: flex;
  flex-direction: column;
}

.box h1 {
  margin: 0;
}

#content {
  overflow-y: auto;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, black var(--top-mask-size, 0), black calc(100% - var(--bottom-mask-size, 0)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, black var(--top-mask-size, 0), black calc(100% - var(--bottom-mask-size, 0)), transparent 100%);
  --top-mask-size: 0px;
  --bottom-mask-size: 0px;
}

#content.is-top-overflowing {
  --top-mask-size: 48px !important;
}

#content.is-bottom-overflowing {
  --bottom-mask-size: 48px !important;
}
<article class="box">
  <h1>Lorem ipsum</h1>
  <section id="content">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel metus at tellus consectetur rhoncus. Aenean euismod eget mauris cursus tincidunt. Cras lectus dolor, suscipit nec porttitor a, tincidunt vel arcu. Etiam facilisis faucibus lectus
      vitae pharetra. Fusce euismod lacus sit amet consequat mattis. Aliquam suscipit metus a nulla suscipit varius. In tempor suscipit pretium.</p>
    <p>Vivamus aliquam eros eu orci finibus, id efficitur lorem placerat. Sed congue ipsum quis accumsan feugiat. Nunc imperdiet faucibus tellus, nec tincidunt ipsum dictum non. Quisque dui lacus, bibendum vitae lectus vel, vulputate tempus quam. Praesent
      ullamcorper ultricies felis, at fermentum massa gravida quis. Pellentesque mollis, urna sed vehicula elementum, dolor lorem congue ipsum, eget ullamcorper ex arcu nec mi. Suspendisse potenti. Morbi pharetra eu nisi quis laoreet. Donec ut quam id
      justo pulvinar volutpat eget ut magna.</p>
    <p>Etiam aliquam eleifend dignissim. Donec sagittis tincidunt quam, eget venenatis mi sollicitudin et. Vestibulum id lectus mi. Aenean enim sem, viverra at velit ut, posuere dapibus tellus. Ut accumsan mi eu lectus sollicitudin ornare. Morbi eu semper
      lacus lacus. Aenean id erat at nulla ornare consequat a at leo. In risus risus, blandit sit amet tortor sed, accumsan porttitor velit. Quisque interdum id ipsum quis convallis. Suspendisse vel pretium augue. Sed tincidunt, felis ut porta consectetur,
      mauris urna hendrerit diam, nec aliquet augue ante quis metus.</p>
  </section>
</article>

Upvotes: 21

Related Questions