Stand with Ukraine 🇺🇦

Confetti Effects — Confluence Cloud Macro

visuals

This Confluence user macro adds a celebratory confetti animation when a page is updated.

It automatically detects if the current page version has changed and, if so, displays a short burst of animated confetti along with a random motivational message.

The macro runs silently in the background, cleans up after itself, and resizes to zero once the animation ends — ensuring minimal layout disruption.

Confetti effects are powered by the open-source canvas-confetti library.

Try for free

User Parameters

This macro comes without configurable user parameters.

Template

#set($pageId = $page.id)
#set($outputType = $renderContext.outputType)

<div id="page-edit-confetti" data-macro="page-edit-confetti" style="display: none; overflow: hidden; padding: 0; margin: 0;"></div>

<style>
    @keyframes fadeOut {
        0% {
            opacity: 1;
            transform: scale(1);
        }
        100% {
            opacity: 0;
            transform: scale(0.95);
        }
    }

    .fade-out {
        animation: fadeOut 500ms ease-out forwards;
    }
</style>

<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>

<script>
    (function () {
        const pageId = "$pageId";
        const storageKey = "confluence-page-version-" + pageId;
        const confettiDuration = 3500;
        const fadeDuration = 500;
        const confettiBox = document.getElementById("page-edit-confetti");
        const outputType = "$outputType";

        const messages = [
            "🎉 Another page powered up! Your contribution just made the workspace smarter.",
            "📝 Update complete! Thanks for keeping the team in the loop!",
            "🌟 Way to go! You’ve just made collaboration a little easier for everyone.",
            "🚀 Nice update! Great documentation is what drives great teams.",
            "💡 Content refreshed! Thanks for keeping knowledge flowing.",
            "🏆 Knowledge champion alert! Your update keeps us all moving forward.",
            "🔁 Sync complete! This kind of effort is what makes teamwork work.",
            "💥 Update magic! You’ve added real value—thanks for keeping things sharp.",
            "🌈 Documentation wizardry! A small update, a big impact.",
            "🥳 Celebrate that save! Another step toward a more organized, efficient team."
        ];

        if (!confettiBox || typeof AP === "undefined") return;

        if (outputType === "preview") {
            confettiBox.style.display = "none";
            confettiBox.style.height = "0px";
            AP.resize("0px", "0px");
            setTimeout(() => AP.resize("0px", "0px"), 100);
            return
        }

        confettiBox.style.display = "none";
        confettiBox.style.height = "0px";
        confettiBox.innerHTML = "";
        AP.resize("0px", "0px");

        AP.request({
            url: "/api/v2/pages/" + pageId + "/versions",
            type: "GET",
            success: function (responseText) {
                try {
                    const data = JSON.parse(responseText);
                    const currentVersion = data.results?.[0]?.number;
                    if (!currentVersion) return;

                    const storedVersion = parseInt(localStorage.getItem(storageKey), 10);
                    const isNew = !storedVersion || currentVersion > storedVersion;

                    if (isNew) {
                        localStorage.setItem(storageKey, currentVersion.toString());

                        const message = messages[Math.floor(Math.random() * messages.length)];

                        confettiBox.innerHTML =
                        '<div id="confetti-msg" style="' +
                        'text-align: center;' +
                        'color: #ff4081;' +
                        'font-size: 1.8rem;' +
                        'font-weight: bold;' +
                        'padding: 2rem 1rem;' +
                        'font-family: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;' +
                        '">' +
                        message +
                        '</div>';

                        confettiBox.style.display = "block";
                        confettiBox.style.height = "500px";
                        AP.resize("100%", "500px");

                        if (window.confetti) {
                            window.confetti({
                                particleCount: 200,
                                spread: 70,
                                origin: {
                                    y: 0.6
                                }
                            });
                        }

                        setTimeout(() => {
                            const msg = document.getElementById("confetti-msg");
                            if (msg) {
                                msg.classList.add("fade-out");
                            }

                            setTimeout(() => {
                                confettiBox.innerHTML = "";
                                confettiBox.style.display = "none";
                                confettiBox.style.height = "0px";
                                AP.resize("0px", "0px");
                                setTimeout(() => AP.resize("0px", "0px"), 100);
                            }, fadeDuration);
                        },
                            confettiDuration);

                    } else {
                        confettiBox.innerHTML = "";
                        confettiBox.style.display = "none";
                        confettiBox.style.height = "0px";
                        AP.resize("0px", "0px");
                        setTimeout(() => AP.resize("0px", "0px"), 100);
                    }

                } catch (e) {
                    console.error("Failed to parse version response:",
                        e);
                }
            },
            error: function (err) {
                console.error("API request failed:",
                    err);
            }
        });
    })();
</script>