Create an Infinite Horizontal Scroll Animation with Tailwind CSS

create-an-infinite-horizontal-scroll-animation-with-tailwind-css

Live Demo / Download

In this tutorial, we will show you how to create a cool infinite horizontal animation using only CSS. This effect can be used to display a variety of things, and for this post, we’ll focus on building a logo carousel that smoothly scrolls from right to left using Tailwind CSS classes.

The best part is that you can simply copy and paste the code into your HTML page, and there’s no need for any custom CSS.

As the last thing before we begin, if you’re interested in seeing infinite horizontal scroll in action in a real-case scenario, look at our dark Next.js landing page template, or our recruitment website template.

Let’s begin!

Create the HTML structure

First, let’s set up a straightforward structure for our logo carousel using an unordered list.

 class="w-full inline-flex flex-nowrap">
     class="flex items-center justify-center md:justify-start [&_li]:mx-8 [&_img]:max-w-none">
        
  • src="./facebook.svg" alt="Facebook" />
  • src="./disney.svg" alt="Disney" />
  • src="./airbnb.svg" alt="Airbnb" />
  • src="./apple.svg" alt="Apple" />
  • src="./spark.svg" alt="Spark" />
  • src="./samsung.svg" alt="Samsung" />
  • src="./quora.svg" alt="Quora" />
  • src="./sass.svg" alt="Sass" />
  • Each list item will represent a company’s logo, and we’ve added the alt attribute to every image to ensure accessibility.

    The ul element has the classes flex and items-center, which make the logos stand in a row and be vertically centered.

    Additionally, we’ve used some arbitrary Tailwind CSS variants to optimize the layout:

    • [&_li]:mx-8 adds a horizontal margin of 32px to each list item
    • [&_img]:max-w-none removes the default 100% maximum width applied to images, preserving their original size, even on smaller screens

    Define the animation

    Defining the animation is straightforward. All we need to do is translate the ul element to the left from 0 to -100%. However, Tailwind CSS doesn’t provide this specific animation out-of-the-box, so we’ll define it ourselves in our tailwind.config object.

    Typically, you’d have a tailwind.config.js file in your project’s root directory. But since we’re using the Tailwind CDN, we’ll define the tailwind.config object within a script tag in our HTML file.

    tailwind.config = {
        theme: {
        extend: {
            animation: {
            'infinite-scroll': 'infinite-scroll 25s linear infinite',
            },
            keyframes: {
            'infinite-scroll': {
                from: { transform: 'translateX(0)' },
                to: { transform: 'translateX(-100%)' },
            }
            }                    
        },
        },
    }
    

    We’ve named the animation infinite-scroll, and it’s a linear, infinite, and lasts for 25 seconds. We’ve also specified the keyframes of the animation, to translate from 0 to -100%.

    Using this animation is as easy as adding the class animate-infinite-scroll to the element you wish to translate. In our case, that’s the ul element.

    Make the animation loop

    Now, if you preview the page, you’ll see that the animation works like a charm! But there’s one tiny hiccup – when the ul element reaches -100%, it suddenly jumps back to 0, breaking the seamless flow.

    To avoid that, we’ll duplicate the ul element and position it right after the existing one. This way, when the animation reaches -100%, the duplicate element takes over, ensuring the animation continues without any disruptions.

    We know redundant elements in HTML are a no-no, so for now, let’s add the aria-hidden="true" attribute to the duplicated element, making it invisible to screen readers. Later, we’ll use some just a bit of JavaScript to create the duplicate element dynamically.

     class="w-full inline-flex flex-nowrap">
         class="flex items-center justify-center md:justify-start [&_li]:mx-8 [&_img]:max-w-none animate-infinite-scroll">
            
  • src="./facebook.svg" alt="Facebook" />
  • src="./disney.svg" alt="Disney" />
  • src="./airbnb.svg" alt="Airbnb" />
  • src="./apple.svg" alt="Apple" />
  • src="./spark.svg" alt="Spark" />
  • src="./samsung.svg" alt="Samsung" />
  • src="./quora.svg" alt="Quora" />
  • src="./sass.svg" alt="Sass" />
  • class="flex items-center justify-center md:justify-start [&_li]:mx-8 [&_img]:max-w-none animate-infinite-scroll" aria-hidden="true">
  • src="./facebook.svg" alt="Facebook" />
  • src="./disney.svg" alt="Disney" />
  • src="./airbnb.svg" alt="Airbnb" />
  • src="./apple.svg" alt="Apple" />
  • src="./spark.svg" alt="Spark" />
  • src="./samsung.svg" alt="Samsung" />
  • src="./quora.svg" alt="Quora" />
  • src="./sass.svg" alt="Sass" />
  • Voila! The animation is now seamless and never-ending! If you find the speed too slow or too fast for your taste, go ahead and tweak the animation duration in the tailwind.config.js file.

    Creating a gradient mask

    Even though we’ve set a maximum width for our container, the animation goes a bit wild and extends beyond the edges, taking over the whole page. That’s totally fine, but if you want to keep things within the container, we definitely need a gradient mask.

    Just add two classes to the element containing the unordered lists:

    • overflow-hidden, which hides anything that extends beyond the container’s borders
    • [mask-image:_linear-gradient(to_right,transparent_0,_black_128px,_black_calc(100%-128px),transparent_100%)], which defines a gradient mask at the container’s edges, transitioning from transparent to black
     class="w-full inline-flex flex-nowrap overflow-hidden [mask-image:_linear-gradient(to_right,transparent_0,_black_128px,_black_calc(100%-200px),transparent_100%)]">
         class="flex items-center justify-center md:justify-start [&_li]:mx-8 [&_img]:max-w-none animate-infinite-scroll">
            
        
         class="flex items-center justify-center md:justify-start [&_li]:mx-8 [&_img]:max-w-none animate-infinite-scroll" aria-hidden="true">
            
        
    

    Use a bit of JavaScript to clone the list

    Alright, as mentioned earlier, now we’ll see how we can easily create the duplicate element using Alpine.js to make the code cleaner and more readable.

    Here’s what we’re going to do:

    • Remove the duplicated list from the HTML
    • Define a x-data directive with an empty object
    • Add an x-ref attribute to the element containing the list to be duplicated, so we can refer to it in Alpine.js
    • Define the x-init directive with a function that works its magic when the page loads
    
        x-data="{}"
        x-init="$nextTick(() => {
            let ul = $refs.logos;
            ul.insertAdjacentHTML('afterend', ul.outerHTML);
            ul.nextSibling.setAttribute('aria-hidden', 'true');
        })"
        class="w-full inline-flex flex-nowrap overflow-hidden [mask-image:_linear-gradient(to_right,transparent_0,_black_128px,_black_calc(100%-128px),transparent_100%)]"
    >
         x-ref="logos" class="flex items-center justify-center md:justify-start [&_li]:mx-8 [&_img]:max-w-none animate-infinite-scroll">
            
  • src="./facebook.svg" alt="Facebook" />
  • src="./disney.svg" alt="Disney" />
  • src="./airbnb.svg" alt="Airbnb" />
  • src="./apple.svg" alt="Apple" />
  • src="./spark.svg" alt="Spark" />
  • src="./samsung.svg" alt="Samsung" />
  • src="./quora.svg" alt="Quora" />
  • src="./sass.svg" alt="Sass" />
  • The function defined in x-init simply copies the ul element and inserts it right after, adding the aria-hidden attribute to the duplicated element.

    Conclusions

    As for all our tutorials, we also included components for both Next.js and Vue, so you can easily integrate this animation in your favorite framework.

    If you’re hungry for more guides and tips we invite you to look at our section of Tailwind CSS tutorials or our gallery of Tailwind CSS templates if you need a starting point for your next project.

    Total
    0
    Shares
    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Previous Post
    23-key-skills-every-product-marketing-manager-needs

    23 key skills every product marketing manager needs

    Next Post
    how-low-code-platforms-in-retail-are-transforming-the-industry?

    How Low-Code Platforms in Retail are Transforming the Industry?

    Related Posts