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

      This week in AI dev tools: Gemini API Batch Mode, Amazon SageMaker AI updates, and more (July 11, 2025)

      July 11, 2025

      JFrog finds MCP-related vulnerability, highlighting need for stronger focus on security in MCP ecosystem

      July 11, 2025

      8 Key Questions Every CEO Should Ask Before Hiring a Node.js Development Company in 2025

      July 11, 2025

      Vibe Loop: AI-native reliability engineering for the real world

      July 10, 2025

      One of Atlus’ best Xbox JRPGs that puts modern Final Fantasy games to shame is now on a 45% discount — This is your last chance to seize it as the Amazon Day Prime closes today

      July 11, 2025

      Don’t waste the LAST 24 hours of Amazon Prime Day sales buying a MacBook — buy this much better Windows laptop instead!

      July 11, 2025

      This fantastic Xbox remake of a classic Atlus JRPG we gave a perfect review score to is now 49% cheaper — Don’t miss the deadline for this Amazon Prime Day deal, which ends today

      July 11, 2025

      HP’s discount on one of the most powerful gaming laptops on the planet is absolutely UNBEATABLE — but you only have a few hours to get one!

      July 11, 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

      The details of TC39’s last meeting

      July 11, 2025
      Recent

      The details of TC39’s last meeting

      July 11, 2025

      Francisco Bergeret Paves the Way Through Strong Leadership at Perficient

      July 11, 2025

      Intelligent Automation in the Healthcare Sector with n8n, OpenAI, and Pinecone

      July 11, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      One of Atlus’ best Xbox JRPGs that puts modern Final Fantasy games to shame is now on a 45% discount — This is your last chance to seize it as the Amazon Day Prime closes today

      July 11, 2025
      Recent

      One of Atlus’ best Xbox JRPGs that puts modern Final Fantasy games to shame is now on a 45% discount — This is your last chance to seize it as the Amazon Day Prime closes today

      July 11, 2025

      Don’t waste the LAST 24 hours of Amazon Prime Day sales buying a MacBook — buy this much better Windows laptop instead!

      July 11, 2025

      This fantastic Xbox remake of a classic Atlus JRPG we gave a perfect review score to is now 49% cheaper — Don’t miss the deadline for this Amazon Prime Day deal, which ends today

      July 11, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Scroll-Driven Sticky Heading

    Scroll-Driven Sticky Heading

    July 11, 2025

    Scroll-driven animations are great! They’re a powerful tool that lets developers tie the movement and transformation of elements directly to the user’s scroll position. This technique opens up new ways to create interactive experiences, cuing images to appear, text to glide across the stage, and backgrounds to subtly shift. Used thoughtfully, scroll-driven animations (SDA) can make your website feel more dynamic, engaging, and responsive.

    A few weeks back, I was playing around with scroll-driven animations, just searching for all sorts of random things you could do with it. That’s when I came up with the idea to animate the text of the main heading (h1) and, using SDA, change the heading itself based on the user’s scroll position on the page. In this article, we’re going to break down that idea and rebuild it step by step. This is the general direction we’ll be heading in, which looks better in full screen and viewed in a Chromium browser:

    CodePen Embed Fallback

    It’s important to note that the effect in this example only works in browsers that support scroll-driven animations. Where SDA isn’t supported, there’s a proper fallback to static headings. From an accessibility perspective, if the browser has reduced motion enabled or if the page is being accessed with assistive technology, the effect is disabled and the user gets all the content in a fully semantic and accessible way.

    Just a quick note: this approach does rely on a few “magic numbers” for the keyframes, which we’ll talk about later on. While they’re surprisingly responsive, this method is really best suited for static content, and it’s not ideal for highly dynamic websites.

    Closer Look at the Animation

    Before we dive into scroll-driven animations, let’s take a minute to look at the text animation itself, and how it actually works. This is based on an idea I had a few years back when I wanted to create a typewriter effect. At the time, most of the methods I found involved animating the element’s width, required using a monospace font, or a solid color background. None of which really worked for me. So I looked for a way to animate the content itself, and the solution was, as it often is, in pseudo-elements.

    CodePen Embed Fallback

    Pseudo-elements have a content property, and you can (kind of) animate that text. It’s not exactly animation, but you can change the content dynamically. The cool part is that the only thing that changes is the text itself, no other tricks required.

    Start With a Solid Foundation

    Now that you know the trick behind the text animation, let’s see how to combine it with a scroll-driven animation, and make sure we have a solid, accessible fallback as well.

    We’ll start with some basic semantic markup. I’ll wrap everything in a main element, with individual sections inside. Each section gets its own heading and content, like text and images. For this example, I’ve set up four sections, each with a bit of text and some images, all about Primary Colors.

    <main>
      <section>
        <h1>Primary Colors</h1>
        <p>The three primary colors (red, blue, and yellow) form the basis of all other colors on the color wheel. Mixing them in different combinations produces a wide array of hues.</p>
        <img src="./colors.jpg" alt="...image description">
      </section>
      
      <section>
        <h2>Red Power</h2>
        <p>Red is a bold and vibrant color, symbolizing energy, passion, and warmth. It easily attracts attention and is often linked with strong emotions.</p>
        <img src="./red.jpg" alt="...image description">
      </section>
      
      <section>
        <h2>Blue Calm</h2>
        <p>Blue is a calm and cool color, representing tranquility, stability, and trust. It evokes images of the sky and sea, creating a peaceful mood.</p>
        <img src="./blue.jpg" alt="...image description">
      </section>
      
      <section>
        <h2>Yellow Joy</h2>
        <p>Yellow is a bright and cheerful color, standing for light, optimism, and creativity. It is highly visible and brings a sense of happiness and hope.</p>
        <img src="./yellow.jpg" alt="...image description">
      </section>
    </main>

    As for the styling, I’m not doing anything special at this stage, just the basics. I changed the font and adjusted the text and heading sizes, set up the display for the main and the sections, and fixed the image sizes with object-fit.

    CodePen Embed Fallback

    So, at this point, we have a simple site with static, semantic, and accessible content, which is great. Now the goal is to make sure it stays that way as we start adding our effect.

    The Second First Heading

    We’ll start by adding another h1 element at the top of the main. This new element will serve as the placeholder for our animated text, updating according to the user’s scroll position. And yes, I know there’s already an h1 in the first section; that’s fine and we’ll address it in a moment so that only one is accessible at a time.

    <h1 class="scrollDrivenHeading" aria-hidden="true">Primary Colors</h1>

    Notice that I’ve added aria-hidden="true" to this heading, so it won’t be picked up by screen readers. Now I can add a class specifically for screen readers, .srOnly, to all the other headings. This way, anyone viewing the content “normally” will see only the animated heading, while assistive technology users will get the regular, static semantic headings.

    CodePen Embed Fallback

    Note: The style for the .srOnly class is based on “Inclusively Hidden” by Scott O’Hara.

    Handling Support

    As much as accessibility matters, there’s another concern we need to keep in mind: support. CSS Scroll-Driven Animations are fantastic, but they’re still not fully supported everywhere. That’s why it’s important to provide the static version for browsers that don’t support SDA.

    The first step is to hide the animated heading we just added using display: none. Then, we’ll add a new @supports block to check for SDA support. Inside that block, where SDA is supported, we can change back the display for the heading.

    The .srOnly class should also move into the @supports block, since we only want it to apply when the effect is active, not when it’s not supported. This way, just like with assistive technology, anyone visiting the page in a browser without SDA support will still get the static content.

    .scrollDrivenHeading {
      display: none;
    }
    
    @supports (animation-timeline: scroll()) {
      .scrollDrivenHeading {
        display: block;
      }
      
      /* Screen Readers Only */
      .srOnly {
        clip: rect(0 0 0 0); 
        clip-path: inset(50%);
        height: 1px;
        overflow: hidden;
        position: absolute;
        white-space: nowrap; 
        width: 1px;
      }
    }

    Get Sticky

    The next thing we need to do is handle the stickiness of the heading. To make sure the heading always stays on screen, we’ll set its position to sticky with top: 0 so it sticks to the top of the viewport.

    While we’re at it, let’s add some basic styling, including a background so the text doesn’t blend with whatever’s behind the heading, a bit of padding for spacing, and white-space: nowrap to keep the heading on a single line.

    /* inside the @supports block */
    .scrollDrivenHeading {
      display: block;
      position: sticky;
      top: 0;
      background-image: linear-gradient(0deg, transparent, black 1em);
      padding: 0.5em 0.25em;
      white-space: nowrap;
    }

    Now everything’s set up: in normal conditions, we’ll see a single sticky heading at the top of the page. And if someone uses assistive technology or a browser that doesn’t support SDA, they’ll still get the regular static content.

    CodePen Embed Fallback

    Now we’re ready to start animating the text. Almost…

    The Magic Numbers

    To build the text animation, we need to know exactly where the text should change. With SDA, scrolling basically becomes our timeline, and we have to determine the exact points on that timeline to trigger the animation.

    To make this easier, and to help you pinpoint those positions, I’ve prepared the following script:

    @property --scroll-position {
      syntax: "<number>";
      inherits: false;
      initial-value: 0;
    }
    
    body::after {
      counter-reset: sp var(--scroll-position);
      content: counter(sp) "%";
      position: fixed;
      top: 0;
      left: 0;
      padding: 1em;
      background-color: maroon;
      animation: scrollPosition steps(100);
      animation-timeline: scroll();
    }
    
    @keyframes scrollPosition {
      0% { --scroll-position: 0; }
      100% { --scroll-position: 100; }
    }

    I don’t want to get too deep into this code, but the idea is to take the same scroll timeline we’ll use next to animate the text, and use it to animate a custom property (--scroll-position) from 0 to 100 based on the scroll progress, and display that value in the content.

    If we’ll add this at the start of our code, we’ll see a small red square in the top-left corner of the screen, showing the current scroll position as a percentage (to match the keyframes). This way, you can scroll to any section you want and easily mark the percentage where each heading should begin.

    CodePen Embed Fallback

    With this method and a bit of trial and error, I found that I want the headings to change at 30%, 60%, and 90%. So, how do we actually do it? Let’s start animating.

    Animating Text

    First, we’ll clear out the content inside the .scrollDrivenHeading element so it’s empty and ready for dynamic content. In the CSS, I’ll add a pseudo-element to the heading, which we’ll use to animate the text. We’ll give it empty content, set up the animation-name, and of course, assign the animation-timeline to scroll().

    And since I’m animating the content property, which is a discrete type, it doesn’t transition smoothly between values. It just jumps from one to the next. By setting the animation-timing-function property to step-end, I make sure each change happens exactly at the keyframe I define, so the text switches precisely where I want it to, instead of somewhere in between.

    .scrollDrivenHeading {
      /* style */
    
      &::after {
        content: '';
        animation-name: headingContent;
        animation-timing-function: step-end;
        animation-timeline: scroll();
      }
    }

    As for the keyframes, this part is pretty straightforward (for now). We’ll set the first frame (0%) to the first heading, and assign the other headings to the percentages we found earlier.

    @keyframes headingContent {
      0% { content: 'Primary Colors'}
      30% { content: 'Red Power'}
      60% { content: 'Blue Calm'}
      90%, 100% { content: 'Yellow Joy'}
    }

    So, now we’ve got a site with a sticky heading that updates as you scroll.

    CodePen Embed Fallback

    But wait, right now it just switches instantly. Where’s the animation?! Here’s where it gets interesting. Since we’re not using JavaScript or any string manipulation, we have to write the keyframes ourselves. The best approach is to start from the target heading you want to reach, and build backwards. So, if you want to animate between the first and second heading, it would look like this:

    @keyframes headingContent {
      0% { content: 'Primary Colors'}
      
      9% { content: 'Primary Color'}
      10% { content: 'Primary Colo'}
      11% { content: 'Primary Col'}
      12% { content: 'Primary Co'}
      13% { content: 'Primary C'}
      14% { content: 'Primary '}
      15% { content: 'Primary'}
      16% { content: 'Primar'}
      17% { content: 'Prima'}
      18% { content: 'Prim'}
      19% { content: 'Pri'}
      20% { content: 'Pr'}
      21% { content: 'P'}
      
      22% { content: 'R'}
      23% { content: 'Re'}
      24% { content: 'Red'}
      25% { content: 'Red '}
      26% { content: 'Red P'}
      27% { content: 'Red Po'}
      28%{ content: 'Red Pow'}
      29% { content: 'Red Powe'}
      
      30% { content: 'Red Power'}
      60% { content: 'Blue Calm'}
      90%, 100% { content: 'Yellow Joy'}
    }

    I simply went back by 1% each time, removing or adding a letter as needed. Note that in other cases, you might want to use a different step size, and not always 1%. For example, on longer headings with more words, you’ll probably want smaller steps.

    If we repeat this process for all the other headings, we’ll end up with a fully animated heading.

    CodePen Embed Fallback

    User Preferences

    We talked before about accessibility and making sure the content works well with assistive technology, but there’s one more thing you should keep in mind: prefers-reduced-motion. Even though this isn’t a strict WCAG requirement for this kind of animation, it can make a big difference for people with vestibular sensitivities, so it’s a good idea to offer a way to show the content without animations.

    If you want to provide a non-animated alternative, all you need to do is wrap your @supports block with a prefers-reduced-motion query:

    @media screen and (prefers-reduced-motion: no-preference) {
      @supports (animation-timeline: scroll()) {
        /* style */
      }
    }

    Leveling Up

    Let’s talk about variations. In the previous example, we animated the entire heading text, but we don’t have to do that. You can animate just the part you want, and use additional animations to enhance the effect and make things more interesting. For example, here I kept the text “Primary Color” fixed, and added a span after it that handles the animated text.

    <h1 class="scrollDrivenHeading" aria-hidden="true">
      Primary Color<span></span>
    </h1>

    And since I now have a separate span, I can also animate its color to match each value.

    CodePen Embed Fallback

    In the next example, I kept the text animation on the span, but instead of changing the text color, I added another scroll-driven animation on the heading itself to change its background color. This way, you can add as many animations as you want and change whatever you like.

    CodePen Embed Fallback

    Your Turn!

    CSS Scroll-Driven Animations are more than just a cool trick; they’re a game-changer that opens the door to a whole new world of web design. With just a bit of creativity, you can turn even the most ordinary pages into something interactive, memorable, and truly engaging. The possibilities really are endless, from subtle effects that enhance the user experience, to wild, animated transitions that make your site stand out.

    So, what would you build with scroll-driven animations? What would you create with this new superpower? Try it out, experiment, and if you come up with something cool, have some ideas, wild experiments, or even weird failures, I’d love to hear about them. I’m always excited to see what others come up with, so feel free to share your work, questions, or feedback below.


    Special thanks to Cristian Díaz for reviewing the examples, making sure everything is accessible, and contributing valuable advice and improvements.


    Scroll-Driven Sticky Heading 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 ArticleThe Layout Maestro Course
    Next Article GoZen – minimalistic video editor

    Related Posts

    News & Updates

    One of Atlus’ best Xbox JRPGs that puts modern Final Fantasy games to shame is now on a 45% discount — This is your last chance to seize it as the Amazon Day Prime closes today

    July 11, 2025
    News & Updates

    Don’t waste the LAST 24 hours of Amazon Prime Day sales buying a MacBook — buy this much better Windows laptop instead!

    July 11, 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

    Developing a Serverless Blogging Platform with AWS Lambda and Python

    Development

    A Comprehensive Coding Guide to Crafting Advanced Round-Robin Multi-Agent Workflows with Microsoft AutoGen

    Machine Learning

    A new game from the team behind Spiritfarer was just unveiled during the Xbox Games Showcase

    News & Updates

    Forget cheap multitools. My favorite brand is repairable with a 25-year warranty

    News & Updates

    Highlights

    CVE-2025-31710 – Cisco Engineermode Command Injection Vulnerability

    June 3, 2025

    CVE ID : CVE-2025-31710

    Published : June 3, 2025, 6:15 a.m. | 1 hour, 12 minutes ago

    Description : In engineermode service, there is a possible command injection due to improper input validation. This could lead to local escalation of privilege with no additional execution privileges needed.

    Severity: 5.9 | MEDIUM

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    CVE-2025-6700 – Xuxueli xxl-sso Remote Cross-Site Scripting Vulnerability

    June 26, 2025

    CVE-2025-40733 – Daily Expense Manager Reflected XSS

    June 30, 2025

    CVE-2023-50338 – Apache HTTP Server SQL Injection

    May 28, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

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