Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Automating Design Systems: Tips And Resources For Getting Started

      August 6, 2025

      OpenAI releases two open weight reasoning models

      August 6, 2025

      Accelerate tool adoption with a developer experimentation framework

      August 6, 2025

      UX Job Interview Helpers

      August 5, 2025

      Yes, you can edit video like a pro on Linux – here are my 4 go-to apps

      August 6, 2025

      I tried Perplexity’s new reservation feature, and it surprised me with new dining spots to try

      August 6, 2025

      Your Samsung TV is getting a huge feature upgrade – 3 AI tools launching right now

      August 6, 2025

      This multi-card reader is one of the best investments I’ve made for my creative workflow

      August 6, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Fluent Object Operations with Laravel’s Enhanced Helper Utilities

      August 6, 2025
      Recent

      Fluent Object Operations with Laravel’s Enhanced Helper Utilities

      August 6, 2025

      Record and Replay Requests With Laravel ChronoTrace

      August 6, 2025

      How to Write Media Queries in Optimizely Configured Commerce (Spire)

      August 6, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Battlefield 6 Developers Confirm AI Bots Will Auto-fill Servers If Player Count Drops

      August 6, 2025
      Recent

      Battlefield 6 Developers Confirm AI Bots Will Auto-fill Servers If Player Count Drops

      August 6, 2025

      Canon imageFORMULA R40 Driver for Windows 11, 10 (Download)

      August 6, 2025

      Microsoft to End Support for Visual Studio 2015 This October

      August 6, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Bringing Back Parallax With Scroll-Driven CSS Animations

    Bringing Back Parallax With Scroll-Driven CSS Animations

    August 6, 2025

    For a period in the 2010s, parallax was a guaranteed way to make your website “cool”. Indeed, Chris Coyier was writing about it as far back as 2008.

    For those unfamiliar with the concept, parallax is a pattern in which different elements of a webpage move at varying speeds as the user scrolls, creating a three-dimensional, layered appearance. A true parallax effect was once only achievable using JavaScript. However, scroll-driven animations have now given us a CSS-only solution, which is free from the main-thread blocking that can plague JavaScript animations.

    Parallax may have become a little cliché, but I think it’s worth revisiting with this new CSS feature.

    Note: Scroll-driven animations are available on Chrome, Edge, Opera, and Firefox (behind a feature flag) at the time of writing. Use a supported browser when following this tutorial.

    Starting code

    In this example, we will apply parallax animations to the background and icons within the three “hero” sections of a universe-themed webpage. We’ll start with some lightly styled markup featuring alternating hero and text sections while including some space-related nonsense as placeholder content.

    CodePen Embed Fallback

    Adding initial animations

    Let’s add an animation to the background pattern within each hero section to modify the background position.

    @keyframes parallax {
      from {
        background-position: bottom 0px center;
      }
      to {
        background-position: bottom -400px center;
      }
    }
    
    section.hero {
      /* previous code */
    + animation: parallax 3s linear;
    }

    Here we use the keyframes CSS rule to create a start and end position for the background. Then we attach this animation to each of our hero sections using the animation property.

    By default, CSS animations are duration-based and run when the specified selector is loaded in the DOM. If you refresh your browser, you will see the animation running for three seconds as soon as the page loads.

    We do not want our animation to be triggered immediately. Instead, we intend to use the page’s scroll position as a reference to calculate the animation’s progress.

    Scroll-driven animations provide two new animation timeline CSS functions. These additions, view() and scroll(), tell the browser what to reference when calculating the progress of a CSS animation. We will use the view() function later, but for now, let’s focus on scroll(). The scroll progress timeline couples the progression of an animation to the user’s scroll position within a scroll container. Parameters can be included to change the scroll axis and container element, but these are not necessary for our implementation.

    Let’s use a scroll progress timeline for our animation:

    section.hero {
      /* previous code */
    - animation: parallax 3s linear;
    + animation: parallax linear;
    + animation-timeline: scroll(); 
    }

    If you refresh the page, you will notice that as you scroll down, the position of the background of each hero section also changes. If you scroll back up, the animation reverses. As a bonus, this CSS animation is handled off the main thread and thus is not subject to blocking by any JavaScript that may be running.

    Using the view progress timeline

    Now let’s add a new parallax layer by animating the header text and icons within each hero section. This way, the background patterns, headers, and main page content will all appear to scroll at different speeds. We will initially use the scroll() CSS function for the animation timeline here as well.

    @keyframes float {
      from {
        top: 25%;
      }
      to {
        top: 50%;
      }
    }
    
    .hero-content {
      /* previous code */
    + position: absolute;
    + top: 25%;
    + animation: float linear;
    + animation-timeline: scroll(); 
    }

    That’s not quite right. The animation for the sections further down the page is nearly done by the time they come into view. Luckily, the view animation timeline solves this problem. By setting the animation-timeline property to view(), our animation progresses based on the position of the subject within the scrollport, which is the part of the container that is visible when scrolling. Like the scroll animation timeline, scrolling in reverse will also reverse the animation.

    Let’s try changing our animation timeline property for the hero text:

    .hero-content {
      /* previous code */
    - animation-timeline: scroll(); 
    + animation-timeline: view(); 
    }

    That looks pretty good, but there is a problem with the header content flashing into the view when scrolling back up the document. This is because the view timeline is calculated based on the original, pre-animation positioning of the subject element.

    We can solve this by adding an inset parameter to the view() function. This adjusts the size of the container in which the animation will take place. According to MDN’s documentation, the “inset is used to determine whether the element is in view which determines the length of the animation timeline. In other words, the animation lasts as long as the element is in the inset-adjusted view.”

    So, by using a negative value, we make the container larger than the window and trigger the animation to start a little before and end a little after the subject is visible. This accounts for the fact that the subject moves during the animation.

    - animation-timeline: view();
    + animation-timeline: view(-100px);

    Now both the text and background animate smoothly at different speeds.

    CodePen Embed Fallback

    Adjusting animations using animation ranges

    So far, we have employed both scroll and view progress timelines. Let’s look at another way to adjust the start and end timing of the animations using the animation-range property. It can be used to modify where along the timeline the animation will start and end.

    We’ll start by adding a view() timeline animation to the #spaceship emoji:

    @keyframes launch {
      from {
        transform: translate(-100px, 200px);
      }
      to {
        transform: translate(100px, -100px);
      }
    }
    
    #spaceship {
      animation: launch;
      animation-timeline: view();
    }

    Again, we see the emoji returning to its 0% position once its original unanimated position is outside of the scrollport.

    As discussed before, animations are based on the original pre-animation position of the subject. Previously, we solved this by adding an inset parameter to the view() function. We can also adjust the animation range and tell our animation to continue beyond 100% of the animation timeline without having to manipulate the inset of the view timeline itself.

    #spaceship {
      animation: launch;
      animation-timeline: view();
    + animation-range: 0% 120%;
    }

    Now the animation continues until we have scrolled an extra 20% beyond the calculated scroll timeline’s normal endpoint.

    Let’s say that we want to add an animation to the #comet emoji, but we don’t want it to start animating until it has passed 4rem from the bottom of the scrollport:

    @keyframes rotate {
      from {
        transform: rotate(0deg) translateX(100px);
      }
      to {
        transform: rotate(-70deg) translateX(0px);
      }
    }
    
    #comet {
      animation: rotate linear;
      transform-origin: center 125px;
      animation-timeline: view();
      animation-range: 4rem 120%;
    }

    Here we see the “delayed” animation in action:

    We can also combine animation ranges to run completely different animations at different points within the same timeline! Let’s illustrate this by combining animation ranges for the #satellite icon at the top of the page. The result is that the first animation runs until the icon passes 80% of the scrollport, then the second animation takes over for the final 20%.

    @keyframes orbit-in {
      0% {
        transform: rotate(200deg);
      }
      100% {
        transform: rotate(0deg);
      }
    }
    
    @keyframes orbit-out {
      0% {
        transform: translate(0px, 0px);
      }
      100% {
        transform: translate(-50px, -15px);
      }
    }
    
    #satellite {
      animation: orbit-in linear, orbit-out ease;
      animation-timeline: view();
      animation-range: 0% 80%, 80% 110%;
    }

    Fallbacks and accessibility

    Our webpage features numerous moving elements that may cause discomfort for some users. Let’s consider accessibility for motion sensitivities and incorporate the prefers-reduced-motion CSS media feature.

    There are two possible values: no-preference, and reduce. If we want to fine-tune the webpage with animations disabled by default and then enhance each selector with animations and associated styles, then we can use no-preference to enable them.

    @media (prefers-reduced-motion: no-preference) {
      .my-selector {
        position: relative;
        top: 25%;
        animation: cool-animation linear;
        animation-timeline: scroll(); 
      }
    }

    For us, however, the webpage content and images will still all be visible if we disable all animations simultaneously. This can be done concisely using the reduce option. It’s important to note that this sort of blanket approach works for our situation, but you should always consider the impact on your specific users when implementing accessibility features.

    @media (prefers-reduced-motion: reduce) {
      .my-selector {
        animation: none !important;
      }
    }

    In addition to considering accessibility, we should also take into account that scroll-driven animations are not supported by all browsers at the time of writing. If we care a lot about users seeing our animations, we can add a polyfill (direct link) to extend this functionality to currently unsupported browsers. This, however, will force the animation to run on the main thread.

    Alternatively, we could decide that performance is important enough to skip the animations on unsupported browsers, thereby keeping the main thread clear. In this case, we can use the @supports selector and include the styles only on supported browsers.

    Here is the final code with everything, including the polyfill and reduced motion fallback:

    CodePen Embed Fallback

    Conclusion

    There we go, we just re-created a classic web effect with scroll-driven animations using scroll and view progress timelines. We also discussed some of the parameters that can be used to adjust animation behavior. Whether or not parallax is your thing, I like the idea that we can use a modern approach that is capable of doing what we could before… only better with a dash of progressive enhancement.

    More information

    • Unleash the Power of Scroll-Driven Animations
    • animation-timeline (CSS-Tricks)
    • CSS scroll-driven animations (MDN)
    • Scroll-driven Animations Demo Site (Bramus)

    Bringing Back Parallax With Scroll-Driven CSS Animations originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleBuilding Aether 1: Sound Without Boundaries
    Next Article Firefly AIBOX-3588S Running Linux: Access Remote File Systems Over SSH with SSHFS

    Related Posts

    News & Updates

    Yes, you can edit video like a pro on Linux – here are my 4 go-to apps

    August 6, 2025
    News & Updates

    I tried Perplexity’s new reservation feature, and it surprised me with new dining spots to try

    August 6, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    CVE-2025-43026 – HP Support Assistant Privilege Escalation Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    Night Light Broken on Dell Arm PCs—Qualcomm Chip to Blame

    Operating Systems

    CVE-2025-49507 – LoftOcean CozyStay Deserialization of Untrusted Data Object Injection Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-7259 – MongoDB Server Duplicate _id Field Denial of Service

    Common Vulnerabilities and Exposures (CVEs)

    Highlights

    Free Nextjs Landing Page Templates & Examples

    April 3, 2025

    Post Content Source: Read More 

    Built to Move: A Closer Look at the Animations Behind Eduard Bodak’s Portfolio

    July 29, 2025

    Technology Innovation Institute TII Releases Falcon-H1: Hybrid Transformer-SSM Language Models for Scalable, Multilingual, and Long-Context Understanding

    May 22, 2025

    A Step-by-Step Coding Guide to Building a Gemini-Powered AI Startup Pitch Generator Using LiteLLM Framework, Gradio, and FPDF in Google Colab with PDF Export Support

    April 7, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.