Just simple CSS, JS and HTML marquee.
You can duplicate as many marquee as you want on the same page easy to setup thanks to html attributes 🙂
Setup your html thats it.
<div class="marquee-container" data-text="White Label the Name, Custom Post Types, Custom Fields, Custom Taxonomies, Security Settings, Block Editor Settings, Disable File Editing, Disable WP JSON If Not Logged In" data-font-size="50" data-speed="20" data-direction="left"> </div> <div class="marquee-container" data-text="SMTP Settings, Mail Logs, 404 Logs, Custom Login/Register Page, 301 Redirects, Remove WP Version, WP Revision Limit" data-font-size="50" data-speed="25" data-direction="right"> </div> <style> .marquee-container { overflow: hidden; white-space: nowrap; margin-bottom: 20px; } .marquee { display: inline-block; white-space: nowrap; } .marquee-text { display: inline-block; white-space: nowrap; color: #00000044; line-height: 1; font-weight: 200; } </style> <script> class Marquee { constructor(container, settings) { this.container = container; this.settings = settings; this.animationStyleElement = null; this.init(); } // Initialize the marquee: set font size and create the track element init() { this.container.style.fontSize = this.settings.fontSize + 'px'; let marquee = this.container.querySelector('.marquee'); if (!marquee) { marquee = document.createElement('div'); marquee.className = 'marquee'; this.container.appendChild(marquee); } this.marquee = marquee; this.setupMarquee(); } // Setup the marquee by measuring text, duplicating content, and creating animation setupMarquee() { // Clear any existing content in the marquee track this.marquee.innerHTML = ''; // Create a temporary element to measure the width of one copy of the text const tempSpan = document.createElement('span'); tempSpan.className = 'marquee-text'; tempSpan.style.visibility = 'hidden'; tempSpan.style.position = 'absolute'; tempSpan.textContent = this.settings.text; document.body.appendChild(tempSpan); const textWidth = tempSpan.getBoundingClientRect().width; document.body.removeChild(tempSpan); // Get the container width const containerWidth = this.container.getBoundingClientRect().width; // Calculate the number of copies needed for a seamless loop let copies = Math.ceil((containerWidth + textWidth) / textWidth); copies = Math.max(copies, 2); // Ensure at least two copies // Append copies of the text into the marquee track for (let i = 0; i < copies; i++) { const span = document.createElement('span'); span.className = 'marquee-text'; span.textContent = this.settings.text; this.marquee.appendChild(span); } // Determine the animation duration (in seconds) const duration = textWidth / this.settings.speed; // Create a unique animation name (useful for multiple marquees) const animationName = 'marqueeAnimation_' + Math.random().toString(36).substr(2, 9); // Create dynamic keyframes based on the direction let keyframes; if (this.settings.direction === 'left') { keyframes = ` @keyframes ${animationName} { 0% { transform: translateX(0); } 100% { transform: translateX(-${textWidth}px); } } `; this.marquee.style.transform = 'translateX(0)'; } else if (this.settings.direction === 'right') { keyframes = ` @keyframes ${animationName} { 0% { transform: translateX(-${textWidth}px); } 100% { transform: translateX(0); } } `; this.marquee.style.transform = `translateX(-${textWidth}px)`; } // Remove any previous dynamic style element if it exists if (this.animationStyleElement) { this.animationStyleElement.remove(); } // Insert the dynamic keyframes into a new style element in the document head this.animationStyleElement = document.createElement('style'); this.animationStyleElement.innerHTML = keyframes; document.head.appendChild(this.animationStyleElement); // Force reflow to ensure the animation starts properly void this.marquee.offsetWidth; // Apply the animation to the marquee track this.marquee.style.animation = `${animationName} ${duration}s linear infinite`; } // Update the marquee (for example on window resize) update() { this.setupMarquee(); } } document.addEventListener('DOMContentLoaded', function() { // Array to store marquee instances for later updates (e.g., on resize) const marqueeInstances = []; // Select all elements with the class "marquee-container" const containers = document.querySelectorAll('.marquee-container'); containers.forEach(container => { // Retrieve settings from data attributes, with defaults if not provided const settings = { text: container.getAttribute('data-text') || '', fontSize: parseInt(container.getAttribute('data-font-size')) || 20, speed: parseFloat(container.getAttribute('data-speed')) || 20, direction: container.getAttribute('data-direction') || 'left' }; marqueeInstances.push(new Marquee(container, settings)); }); // On window resize, update all marquees for responsiveness window.addEventListener('resize', () => { marqueeInstances.forEach(instance => instance.update()); }); }); </script>