CSS-Only Marquee Effect

A simple CSS-only marquee effect for a menu based on Francesco Zagami’s Dribbble shot.


Some time ago I encountered this great Dribbble shot by Francesco Zagami. It has a really nice marquee animation when hovering a menu item (you have to wait a couple of seconds to see the menu).

I really love this effect and I have seen it in more designs recently. So I wanted to try and implement it using CSS only, without any JavaScript, and share it with you. After some searching, I found an interesting solution on StackOverflow by Fabrizio Calderan and one by Alvin Kobie on Codepen.

In the meantime, Fabrizio showed another really brilliant solution that does not require the repeated spans, but that uses text shadows. Have a look at it here: https://codepen.io/fcalderan/pen/GRJeYOL

For this demo, I needed to adjust the styles a bit to create the exact effect seen in Francesco’s Dribbble shot, like offsetting the marquee text and fading it in on hover. The marquee requires text repetition so that the illusion works. The main idea is to animate the marquee infinitely, restarting it seamlessly.

For that we can use the following markup:

<div class="marquee">
	<div class="marquee__inner" aria-hidden="true">

… and these styles:

.marquee {
    position: relative;
    overflow: hidden;
    --offset: 20vw;
    --move-initial: calc(-25% + var(--offset));
    --move-final: calc(-50% + var(--offset));

.marquee__inner {
    width: fit-content;
    display: flex;
    position: relative;
    transform: translate3d(var(--move-initial), 0, 0);
    animation: marquee 5s linear infinite;
    animation-play-state: paused;

.marquee span {
    font-size: 10vw;
    padding: 0 2vw;

.marquee:hover .marquee__inner {
    animation-play-state: running;

@keyframes marquee {
    0% {
        transform: translate3d(var(--move-initial), 0, 0);

    100% {
        transform: translate3d(var(--move-final), 0, 0);

For the marquee to have an offset (i.e. we want to show the first item, cut off at the beginning), it basically needs to be pulled back. So let’s use four repeated items, like this:

The amount that we want the items to be pulled back is defined in the variable --move-initial. So -25% makes it move back the exact length of one item (as we have four in total).

And the --offset lets us adjust this a bit, so that we see some of the text. --move-final is the end position of the animation, where we can seamlessly start a new loop. It’s half of the way (two items now), again with one item on the left being cut off the same amount like in the initial position. By setting an adequate font size (in vw), we can make sure that three repetitions are visible in the viewport. This is important for the “illusion” to work (i.e. start the next loop).

For the demo, I’ve added some more transitions and images with a blend mode. Have a look at the code if you’d like to see how that all works together.

I really hope you like this demo and find it useful!


Tagged with:

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

👾 Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!