Gradient Sweep
A band of colours flows continuously through the letters, like light moving through liquid. It loops seamlessly and rests while the text is off screen.
Colour in motion
How to use it
- Paste the snippet just before
</head>. - Add
data-gradient-sweepto any heading or text element.
Options
Add any of these attributes to the same element.
data-colors | Colours to flow through, comma separated. Default purple, pink and amber. |
data-speed | Time for the colours to travel one full cycle. Default 4000 ms. |
data-direction | right (default) or left. |
The gradient replaces the text’s colour. With reduced motion turned on it stays put.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Gradient Sweep: a band of colours flows continuously through the letters,
// like light moving through liquid. Keeps going while the text is on screen.
// Usage: <h1 data-gradient-sweep>Full spectrum</h1>
// Optional: data-colors (comma separated; default purple, pink, amber),
// data-speed (ms for the colours to travel one full cycle; default 4000),
// data-direction ("right", the default, or "left").
var DEFAULTS = { colors: '#6a0dad, #ec4899, #f59e0b', speed: 4000 };
var css =
'.gsw{background-repeat:repeat-x;background-size:200% 100%;-webkit-background-clip:text;background-clip:text;' +
'-webkit-text-fill-color:transparent;color:transparent;-webkit-box-decoration-break:clone;box-decoration-break:clone}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function paint(el) {
var colors = (el.dataset.colors || DEFAULTS.colors).split(',').map(function (c) { return c.trim(); });
// The gradient is two text-widths long and ends on the colour it starts
// with, so it tiles seamlessly as it slides.
el.style.backgroundImage = 'linear-gradient(90deg, ' + colors.concat(colors[0]).join(', ') + ')';
el.classList.add('gsw');
}
function start(el) {
var d = el.dataset;
// With a 200% wide background, shifting the position by 200% moves it
// exactly one tile.
var shift = d.direction === 'left' ? ['0% 50%', '200% 50%'] : ['200% 50%', '0% 50%'];
return anime.animate(el, {
backgroundPosition: shift,
duration: +d.speed || DEFAULTS.speed,
ease: 'linear',
loop: true
});
}
function init() {
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
document.querySelectorAll('[data-gradient-sweep]').forEach(function (el) {
if (el._gsw) return;
el._gsw = true;
paint(el);
// Reduced motion: the gradient stays still.
if (reduced) return;
var anim = null;
// Only flow while visible, to save battery.
new IntersectionObserver(function (entries) {
if (!entries[0].isIntersecting) { if (anim) anim.pause(); return; }
if (anim) anim.play();
else anim = start(el);
}).observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><h1 data-gradient-sweep>Colour in motion</h1>
<!-- your own colours, flowing left: -->
<h1 data-gradient-sweep data-colors="#0ea5e9, #a855f7, #ec4899" data-direction="left">Colour in motion</h1>