Shine Sweep
A bright, slanted glint passes across the letters every few seconds, like light catching polished metal. The text keeps its own colour; only the letters catch the light.
Gold standard
How to use it
- Paste the snippet just before
</head>. - Add
data-shine-sweepto any heading or text element.
Options
Add any of these attributes to the same element.
data-trigger | view (default) shines every few seconds while on screen. hover shines when the pointer arrives. |
data-color | The glint’s colour. Default white. |
data-interval | Time between glints. Default 2800 ms. |
data-duration | Time for one pass. Default 1100 ms. |
Shows up best on coloured or dark text. On white text, use a coloured glint.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Shine Sweep: a bright glint passes across the letters every few seconds,
// like light catching polished metal. The text keeps its own colour.
// Usage: <h1 data-shine-sweep>Premium</h1>
// Optional: data-trigger ("view", the default, shines every few seconds
// while on screen; "hover" shines when the pointer arrives), data-color
// (the glint; default white), data-interval (ms between glints; default
// 2800), data-duration (ms for one pass; default 1100).
var DEFAULTS = { color: 'rgba(255, 255, 255, 0.95)', interval: 2800, duration: 1100 };
// Where the glint layer sits before and after a pass: just off the left
// edge, then just off the right. (The layer is twice as wide as the text,
// and background percentages are odd: these move it by about 1.4 widths.)
var FROM = '120% 0%';
var TO = '-20% 0%';
var css =
'.shn{background-repeat:no-repeat;background-size:200% 100%;background-position:' + FROM + ';' +
'-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;' +
'-webkit-box-decoration-break:clone;box-decoration-break:clone}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
// The text's own colour fills the letters (as a background colour, clipped
// to the text), with a narrow, slanted glint on top.
function paint(el) {
var glint = el.dataset.color || DEFAULTS.color;
el.style.backgroundColor = getComputedStyle(el).color;
el.style.backgroundImage = 'linear-gradient(110deg, transparent 40%, ' + glint + ' 50%, transparent 60%)';
el.classList.add('shn');
}
function shine(el) {
return anime.animate(el, {
backgroundPosition: [FROM, TO],
duration: +el.dataset.duration || DEFAULTS.duration,
ease: 'inOutSine'
});
}
function init() {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
document.querySelectorAll('[data-shine-sweep]').forEach(function (el) {
if (el._shn) return;
el._shn = true;
paint(el);
var d = el.dataset;
var busy = false;
function once() {
if (busy) return;
busy = true;
shine(el).then(function () { busy = false; });
}
if (d.trigger === 'hover') {
el.addEventListener('pointerenter', once);
return;
}
// Only shine while visible, starting soon after it scrolls in.
var timer = null;
new IntersectionObserver(function (entries) {
clearInterval(timer);
if (!entries[0].isIntersecting) return;
setTimeout(once, 400);
timer = setInterval(once, +d.interval || DEFAULTS.interval);
}, { threshold: 0.4 }).observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><h1 data-shine-sweep>Gold standard</h1>
<!-- shine on hover, e.g. a pricing tier: -->
<h3 data-shine-sweep data-trigger="hover">Pro</h3>