Tag: Scroll

Scroll Shadows With JavaScript

Scroll shadows are when you can see a little inset shadow on elements if (and only if) you can scroll in that direction. It’s just good UX. You can actually pull it off in CSS, which I think is amazing and one of the great CSS tricks. Except… it just doesn’t work on iOS Safari. It used to work, and then it broke in iOS 13, along with some other useful CSS things, with no explanation why and has never been fixed.

So, now, if you really want scroll shadows (I think they are extra useful on mobile browsers anyway), it’s probably best to reach for JavaScript.

Here’s a pure CSS example so you can see it work in all browsers except iOS Safari. Screenshots:

I’m bringing this up now because I see Jonnie Hallman is blogging about tit again. He mentioned it as an awesome little detail back in May. There are certain interfaces where scroll shadows really extra make sense.

Taking a step back, I thought about the solution that currently worked, using scroll events. If the scroll area has scrolled, show the top and left shadows. If the scroll area isn’t all the way scrolled, show the bottom and right shadows. With this in mind, I tried the simplest, most straight-forward, and least clever approach by putting empty divs at the top, right, bottom, and left of the scroll areas. I called these “edges”, and I observed them using the Intersection Observer API. If any of the edges were not intersecting with the scroll area, I could assume that the edge in question had been scrolled, and I could show the shadow for that edge. Then, once the edge is intersecting, I could assume that the scroll area has reached the edge of the scroll, so I could hide that shadow.

Clever clever. No live demo, unfortunately, but read the post for a few extra details on the implementation.

Other JavaScript-powered examples

I do think if you’re going to do this you should go the IntersectionObserver route though. Would love to see someone port the best of these ideas all together (wink wink).


The post Scroll Shadows With JavaScript appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,

2021 Scroll Survey Report

Here’s a common thought and question: how do browsers prioritize what they work on? We get little glimpses of it sometimes. We’re told to “star issues” in bug trackers to signal interest. We’re told to get involved in GitHub threads for spec issues. We’re told they do read the blog posts. And, sometimes, we get to see the results of surveys. Chrome ran a survey about scrolling on the web back in April and has published the results with an accompanying a blog post.

“Scrolling” is a big landscape:

From our research, these difficulties come from the multitude of use cases for scroll. When we talk about scrolling, that might include:

According to the results, dang near half of developers are dissatisfied with scrolling on the web, so this is a metric Google devs want to change and they will prioritize it.

To add to the list above, I think even smooth scrolling is a little frustrating in how you can’t control the speed or other behaviors of it. For example, you can’t say “smooth scroll an on-page jump-down link, but don’t smooth scroll a find-on-page jump.”

And that’s not to mention scroll snapping, which is another whole thing with the occasional bug. Speaking of which, Dave had an idea on the show the other day that was pretty interesting. Now that scroll snapping is largely supported, even on desktop, and feels pretty smooth for the most part, should we start using it more liberally, like on whole page sections? Maybe even like…

/* Reset stylesheet */ main, section, article, footer {   scroll-snap-align: start; }

I’ve certainly seen scroll snapping in more places. Like this example from Scott Jehl where he was playing with scroll snapping on fixed table headers and columns. It’s a very nice touch:

Direct Link to ArticlePermalink


The post 2021 Scroll Survey Report appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Yet Another Mobile Context Menu With No Indication it Can Scroll

Remember Tyler Hall’s personal story of a UX moment where the popup sharing context menu on iOS had no visible indication that the content inside was scrollable? The thing his mom wanted to do seemed impossible iOS isn’t alone here — Terence Eden documents essentially the same problem on Android:

I tried sharing a website using Google Chrome for Android. I hit the share button, and a panel popped-up from the bottom of the screen.

Hmmm. It didn’t have the share destination that I wanted. It was early in the morning – when I’m not at my cognitive best – and I was stumped. There is nothing on this screen – other than the icons – to tell me how I can interact with it. There’s no scrollbar, no handle, no “more” icon, nothing.

I would think even just fairly subtle “scroll shadows” would go a long way in both cases, but some serious user testing should be in order here.

Android menu with no indication of scrolling potential.

iOS menu with no indication of scrolling potential.


The post Yet Another Mobile Context Menu With No Indication it Can Scroll appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Practical Use Cases for Scroll-Linked Animations in CSS with Scroll Timelines

The Scroll-Linked Animations specification is an upcoming and experimental addition to CSS. Using the @scroll-timeline at-rule and animation-timeline property this specification provides you can control the time position of regular CSS Animations by scrolling.

In this post, we take a look at some practical use cases where scroll-linked animations come in handy, replacing a typical JavaScript approach.

👨‍🔬 The CSS features described in this post are still experimental and not finalized at all. The are not supported by any browser at the time of writing, except for Chromium ≥ 89 with the #experimental-web-platform-features flag enabled.

CSS Scroll-Linked Animations, a quick primer

With CSS Scroll-Linked Animations, you can drive a CSS animation by scroll: as you scroll up or down inside a scroll container, the linked CSS animation will advance or rewind. Best of all is that this is all running off main thread, on the compositor.

You need three things to implement a basic scroll-linked animation:

  1. a CSS animation
  2. a scroll timeline
  3. a link between both

CSS animation

This is a regular CSS Animation like we already know:

@keyframes adjust-progressbar {   from {     transform: scaleX(0);   }   to {     transform: scaleX(1);   } }

As you normally do, attach it to an element using the animation property:

#progressbar {   animation: 1s linear forwards adjust-progressbar; }

Scroll timeline

The scroll timeline allows us to map the scroll distance to the animation progress. In CSS, we describe this with the CSS @scroll-timeline at-rule.

@scroll-timeline scroll-in-document-timeline {   source: auto;   orientation: vertical;   scroll-offsets: 0%, 100%; }

This at-rule consists of descriptors, including:

  1. The source describes the scrollable element whose scrolling triggers the activation and drives the progress of the timeline. By default, this is the entire document.
  2. The orientation determines the scrolling direction that should trigger the animation. By default, this is vertical.
  3. The scroll-offsets property is an array of key points that describe the range in which the animation should be active. It can be absolute values (e.g. percentages and lengths) or element-based.

A previous version of the specification required you to also set a time-range descriptor. This descriptor has been removed and will automatically take over the animation-duration from the linked animation. You may still see traces of it in the demos, but you can safely ignore it.

To associate our @scroll-timeline with our CSS animation, we use the new animation-timeline CSS property, and have it refer to the timeline’s name.

#progressbar {   animation: 1s linear forwards adjust-progressbar;   animation-timeline: scroll-in-document-timeline; /* 👈 THIS! */ }

With that set up the adjust-progressbar animation won’t run automatically on page load, but will only advance as we scroll down the page.

For a more in-depth introduction to @scroll-timeline please refer to Part 1 and Part 2 of my series on the future of scroll-linked animations.

The first post looks at each descriptor/property in more detail, explaining them with an example to go along with them, before covering many more interesting demos.

The second post digs even deeper, looking into Element-Based Offsets, which allow us to drive an animation as an element appears into and disappears from the scrollport as we scroll.

An example of what you can achieve with CSS Scroll-Linked Animations using Element-Based Offsets.

Practical use cases

Apart from the progress par demo above, there are a few more use cases or scenarios where scroll-linked animations can replace a solution typically implemented using JavaScript.

  1. parallax header
  2. image reveal/hide
  3. typing animation
  4. carousel indicators
  5. scrollspy

Parallax header

A typical use case for Scroll-Linked Animations is a parallax effect, where several sections of a page seem to have a different scrolling speed. There’s a way to create these type of effects using only CSS, but that requires mind-bending transform hacks involving translate-z() and scale().

Inspired upon the Firewatch Header—which uses the mentioned transform hack—I created this version that uses a CSS scroll timeline:

Compared to the original demo:

  • The markup was kept, except for that extra .parallax__cover that’s no longer needed.
  • The <body> was given a min-height to create some scroll-estate.
  • The positioning of the .parallax element and its .parallax_layer child elements was tweaked.
  • The transform/perspective-hack was replaced with a scroll timeline.

Each different layer uses the same scroll timeline: scroll over a distance of 100vh.

@scroll-timeline scroll-for-100vh {   time-range: 1s;   scroll-offsets: 0, 100vh; }  .parallax__layer {   animation: 1s parallax linear;   animation-timeline: scroll-for-100vh; }

What’s different between layers is the distance that they move as we scroll down:

  • The foremost layer should stay in place, eg. move for 0vh.
  • The last layer should should move the fastest, e.g. 100vh.
  • All layers in between are interpolated.
@keyframes parallax {   to {     transform: translateY(var(--offset));   } }  .parallax__layer__0 {   --offset: 100vh; }  .parallax__layer__1 {   --offset: 83vh; }  .parallax__layer__2 {   --offset: 67vh; }  .parallax__layer__3 {   --offset: 50vh; }  .parallax__layer__4 {   --offset: 34vh; }  .parallax__layer__5 {   --offset: 17vh; }  .parallax__layer__6 {   --offset: 0vh; }

As the foremost layers move over a greater distance, they appear to move faster than the lower layers, achieving the parallax effect.

Image reveal/hide

Another great use-case for scroll linked animations is an image reveal: an image slides into view as it appears.

By default, the image is given an opacity of 0 and is masked using a clip-path:

#revealing-image {   opacity: 0;   clip-path: inset(45% 20% 45% 20%); }

In the end-state we want the image to be fully visible, so we sent the end-frame of our animation to reflect that:

@keyframes reveal {   to {     clip-path: inset(0% 0% 0% 0%);     opacity: 1;   } }

By using element-based offsets as our scroll timeline offsets, we can have it so thar the image begins to appear only when the image itself slides into view.

@scroll-timeline revealing-image-timeline {   scroll-offsets:     selector(#revealing-image) end 0.5,     selector(#revealing-image) end 1   ; }  #revealing-image {   animation: reveal 1s linear forwards;   animation-timeline: revealing-image-timeline; }

😵 Can’t follow with those element-based offsets? This visualization/tool has got you covered.

Typing animation

As CSS scroll timelines can be linked to any existing CSS animation, you can basically take any CSS Animation demo and transform it. Take this typing animation for example:

With the addition of a scroll timeline and the animation-timeline property, it can be adjusted to “type on scroll”:

Note that to create some scroll-estate the <body>was also given a height of 300vh.

Using a different animation, the code above can easily be adjusted to create a zoom on scroll effect:

I can see these two working great for article intros.

One of the components of a carousel (aka slider) is an indicator that exposes how many slides it contains, as well as which slide is currently active. This is typically done using bullets.

This again is something we will be able to achieve using a CSS scroll timeline, as shown in this demo created by Fabrizio Calderan:

The active state bullet is injected via .slider nav::before and has an animation set that moves it over the other bullets

/* Styling of the dots */ .slider nav::before, .slider a {   inline-size: 1rem;   aspect-ratio: 1;   border-radius: 50%;   background: #9bc; }  /* Positioning of the active dot */ .slider nav::before {   content: "";   position: absolute;   z-index: 1;   display: block;   cursor: not-allowed;   transform: translateX(0);   animation: dot 1s steps(1, end) 0s forwards; }  /* Position over time of the active dot */ @keyframes dot {   0%      { transform: translateX(0); }   33%      { transform: translateX(calc((100% + var(--gap)) * 1)); }   66%      { transform: translateX(calc((100% + var(--gap)) * 2)); }    100%      { transform: translateX(calc((100% + var(--gap)) * 3)); } }

By attaching a @scroll-timeline onto the slider, the dot that indicates the active state can move as you scroll:

@scroll-timeline slide {   source: selector(#s);   orientation: inline;  }  .slider nav::before {   /* etc. */   animation-timeline: slide; }

The dot only moves after the slide has snapped to its position thanks to the inclusion of a steps() function in the animation. When removing it, it becomes more clear how the dot moves as you scroll

💡 This feels like the final missing piece to Christian Shaefer’s CSS-only carousel.

ScrollSpy

Back in early 2020, I created a sticky table of contents with scrolling active states. The final part to creating the demo was to use IntersectionObserver to set the active states in the table of contents (ToC) as you scroll up/down the document.

Unlike the carousel indicators demo from above we can’t simply get there by moving a single dot around, as it’s the texts in the ToC that get adjusted. To approach this situation, we need to attach two animations onto each element in the ToC:

  1. The first animation is to visually activate the ToC item when the proper section comes into view at the bottom edge of the document.
  2. The second animation is to visually deactivate the ToC item when the proper section slides out of view at the top edge of the document.
.section-nav li > a {   animation:     1s activate-on-enter linear forwards,     1s deactivate-on-leave linear forwards; }

As we have two animations, we also need to create two scroll timelines, and this for each section of the content. Take the #introduction section for example:

@scroll-timeline section-introduction-enter {   source: auto;   scroll-offsets:     selector(#introduction) end 0,     selector(#introduction) end 1; }  @scroll-timeline section-introduction-leave {   source: auto;   scroll-offsets:     selector(#introduction) start 1,     selector(#introduction) start 0; }

Once both of these timelines are linked to both animations, everything will work as expected:

.section-nav li > a[href"#introduction"] {   animation-timeline:     section-introduction-enter,     section-introduction-leave; }

In closing

I hope I have convinced you of the potential offered by the CSS Scroll-linked Animations specification. Unfortunately, it’s only supported in Chromium-based browsers right now, hidden behind a flag.

Given this potential, I personally hope that—once the specification settles onto a certain syntax—other browser vendors will follow suit. If you too would like to see Scroll-Linked Animations land in other browsers, you can actively star/follow the relevant browser issues.

By actively starring issues, us developers can signal our interest into these features to browser vendors.


The post Practical Use Cases for Scroll-Linked Animations in CSS with Scroll Timelines appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Maps Scroll Wheel Fix

This blog post by Steve Fenton came across my feeds the other day. I’d never heard of HERE maps before, but apparently they are embeddable somehow, like Google Maps. The problem is that you zoom and and out of HERE maps with the scrollwheel. So imagine you’re scrolling down a page, your cursor (or finger) ends up on the HERE map, and now you can’t continue scrolling down the page because that scrolling event is captured by the map and turns into map zooming.

Steve’s solution: put a “coverer” <div> over the map when a scroll event starts on the window, and remove it after a short delay (when scrolling “stops”). That solution resonates with me, as not only have I coded solutions like that in the past for embedded maps, we have a solution like that in place on CodePen today. On CodePen, you can resize the “preview” window, which is an <iframe> of the code you write. If you drag too swiftly, your mouse cursor (or touch event) might trigger movement off of the draggable element, possible onto the <iframe> itself. If that happens, the <iframe> will swallow the event, and the resizing you are trying to do stops working correctly. To prevent this, we put a “covered” <div> over top the <iframe> while you are dragging, and remove it when you stop dragging.

Thinking of maps though, it reminds me Brad Frost’s Adaptive Maps idea documented back in 2012. The idea is that embedding a map on a small screen mobile device just isn’t a good idea. Space is cramped, they can slow down page load, and, like Steve experienced nearly a decade later, they can mess with users scrolling through the page. Brads solution is to serve an image of a map (which can still be API-driven) conditionally for small screens with a “View Map” link that takes them to a full-screen map experience, probably within the map native app itself. Large screens can still have the interactive map, although, I might argue that having the image-that-links-to-map-service might be a smart pattern for any browser with less technical debt.


The post Maps Scroll Wheel Fix appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

How to Create a Shrinking Header on Scroll Without JavaScript

Imagine a header of a website that is nice and thick, with plenty of padding on top and bottom of the content. As you scroll down, it shrinks up on itself, reducing some of that padding, making more screen real estate for other content.

Normally you would have to use some JavaScript to add a shrinking effect like that, but there’s a way to do this using only CSS since the introduction of position: sticky.

Let me just get this out there: I’m generally not a fan of sticky headers. I think they take up too much of the screen’s real estate. Whether or not you should use sticky headers on your own site, however, is a different question. It really depends on your content and whether an ever-present navigation adds value to it. If you do use it, take extra care to avoid inadvertently covering or obscuring content or functionality with the sticky areas — that amounts to data loss.

Either way, here’s how to do it without JavaScript, starting with the markup. Nothing complicated here — a <header> with one descendant <div> which, intern, contains the logo and navigation.

<header class="header-outer">   <div class="header-inner">     <div class="header-logo">...</div>     <nav class="header-navigation">...</nav>   </div> </header>

As far as styling, we’ll declare a height for the parent <header> (120px) and set it up as a flexible container that aligns its descendant in the center. Then, we’ll make it sticky.

.header-outer {   display: flex;   align-items: center;   position: sticky;   height: 120px; }

The inner container contains all the header elements, such as the logo and the navigation. The inner container is in a way the actual header, while the only function of the parent <header> element is to make the header taller so there’s something to shrink from.

We’ll give that inner container, .header-inner, a height of 70px and make it sticky as well.

.header-inner {   height: 70px;   position: sticky;   top: 0;  }

That top: 0? It’s there to make sure that the container mounts itself at the very top when it becomes sticky.

Now for the trick! For the inner container to actually stick to the “ceiling” of the page we need to give the parent <header> a negative top value equal to the height difference between the two containers, making it stick “above” the viewport. That’s 70px minus 120px, leaving with with — drumroll, please — -50px. Let’s add that.

.header-outer {   display: flex;   align-items: center;   position: sticky;   top: -50px; /* Equal to the height difference between header-outer and header-inner */   height: 120px; }

Let’s bring it all together now. The <header> slides out of frame, while the inner container places itself neatly at the top of the viewport.

We can extend this to other elements! How about a persistent alert?

While it’s pretty awesome we can do this in CSS, it does have limitations. For example, the inner and outer containers use fixed heights. This makes them vulnerable to change, like if the navigation elements wrap because the number of menu items exceeds the amount of space.

Another limitation? The logo can’t shrink. This is perhaps the biggest drawback, since logos are often the biggest culprit of eating up space. Perhaps one day we’ll be able to apply styles based on the stickiness of an element…


The post How to Create a Shrinking Header on Scroll Without JavaScript appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Going From Solid to Knockout Text on Scroll

Here’s a fun CSS trick to show your friends: a large title that switches from a solid color to knockout text as the background image behind it scrolls into place. And we can do it using plain ol’ HTML and CSS!

This effect is created by rendering two containers with fixed <h1> elements. The first container has a white background with knockout text. The second container has a background image with white text. Then, using some fancy clipping tricks, we hide the first container’s text when the user scrolls beyond its boundaries and vice-versa. This creates the illusion that the text background is changing.

Before we begin, please note that this won’t work on older versions of Internet Explorer. Also, fixed background images can be cumbersome on mobile WebKit browsers. Be sure to think about fallback behavior for these circumstances.

Setting up the HTML

Let’s start by creating our general HTML structure. Inside an outer wrapper, we create two identical containers, each with an <h1> element that is wrapped in a .title_wrapper.

<header>    <!-- First container -->   <div class="container container_solid">     <div class="title_wrapper">       <h1>The Great Outdoors</h1>     </div>   </div>    <!-- Second container -->   <div class="container container_image">     <div class="title_wrapper">       <h1>The Great Outdoors</h1>     </div>   </div>  </header>

Notice that each container has both a global .container class and its own identifier class — .container_solid and .container_image, respectively. That way, we can create common base styles and also target each container separately with CSS.

Initial styles

Now, let’s add some CSS to our containers. We want each container to be the full height of the screen. The first container needs a solid white background, which we can do on its .container_solid class. We also want to add a fixed background image to the second container, which we can do on its .container_image class.

.container {   height: 100vh; }  /* First container */ .container_solid {   background: white; }  /* Second container */ .container_image {   /* Grab a free image from unsplash */   background-image: url(/path/to/img.jpg);   background-size: 100vw auto;   background-position: center;   background-attachment: fixed; }

Next, we can style the <h1> elements a bit. The text inside .container_image can simply be white. However, to get knockout text for the <h1> element inside container_image, we need to apply a background image, then reach for the text-fill-color and background-clip CSS properties to apply the background to the text itself rather than the boundaries of the <h1> element. Notice that the <h1> background has the same sizing as that of our .container_image element. That’s important to make sure things line up.

.container_solid .title_wrapper h1 {   /* The text background */   background: url(https://images.unsplash.com/photo-1575058752200-a9d6c0f41945?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ);   background-size: 100vw auto;   background-position: center;      /* Clip the text, if possible */   /* Including -webkit` prefix for bester browser support */   /* https://caniuse.com/text-stroke */   -webkit-text-fill-color: transparent;   text-fill-color: transparent;   -webkit-background-clip: text;   background-clip: text;      /* Fallback text color */   color: black; }  .container_image .title_wrapper h1 {   color: white; }

Now, we want the text fixed to the center of the layout. We’ll add fixed positioning to our global .title_wrapper class and tack it to the vertical center of the window. Then we use text-align to horizontally center our <h1> elements.

.header-text {   display: block;   position: fixed;    margin: auto;   width: 100%;   /* Center the text wrapper vertically */   top: 50%;   -webkit-transform: translateY(-50%);       -ms-transform: translateY(-50%);           transform: translateY(-50%); }  .header-text h1 {   text-align: center; }

At this point, the <h1> in each container should be positioned directly on top of one another and stay fixed to the center of the window as the user scrolls. Here’s the full, organized, code with some shadow added to better see the text positioning.

Clipping the text and containers

This is where things start to get really interesting. We only want a container’s <h1> to be visible when its current scroll position is within the boundaries of its parent container. Normally this can be solved using overflow: hidden; on the parent container. However, with both of our <h1> elements using fixed positioning, they are now positioned relative to the browser window, rather than the parent element. In this case using overflow: hidden; will have no effect.

For the parent containers to hide fixed overflow content, we can use the CSS clip property with absolute positioning. This tells our browser hide any content outside of an element’s boundaries. Let’s replace the styles for our .container class to make sure they don’t display any overflowing elements, even if those elements use fixed positioning.

.container {   /* Hide fixed overflow contents */   clip: rect(0, auto, auto, 0);    /* Does not work if overflow = visible */   overflow: hidden;    /* Only works with absolute positioning */   position: absolute;    /* Make sure containers are full-width and height */   height: 100vh;   left: 0;   width: 100%; }

Now that our containers use absolute positioning, they are removed from the normal flow of content. And, because of that, we need to manually position them relative to their respective parent element.

.container_solid {   /* ... */    /* Position this container at the top of its parent element */   top: 0; }  .container_image {   /* ... */  /* Position the second container below the first container */   top: 100vh; }

At this point, the effect should be taking shape. You can see that scrolling creates an illusion where the knockout text appears to change backgrounds. Really, it is just our clipping mask revealing a different <h1> element depending on which parent container overlaps the center of the screen.

Let’s make Safari happy

If you are using Safari, you may have noticed that its render engine is not refreshing the view properly when scrolling. Add the following code to the .container class to force it to refresh correctly.

.container {   /* ... */    /* Safari hack */   -webkit-mask-image: -webkit-linear-gradient(top, #ffffff 0%,#ffffff 100%); }

Here’s the complete code up to this point.

Time to clean house

Let’s make sure our HTML is following accessibility best practices. Users not using assistive tech can’t tell that there are two identical <h1> elements in our document, but those using a screen reader sure will because both headings are announced. Let’s add aria-hidden to our second container to let screen readers know it is purely decorative.

<!-- Second container --> <div class="container container_image" aria-hidden="true">   <div class="title_wrapper">     <h1>The Great Outdoors</h1>   </div> </div>

Now, the world is our oyster when it comes to styling. We are free to modify the fonts and font sizes to make the text just how we want. We could even take this further by adding a parallax effect or replacing the background image with a video. But, hey, at that point, just be sure to put a little additional work into the accessibility so those who prefer less motion get the right experience.

That wasn’t so hard, was it?


The post Going From Solid to Knockout Text on Scroll appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

How to Use the Locomotive Scroll for all Kinds of Scrolling Effects

I was recently looking for a way to perform scrolling effects on a project and I stumbled on the Locomotive Scroll library. It lets you perform a variety of scrolling effects, like parallax and triggering/controlling animations at scroll points.

You might also call it a “smooth scrolling” library, but it doesn’t leverage native smooth scrolling — it does just the opposite by virtualizing scrolling and ensuring it’s always smooth. You could probably consider this “scrolljacking” so if you hate that generally, you might hate this, but UX research seems rather mixed on whether it’s actually bad or not. The homepage will give you a good sense of how it works and feels.

Let’s look at the basics of using Locomotive-Scroll JavaScript and how to leverage it to for delightful user experiences.

What is Locomotive Scroll?

Here’s what they say:

Locomotive scroll is a simple scroll library, built as a layer on top of ayamflow’s virtual-scroll, it provides smooth scrolling with support for parallax effects, toggling classes, and triggering event listeners when elements are in the viewport.

In other words, it detects when elements are in the viewport and then alters CSS transform property values on those elements to create scrolling effects.

Oftentimes scrolling effects are called parallax meaning some elements are made to look like they are deep in the background, making them appear to move slower than other elements that are closer to the foreground while scrolling is taking place. Imagine looking out the window from a moving car. The trees far away seems to slowly drift by where the fence right along the road zips quickly by. Sort of like the effect here in this pen from Sarah Drasner:

Here’s how it works

Locomotive Scroll works primarily through specific attributes in the HTML. Elements with these attributes trigger event listeners in JavaScript when they are in the viewport, then apply CSS transform values as inline styles.

There are two key attributes to always call upon Locomotive:

  • data-scroll: detects whether or not an element is in the viewport
  • data-scroll-container: wraps all the HTML content you want to watch for scrolling

Here’s what we’re talking about when we say that the transform property values are updated in the HTML as inline styles.

Notice how, as soon as an element with Locomotive’s data- attributes comes into the viewport, the CSS transform values are are updated.

Let’s set this up

We can use the library right as a <script> tag if we’d like. It’s on CDNs, so like:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/locomotive-scroll@3.5.4/dist/locomotive-scroll.css">  <script src="https://cdn.jsdelivr.net/npm/locomotive-scroll@3.5.4/dist/locomotive-scroll.min.js">

Now we look for the container and kick off the library:

const scroller = new LocomotiveScroll({   el: document.querySelector('[data-scroll-container]'),   smooth: true });

The library is on npm as well, so we can use it that way in our build instead with the typical npm install locomotive-scroll, then:

import LocomotiveScroll from 'locomotive-scroll';  const scroll = new LocomotiveScroll();

That means we could use them off Skypack too, like:

That’s really all there is to the setup! It’s pretty plug-and-play like that.

Here are some examples

You can probably think of some pretty nice use cases for something like this, but let’s go over a few examples where you might use Locomotive Scroll.

Let’s start with this one:

That HTML has all kinds of data- attributes going on in there. We’ve already looked at data-scroll and data-scroll-container. Here’s what the rest are and what they do:

  • data-scroll-section : Defines a scrollable section. For better performance, it’s a good idea to split pages into sections.
  • data-scroll-direction: Defines the vertical or horizontal direction that an element moves.
  • data-scroll-speed: Specifies the speed an element moves. A negative value reverses the direction, but only vertically, unless data-scroll-direction is applied on the same element.
  • data-scroll-sticky: Specifies an element that sticks to the viewport as long as the target element is still in view.
  • data-scroll-target: Targets a particular element. It takes in an ID selector, which is unique compared to the other attributes.

So, let’s say we are using the data-scroll-sticky attribute. We always have to set a data-scroll-target attribute as well, because the target element is usually the container holding the other elements.

<div class="container" id="stick" data-scroll-section >   <p data-scroll data-scroll-sticky data-scroll-target="#stick">     Look at me, I'm going to stick when you scroll pass me.   </p> </div>

Now that we’ve picked one apart, here are a couple of others:

You can also use LocoMotive-Scroll in other frameworks, too. Here’s an example in React:

Scroll aboard!

I can not emphasize the power of Locomotive Scroll enough. I needed to add scroll effects to a side project I was working on, and this was super quick and easy to use. I hope you’re able to use it on a project and experience how great it is for scrolling effects.


The post How to Use the Locomotive Scroll for all Kinds of Scrolling Effects appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Doom Damage Flash on Scroll

The video game Doom famously would flash the screen red when you were hit. Chris Johnson not only took that idea, but incorporated a bunch of the UI from Doom into this tounge-in-cheek JavaScript library called Doom Scroller. Get it? Like, doom scrolling, but like, Doom scrolling. It’s funny, trust me.

I extracted bits from Chris’ cool project to focus on the damage animation itself. The red flash is done in HTML and CSS. First, we create a full screen overlay:

#doom-damage {   background-color: red;   position: fixed;   top: 0;   left: 0;   width: 100%;   height: 100%;   opacity: 0;   pointer-events: none; }

Note that it’s not display: none. It’s much harder to animate that as we have to wait until the animation is completed to apply it. That’s because display isn’t animatable. It’s doable, just annoying.

To flash it, we’ll apply a class that does it, but only temporarily.

const damage = document.getElementById("doom-damage");  function doomTakeDamage() {   damage.classList.add("do-damage");   setTimeout(function () {     damage.classList.remove("do-damage");   }, 400); }

When that class activates, we’ll immediately turn the screen red (really giving it that shock appeal) and then fade the red away:

.do-damage {   background-color: red;   animation: 0.4s doom-damage forwards; }  @keyframes doom-damage {   0% {     opacity: 1;   }   100% {     opacity: 0;   } }

The next bit calls the function that does the damage flash. Essentially it tracks the current scroll position, and if it’s past the nextDamagePosition, it will red flash and reset the next nextDamagePostition one full screen height length away.

If you want to see all that, I’ve abstracted it into this Pen:


The post Doom Damage Flash on Scroll appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

That’s Just How I Scroll

How do you know a page (or any element on that page) scrolls? Well, if it has a scrollbar, that’s a pretty good indication. You might still have to scrapple with your client about “the fold” or whatever, but I don’t think anyone is confused at what a scrollbar is or what it indicates.

But let’s say there is no scrollbar. That’s super common. macOS hides scrollbars by default and only shows them during scroll. Most mobile browsers don’t have scrollbars, even if you attempt to force them with something like overflow: scroll;.

Why does this matter? If you don’t know an area is scrollable, you might miss out on important content or functionality.

I regularly think about the Perfectly Cropped story from Tyler Hall. There is a screen on iOS that has important functionality you need to scroll down to, but there is no indicator whatsoever that you can scroll there.

The result of that was Tyler’s mom literally not being able to find functionality she was used to. Not great.

There is an elaborate way to detect visible scrollbars and force them to be visible, but something about that rubs me the wrong way. It doesn’t honor a user’s preference (assuming it is the user’s preference), requires DOM manipulation tests, and uses vendor-prefixed CSS (which will probably live a long time, but has been standardized now, so maybe not forever).

I enjoy these approaches and by Chris Smith and his thinking:

My favorite are the shadow-based techniques. To me an inset shadow is a really clear indicator, as it makes it appear that content is flowing underneath and the shadow follows an edge that as a hint that you can scroll in that direction. Plus, you’ve got CSS control there so I’d think it could match whatever UI situation you’re in fairly easily.

It should be known though that it can be done entirely in CSS though, no JavaScript, and is one of the great CSS tricks.


The post That’s Just How I Scroll appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]