Confetti and CSS Matryoshka on StackOverflow

StackOverflow’s design uses holiday modals, so the SO team came up with a handy way to display confetti.

The first solution was a simple static SVG with confetti in the background. The team later discovered 12 different static confetti throughout the code and realized they needed a different approach. We share the details of the solution under the cut while our Frontend development course


First solution

This is how the first solution looked like:

Just a few months later, we found that the same approach was used 12 times in the product, each iteration different from the others. Other options have appeared, for example confetti-bold.svg

Art first


As many Stack Overflow users know, the best way to get started is
lean on a good example… Fortunately, there are many examples of confetti on the internet.

Someone visualizes it in JavaScript, and someone solves the problem using gif. Sometimes entire WebGL scenes written in Three.JS are used for confetti

What we needed was something extremely portable, as close as possible to simple HTML and CSS. Didn’t want to inject dependencies or initialize JS every time confetti needs to be shown.

When looking for solutions to common problems, I often start with Codepen. And this search led me to Andy O’Brien

His approach is simple and elegant, the animation is looping! Uses Sass-generated CSS to animate individual DOM elements. And the confetti looks like the latest iteration from our designer Vivian Zhang.

To figure it out, I forked the Codepen, tweaked the colors, and started fiddling with time variables. Once done, I tweaked the colors and placed the confetti element in one of our modals.

I must say that this approach has some problems. In our contexts, we do not have a component approach at the required scale. In other words, engineers have to copy and paste a dozen confetti divs and take care of managing the z-index and pointer events.

And it works through absolute positioning and flexbox to distribute the pieces of confetti over the entire area, that is, if you want to use it on a wide container, it will stretch, and not be tiled. But we really liked the aesthetics.

I bet that instead of being represented as DOM elements, we could put it all in SVG and render it as a background image, that would give us much more control over the positioning, sizing, and repetition of elements.

SVG organization


What about SVG as a background image? Let’s try. Here we have to create an SVG element to place the pieces of confetti – and this is a matter of distributing the rectangles on the canvas, and also choosing the sizes that we like.

Replace the DOM from the previous Codepen to keep the code portable. To work quickly, I used Figma, but you can also write it all by hand:

<svg width="600" height="90" viewBox="0 0 600 90" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect x="42" y="0" width="6" height="10"/>
    <rect x="84" y="0" width="6" height="10"/>
    <rect x="126" y="0" width="5" height="13"/>
    <rect x="168" y="0" width="5" height="13"/>
    <rect x="210" y="0" width="6" height="10"/>
    <rect x="252" y="0" width="5" height="13"/>
    <rect x="294" y="0" width="6" height="10"/>
    <rect x="336" y="0" width="5" height="13"/>
    <rect x="378" y="0" width="5" height="13"/>
    <rect x="420" y="0" width="6" height="10"/>
    <rect x="462" y="0" width="6" height="10"/>
    <rect x="504" y="0" width="5" height="13"/>
    <rect x="546" y="0" width="6" height="10"/>
</svg>

Now I have an SVG element and I can inline CSS in it. Andy’s approach was to color, rotate and position the various confetti elements using a CSS selector

nth-child

… My SVG was initially structured like this:

<svg>
    <style type="text/css">...</style>
    <rect />
</svg>

And now all the CSS selectors

nth-child

offset by one because CSS considers the style element to be a child. I think we will move this element to the very bottom.

Also, Sass functions won’t work in SVG, so you have to compile the code to CSS and paste it into SVG. Codepen can compile, and I’ll copy and paste the result.

<svg width="600" height="90" viewBox="0 0 600 90" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect x="42" y="0" width="6" height="10"/>
    <rect x="84" y="0" width="6" height="10"/>
    <rect x="126" y="0" width="5" height="13"/>
    <rect x="168" y="0" width="5" height="13"/>
    <rect x="210" y="0" width="6" height="10"/>
    <rect x="252" y="0" width="5" height="13"/>
    <rect x="294" y="0" width="6" height="10"/>
    <rect x="336" y="0" width="5" height="13"/>
    <rect x="378" y="0" width="5" height="13"/>
    <rect x="420" y="0" width="6" height="10"/>
    <rect x="462" y="0" width="6" height="10"/>
    <rect x="504" y="0" width="5" height="13"/>
    <rect x="546" y="0" width="6" height="10"/>

    <style type="text/css">
        rect {
            opacity: 0;
        }
        rect:nth-child(1) {
            transform: rotate(-145deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 88ms;
            animation-duration: 631ms;
        }
        rect:nth-child(2) {
            transform: rotate(164deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 131ms;
            animation-duration: 442ms;
        }
        rect:nth-child(3) {
            transform: rotate(4deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 92ms;
            animation-duration: 662ms;
        }
        rect:nth-child(4) {
            transform: rotate(-175deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 17ms;
            animation-duration: 593ms;
        }
        rect:nth-child(5) {
            transform: rotate(-97deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 122ms;
            animation-duration: 476ms;
        }
        rect:nth-child(6) {
            transform: rotate(57deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 271ms;
            animation-duration: 381ms;
        }
        rect:nth-child(7) {
            transform: rotate(-46deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 131ms;
            animation-duration: 619ms;
        }
        rect:nth-child(8) {
            transform: rotate(-65deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 85ms;
            animation-duration: 668ms;
        }
        rect:nth-child(9) {
            transform: rotate(13deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 128ms;
            animation-duration: 377ms;
        }
        rect:nth-child(10) {
            transform: rotate(176deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 311ms;
            animation-duration: 508ms;
        }
        rect:nth-child(11) {
            transform: rotate(108deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 108ms;
            animation-duration: 595ms;
        }
        rect:nth-child(12) {
            transform: rotate(62deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 105ms;
            animation-duration: 375ms;
        }
        rect:nth-child(13) {
            transform: rotate(16deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 149ms;
            animation-duration: 491ms;
        }
        rect:nth-child(odd) {
            fill: #65BB5C;
        }
        rect:nth-child(even) {
            z-index: 1;
            fill: #33AAFF;
        }
        rect:nth-child(4n) {
            animation-duration: 1400ms;
            fill: #F23B14;
        }
        rect:nth-child(3n) {
            animation-duration: 1750ms;
            animation-delay: 700ms;
        }
        rect:nth-child(4n-7) {
            fill: #2A2F6A;
        }
        rect:nth-child(6n) {
            fill: #FBBA23;
        }

        @keyframes blast {
            from {
                opacity: 0;
            }
            20% {
                opacity: 1;
            }
            to {
                transform: translateY(90px);
            }
        }
    </style>
</svg>

Save the code as SVG and open in a browser. You will see animated confetti. But there is a problem with the arrangement of the pieces. In SVG, transformations start from a different point: the origin is located in the upper left corner. I solved this problem by measuring distances in Figma and hardcoding some raw data, for example

transform-origin: 45px 5px

… Not perfect, but close enough to center:

<svg width="600" height="90" viewBox="0 0 600 90" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect x="42" y="0" width="6" height="10"/>
    <rect x="84" y="0" width="6" height="10"/>
    <rect x="126" y="0" width="5" height="13"/>
    <rect x="168" y="0" width="5" height="13"/>
    <rect x="210" y="0" width="6" height="10"/>
    <rect x="252" y="0" width="5" height="13"/>
    <rect x="294" y="0" width="6" height="10"/>
    <rect x="336" y="0" width="5" height="13"/>
    <rect x="378" y="0" width="5" height="13"/>
    <rect x="420" y="0" width="6" height="10"/>
    <rect x="462" y="0" width="6" height="10"/>
    <rect x="504" y="0" width="5" height="13"/>
    <rect x="546" y="0" width="6" height="10"/>

    <style type="text/css">
        rect {
            opacity: 0;
        }
        rect:nth-child(1) {
            transform-origin: 45px 5px;
            transform: rotate(-145deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 88ms;
            animation-duration: 631ms;
        }
        rect:nth-child(2) {
            transform-origin: 87px 5px;
            transform: rotate(164deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 131ms;
            animation-duration: 442ms;
        }
        rect:nth-child(3) {
            transform-origin: 128px 6px;
            transform: rotate(4deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 92ms;
            animation-duration: 662ms;
        }
        rect:nth-child(4) {
            transform-origin: 170px 6px;
            transform: rotate(-175deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 17ms;
            animation-duration: 593ms;
        }
        rect:nth-child(5) {
            transform-origin: 213px 5px;
            transform: rotate(-97deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 122ms;
            animation-duration: 476ms;
        }
        rect:nth-child(6) {
            transform-origin: 255px 6px;
            transform: rotate(57deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 271ms;
            animation-duration: 381ms;
        }
        rect:nth-child(7) {
            transform-origin: 297px 5px;
            transform: rotate(-46deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 131ms;
            animation-duration: 619ms;
        }
        rect:nth-child(8) {
            transform-origin: 338px 6px;
            transform: rotate(-65deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 85ms;
            animation-duration: 668ms;
        }
        rect:nth-child(9) {
            transform-origin: 380px 6px;
            transform: rotate(13deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 128ms;
            animation-duration: 377ms;
        }
        rect:nth-child(10) {
            transform-origin: 423px 5px;
            transform: rotate(176deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 311ms;
            animation-duration: 508ms;
        }
        rect:nth-child(11) {
            transform-origin: 465px 5px;
            transform: rotate(108deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 108ms;
            animation-duration: 595ms;
        }
        rect:nth-child(12) {
            transform-origin: 506px 6px;
            transform: rotate(62deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 105ms;
            animation-duration: 375ms;
        }
        rect:nth-child(13) {
            transform-origin: 549px 5px;
            transform: rotate(16deg);
            animation: blast 700ms infinite ease-out;
            animation-delay: 149ms;
            animation-duration: 491ms;
        }
        rect:nth-child(odd) {
            fill: #65BB5C;
        }
        rect:nth-child(even) {
            z-index: 1;
            fill: #33AAFF;
        }
        rect:nth-child(4n) {
            animation-duration: 1400ms;
            fill: #F23B14;
        }
        rect:nth-child(3n) {
            animation-duration: 1750ms;
            animation-delay: 700ms;
        }
        rect:nth-child(4n-7) {
            fill: #2A2F6A;
        }
        rect:nth-child(6n) {
            fill: #FBBA23;
        }

        @keyframes blast {
            from {
                opacity: 0;
            }
            20% {
                opacity: 1;
            }
            to {
                transform: translateY(90px);
            }
        }
    </style>
</svg>

And let us allow ourselves the last grace. It is noticeable that due to the specific origin of the SVG at the bottom, some of the confetti pieces break off at the end of the animation. This problem is solved by pushing the slice up and away from the canvas:

<rect x="42" y="-10" width="6" height="10"/>
<rect x="84" y="-10" width="6" height="10"/>
<rect x="126" y="-13" width="5" height="13"/>
<rect x="168" y="-13" width="5" height="13"/>
<rect x="210" y="-10" width="6" height="10"/>
<rect x="252" y="-13" width="5" height="13"/>
<rect x="294" y="-10" width="6" height="10"/>
<rect x="336" y="-13" width="5" height="13"/>
<rect x="378" y="-13" width="5" height="13"/>
<rect x="420" y="-10" width="6" height="10"/>
<rect x="462" y="-10" width="6" height="10"/>
<rect x="504" y="-13" width="5" height="13"/>
<rect x="546" y="-10" width="6" height="10"/>

Delivery

Fine! We have one SVG file, which is now responsible for all animation, and we can make it the background of any element. Encapsulation can be continued by encoding the entire SVG (along with CSS) into the design system’s CSS file. This will result in CSS to SVG in CSS. Why not? It is enough to specify the SVG as the background image:

.bg-confetti-animated {
    background-repeat: repeat-x;
    background-position: top -10px center;
    background-image: url("data:image/svg+xml,%3Csvg width="600" height="90" viewBox='0 0 600 90' fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Crect x='42' y='-10' width="6" height="10"/%3E%3Crect x='84' y='-10' width="6" height="10"/%3E%3Crect x='126' y='-13' width="5" height="13"/%3E%3Crect x='168' y='-13' width="5" height="13"/%3E%3Crect x='210' y='-10' width="6" height="10"/%3E%3Crect x='252' y='-13' width="5" height="13"/%3E%3Crect x='294' y='-10' width="6" height="10"/%3E%3Crect x='336' y='-13' width="5" height="13"/%3E%3Crect x='378' y='-13' width="5" height="13"/%3E%3Crect x='420' y='-10' width="6" height="10"/%3E%3Crect x='462' y='-10' width="6" height="10"/%3E%3Crect x='504' y='-13' width="5" height="13"/%3E%3Crect x='546' y='-10' width="6" height="10"/%3E%3Cstyle type="text/css"%3E rect %7B opacity: 0; %7D rect:nth-child(1) %7B transform-origin: 45px 5px; transform: rotate(-145deg); animation: blast 700ms infinite ease-out; animation-delay: 88ms; animation-duration: 631ms; %7D rect:nth-child(2) %7B transform-origin: 87px 5px; transform: rotate(164deg); animation: blast 700ms infinite ease-out; animation-delay: 131ms; animation-duration: 442ms; %7D rect:nth-child(3) %7B transform-origin: 128px 6px; transform: rotate(4deg); animation: blast 700ms infinite ease-out; animation-delay: 92ms; animation-duration: 662ms; %7D rect:nth-child(4) %7B transform-origin: 170px 6px; transform: rotate(-175deg); animation: blast 700ms infinite ease-out; animation-delay: 17ms; animation-duration: 593ms; %7D rect:nth-child(5) %7B transform-origin: 213px 5px; transform: rotate(-97deg); animation: blast 700ms infinite ease-out; animation-delay: 122ms; animation-duration: 476ms; %7D rect:nth-child(6) %7B transform-origin: 255px 6px; transform: rotate(57deg); animation: blast 700ms infinite ease-out; animation-delay: 271ms; animation-duration: 381ms; %7D rect:nth-child(7) %7B transform-origin: 297px 5px; transform: rotate(-46deg); animation: blast 700ms infinite ease-out; animation-delay: 131ms; animation-duration: 619ms; %7D rect:nth-child(8) %7B transform-origin: 338px 6px; transform: rotate(-65deg); animation: blast 700ms infinite ease-out; animation-delay: 85ms; animation-duration: 668ms; %7D rect:nth-child(9) %7B transform-origin: 380px 6px; transform: rotate(13deg); animation: blast 700ms infinite ease-out; animation-delay: 128ms; animation-duration: 377ms; %7D rect:nth-child(10) %7B transform-origin: 423px 5px; transform: rotate(176deg); animation: blast 700ms infinite ease-out; animation-delay: 311ms; animation-duration: 508ms; %7D rect:nth-child(11) %7B transform-origin: 465px 5px; transform: rotate(108deg); animation: blast 700ms infinite ease-out; animation-delay: 108ms; animation-duration: 595ms; %7D rect:nth-child(12) %7B transform-origin: 506px 6px; transform: rotate(62deg); animation: blast 700ms infinite ease-out; animation-delay: 105ms; animation-duration: 375ms; %7D rect:nth-child(13) %7B transform-origin: 549px 5px; transform: rotate(16deg); animation: blast 700ms infinite ease-out; animation-delay: 149ms; animation-duration: 491ms; %7D rect:nth-child(odd) %7B fill: %2365BB5C; %7D rect:nth-child(even) %7B z-index: 1; fill: %2333AAFF; %7D rect:nth-child(4n) %7B animation-duration: 1400ms; fill: %23F23B14; %7D rect:nth-child(3n) %7B animation-duration: 1750ms; animation-delay: 700ms; %7D rect:nth-child(4n-7) %7B fill: %232A2F6A; %7D rect:nth-child(6n) %7B fill: %23FBBA23; %7D @keyframes blast %7B from %7B opacity: 0; %7D 20%25 %7B opacity: 1; %7D to %7B transform: translateY(90px); %7D %7D %3C/style%3E%3C/svg%3E%0A");
}

In such cases, I use

yoksel

Subtleties of animation


Some people struggle with animation. Others prefer to avoid it altogether. Fortunately, browsers offer media (prefers-reduced-motion) media query. Let’s use it to show the statically rendered confetti. The static version in Figma was drawn by Vivian:


It remains to embed it in the media query:

.bg-confetti-animated {
    background-repeat: repeat-x;
    background-position: top -10px center;
    background-image: url("data:image/svg+xml,%3Csvg width="600" height="90" viewBox='0 0 600 90' fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Crect x='42' y='-10' width="6" height="10"/%3E%3Crect x='84' y='-10' width="6" height="10"/%3E%3Crect x='126' y='-13' width="5" height="13"/%3E%3Crect x='168' y='-13' width="5" height="13"/%3E%3Crect x='210' y='-10' width="6" height="10"/%3E%3Crect x='252' y='-13' width="5" height="13"/%3E%3Crect x='294' y='-10' width="6" height="10"/%3E%3Crect x='336' y='-13' width="5" height="13"/%3E%3Crect x='378' y='-13' width="5" height="13"/%3E%3Crect x='420' y='-10' width="6" height="10"/%3E%3Crect x='462' y='-10' width="6" height="10"/%3E%3Crect x='504' y='-13' width="5" height="13"/%3E%3Crect x='546' y='-10' width="6" height="10"/%3E%3Cstyle type="text/css"%3E rect %7B opacity: 0; %7D rect:nth-child(1) %7B transform-origin: 45px 5px; transform: rotate(-145deg); animation: blast 700ms infinite ease-out; animation-delay: 88ms; animation-duration: 631ms; %7D rect:nth-child(2) %7B transform-origin: 87px 5px; transform: rotate(164deg); animation: blast 700ms infinite ease-out; animation-delay: 131ms; animation-duration: 442ms; %7D rect:nth-child(3) %7B transform-origin: 128px 6px; transform: rotate(4deg); animation: blast 700ms infinite ease-out; animation-delay: 92ms; animation-duration: 662ms; %7D rect:nth-child(4) %7B transform-origin: 170px 6px; transform: rotate(-175deg); animation: blast 700ms infinite ease-out; animation-delay: 17ms; animation-duration: 593ms; %7D rect:nth-child(5) %7B transform-origin: 213px 5px; transform: rotate(-97deg); animation: blast 700ms infinite ease-out; animation-delay: 122ms; animation-duration: 476ms; %7D rect:nth-child(6) %7B transform-origin: 255px 6px; transform: rotate(57deg); animation: blast 700ms infinite ease-out; animation-delay: 271ms; animation-duration: 381ms; %7D rect:nth-child(7) %7B transform-origin: 297px 5px; transform: rotate(-46deg); animation: blast 700ms infinite ease-out; animation-delay: 131ms; animation-duration: 619ms; %7D rect:nth-child(8) %7B transform-origin: 338px 6px; transform: rotate(-65deg); animation: blast 700ms infinite ease-out; animation-delay: 85ms; animation-duration: 668ms; %7D rect:nth-child(9) %7B transform-origin: 380px 6px; transform: rotate(13deg); animation: blast 700ms infinite ease-out; animation-delay: 128ms; animation-duration: 377ms; %7D rect:nth-child(10) %7B transform-origin: 423px 5px; transform: rotate(176deg); animation: blast 700ms infinite ease-out; animation-delay: 311ms; animation-duration: 508ms; %7D rect:nth-child(11) %7B transform-origin: 465px 5px; transform: rotate(108deg); animation: blast 700ms infinite ease-out; animation-delay: 108ms; animation-duration: 595ms; %7D rect:nth-child(12) %7B transform-origin: 506px 6px; transform: rotate(62deg); animation: blast 700ms infinite ease-out; animation-delay: 105ms; animation-duration: 375ms; %7D rect:nth-child(13) %7B transform-origin: 549px 5px; transform: rotate(16deg); animation: blast 700ms infinite ease-out; animation-delay: 149ms; animation-duration: 491ms; %7D rect:nth-child(odd) %7B fill: %2365BB5C; %7D rect:nth-child(even) %7B z-index: 1; fill: %2333AAFF; %7D rect:nth-child(4n) %7B animation-duration: 1400ms; fill: %23F23B14; %7D rect:nth-child(3n) %7B animation-duration: 1750ms; animation-delay: 700ms; %7D rect:nth-child(4n-7) %7B fill: %232A2F6A; %7D rect:nth-child(6n) %7B fill: %23FBBA23; %7D @keyframes blast %7B from %7B opacity: 0; %7D 20%25 %7B opacity: 1; %7D to %7B transform: translateY(90px); %7D %7D %3C/style%3E%3C/svg%3E%0A");

    @media (prefers-reduced-motion) {
        background-image: url("data:image/svg+xml,%3Csvg width="574" height="60" viewBox='0 0 574 60' fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Crect opacity='0.8' x='27.1224' y='20.0458' width="5" height="13" transform='rotate(-139 27.1224 20.0458)' fill="%23F23B14"/%3E%3Crect opacity='0.8' x='118.478' y='7.00201' width="5" height="13" transform='rotate(-38.8114 118.478 7.00201)' fill="%23FBBA23"/%3E%3Crect opacity='0.8' x='504.616' y='25.4479' width="5" height="13" transform='rotate(-60.2734 504.616 25.4479)' fill="%23F23B14"/%3E%3Crect opacity='0.6' x='538.983' y='45.555' width="5" height="13" transform='rotate(16.7826 538.983 45.555)' fill="%232A2F6A"/%3E%3Crect opacity='0.3' x='470.322' y='2.63625' width="5" height="13" transform='rotate(11.295 470.322 2.63625)' fill="%2333AAFF"/%3E%3Crect opacity='0.3' x='190.295' y='4.58138' width="5" height="13" transform='rotate(27.5954 190.295 4.58138)' fill="%23F23B14"/%3E%3Crect opacity='0.8' x='234.303' y='16.3233' width="5" height="13" transform='rotate(-41.8233 234.303 16.3233)' fill="%2365BB5C"/%3E%3Crect opacity='0.6' x='369.702' y='40.9875' width="5" height="13" transform='rotate(-56.419 369.702 40.9875)' fill="%2333AAFF"/%3E%3Crect opacity='0.3' x='402.121' y='31.0848' width="5" height="13" transform='rotate(-17.9234 402.121 31.0848)' fill="%23F23B14"/%3E%3Crect opacity='0.6' x='200.316' y='31.9328' width="5" height="13" transform='rotate(-15.8896 200.316 31.9328)' fill="%232A2F6A"/%3E%3Crect opacity='0.6' x='69.6745' y='23.4725' width="6" height="10" transform='rotate(70.0266 69.6745 23.4725)' fill="%2365BB5C"/%3E%3Crect opacity='0.6' x='291.945' y='7.16931' width="6" height="10" transform='rotate(30.4258 291.945 7.16931)' fill="%23FBBA23"/%3E%3Crect opacity='0.3' x='33.7754' y='38.2208' width="6" height="10" transform='rotate(38.6056 33.7754 38.2208)' fill="%23FBBA23"/%3E%3Crect opacity='0.8' x='109.752' y='31.1743' width="6" height="10" transform='rotate(28.5296 109.752 31.1743)' fill="%2333AAFF"/%3E%3Crect opacity='0.3' x='278.081' y='37.8695' width="6" height="10" transform='rotate(-26.5651 278.081 37.8695)' fill="%23F23B14"/%3E%3Crect opacity='0.8' x='416.294' y='11.5573' width="6" height="10" transform='rotate(-22.8498 416.294 11.5573)' fill="%23FBBA23"/%3E%3Crect opacity='0.3' x='354.667' y='9.32341' width="6" height="10" transform='rotate(17.7506 354.667 9.32341)' fill="%232A2F6A"/%3E%3Crect opacity='0.8' x='532.404' y='16.6372' width="6" height="10" transform='rotate(-75.3432 532.404 16.6372)' fill="%23FBBA23"/%3E%3Crect opacity='0.6' x='460.463' y='39.3557' width="6" height="10" transform='rotate(45.4982 460.463 39.3557)' fill="%2365BB5C"/%3E%3C/svg%3E");
    }
}

Or provide the code as a separate class

.bg-confetti-static

:

.bg-confetti-static {
    background-repeat: repeat-x;
    background-position: top -10px center;
    background-image: url("data:image/svg+xml,%3Csvg width="574" height="60" viewBox='0 0 574 60' fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Crect opacity='0.8' x='27.1224' y='20.0458' width="5" height="13" transform='rotate(-139 27.1224 20.0458)' fill="%23F23B14"/%3E%3Crect opacity='0.8' x='118.478' y='7.00201' width="5" height="13" transform='rotate(-38.8114 118.478 7.00201)' fill="%23FBBA23"/%3E%3Crect opacity='0.8' x='504.616' y='25.4479' width="5" height="13" transform='rotate(-60.2734 504.616 25.4479)' fill="%23F23B14"/%3E%3Crect opacity='0.6' x='538.983' y='45.555' width="5" height="13" transform='rotate(16.7826 538.983 45.555)' fill="%232A2F6A"/%3E%3Crect opacity='0.3' x='470.322' y='2.63625' width="5" height="13" transform='rotate(11.295 470.322 2.63625)' fill="%2333AAFF"/%3E%3Crect opacity='0.3' x='190.295' y='4.58138' width="5" height="13" transform='rotate(27.5954 190.295 4.58138)' fill="%23F23B14"/%3E%3Crect opacity='0.8' x='234.303' y='16.3233' width="5" height="13" transform='rotate(-41.8233 234.303 16.3233)' fill="%2365BB5C"/%3E%3Crect opacity='0.6' x='369.702' y='40.9875' width="5" height="13" transform='rotate(-56.419 369.702 40.9875)' fill="%2333AAFF"/%3E%3Crect opacity='0.3' x='402.121' y='31.0848' width="5" height="13" transform='rotate(-17.9234 402.121 31.0848)' fill="%23F23B14"/%3E%3Crect opacity='0.6' x='200.316' y='31.9328' width="5" height="13" transform='rotate(-15.8896 200.316 31.9328)' fill="%232A2F6A"/%3E%3Crect opacity='0.6' x='69.6745' y='23.4725' width="6" height="10" transform='rotate(70.0266 69.6745 23.4725)' fill="%2365BB5C"/%3E%3Crect opacity='0.6' x='291.945' y='7.16931' width="6" height="10" transform='rotate(30.4258 291.945 7.16931)' fill="%23FBBA23"/%3E%3Crect opacity='0.3' x='33.7754' y='38.2208' width="6" height="10" transform='rotate(38.6056 33.7754 38.2208)' fill="%23FBBA23"/%3E%3Crect opacity='0.8' x='109.752' y='31.1743' width="6" height="10" transform='rotate(28.5296 109.752 31.1743)' fill="%2333AAFF"/%3E%3Crect opacity='0.3' x='278.081' y='37.8695' width="6" height="10" transform='rotate(-26.5651 278.081 37.8695)' fill="%23F23B14"/%3E%3Crect opacity='0.8' x='416.294' y='11.5573' width="6" height="10" transform='rotate(-22.8498 416.294 11.5573)' fill="%23FBBA23"/%3E%3Crect opacity='0.3' x='354.667' y='9.32341' width="6" height="10" transform='rotate(17.7506 354.667 9.32341)' fill="%232A2F6A"/%3E%3Crect opacity='0.8' x='532.404' y='16.6372' width="6" height="10" transform='rotate(-75.3432 532.404 16.6372)' fill="%23FBBA23"/%3E%3Crect opacity='0.6' x='460.463' y='39.3557' width="6" height="10" transform='rotate(45.4982 460.463 39.3557)' fill="%2365BB5C"/%3E%3C/svg%3E");
}

That’s all! Our engineers and designers have a single portable .bg-confetti-animated class that they can add to any block element. You can find the documentation in

Stacks

, and you can also find out how we

styled modals

You can continue learning CSS in our courses:

Find out the details here

Other professions and courses

Data Science and Machine Learning

Python, web development

Mobile development

Java and C #

From the basics to the depth

And

Similar Posts

Leave a Reply

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