Seamless infinite-scrolling marquee with images and text. WPBakery container/child pattern for drag-and-drop item management. Configurable speed, direction, and spacing.
Replaced CSS animation: infinite with requestAnimationFrame for truly seamless scrolling.
CSS keyframe animations cause a brief repaint/flash when the infinite loop restarts (going from 100% back to 0%). This is a browser-level issue that can't be fixed with better math.
The fix: drive the scroll entirely with requestAnimationFrame. JavaScript updates translateX every frame at 60fps. When the position exceeds the scroll distance, it resets by adding/subtracting the distance — since the clones are visually identical, the reset is completely imperceptible. No animation restart = no flash.
Also removed the now-unnecessary CSS keyframes.
Instead of manually calculating the scroll distance (summing item widths + gaps in JS), we now clone one full set of items first, then measure the actual rendered distance between the first original and first clone using getBoundingClientRect().
This eliminates any sub-pixel rounding mismatch between our JavaScript calculation and the browser's flex gap rendering — the browser tells us the exact distance, so the animation loop point is truly seamless.
Lazy loading was causing the marquee to jump at the loop point.
The site's lazy loader (LazySizes/WP Rocket/etc.) replaces src with a 1x1 transparent GIF placeholder and stores the real URL in data-src. When our JS cloned items for the infinite loop, the clones kept the placeholder GIFs — rendering at 0 width — while the scroll distance was calculated from the originals' real widths.
Fix:
data-src → src before measuring and cloning<noscript> fallback tags from clonesdata-src, data-srcset, data-lazy-src, data-lazy-srcsetwpautop was inserting invisible <p> and <br> tags between child shortcodes, creating stray flex children that threw off the scroll distance calculationwpautop junk before rendering child shortcodes.jwwd-marquee-item elementsgetBoundingClientRect() for sub-pixel accurate width measurementUse full-resolution source images instead of WordPress 'medium' (300px) thumbnails. CSS height constraint handles sizing.
The Marquee is now a container element with draggable Marquee Item children — just like "Icon with text" or "FAQ Section" work in WPBakery.
How it works:
[jwwd_marquee speed="30" direction="left" gap="40" height="50"]
[jwwd_marquee_item image="123" title="Company A"]
[jwwd_marquee_item title="Breaking news text here"]
[jwwd_marquee_item image="456"]
[/jwwd_marquee]attach_images)[jwwd_marquee images="123,456,789" texts="Line one\nLine two" speed="30" direction="left" gap="40" height="50"][jwwd_marquee speed="30" direction="left" gap="40" height="50" items="..."]
Items are now passed via WPBakery's param_group repeater attribute.
height shortcode attribute (default 50px) constrains image dimensionswill-changejwwd_github_token option (Settings → General)jwwd_marquee_github_token optionJWWD_GITHUB_TOKEN constant in wp-config.php[jwwd_marquee speed="30" direction="left" gap="40" height="50"]Complete rebrand from HSAS Marquee to JWWD Marquee.
hsas_ prefixes to jwwd_marquee_jwwd_github_tokenhsas_marquee_message to jwwd_marquee_message[hsas_marquee] shortcode still works