Striped Shadow Text
A drop shadow made of fine diagonal stripes sits just behind the text, and the stripes scroll steadily along it, like a retro poster that won’t keep still. Hover or touch the text and the shadow pushes further out.
Old school
How to use it
- Paste the snippet just before
</head>. - Add
data-striped-shadow-textto a heading. Big, heavy type shows the stripes best.
Options
Add any of these attributes to the same element.
data-color | Stripe colour. Default #a855f7. |
data-depth | How far the shadow sits from the text, in em. Default 0.1. |
data-size | Distance between stripes, in em. Default 0.12. |
data-speed | How long the stripes take to move along by one stripe. Default 700 ms; 0 keeps them still. |
data-direction | right (default) or left. |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Striped Shadow Text: a drop shadow made of diagonal stripes sits behind
// the text and the stripes scroll steadily along it, like a retro poster.
// Hovering or touching the text pushes the shadow out further.
// Usage: <h1 data-striped-shadow-text>Retro</h1>
// Optional: data-color (stripe colour; default #a855f7), data-depth (how far
// the shadow sits from the text, in em; default 0.1), data-size (distance
// between stripes, in em; default 0.12), data-speed (ms for the stripes to
// move along by one stripe; default 700; 0 keeps them still),
// data-direction (right or left; default right).
var DEFAULTS = { color: '#a855f7', depth: 0.1, size: 0.12, speed: 700 };
var css =
'.sst-wrap{position:relative;display:inline-block;isolation:isolate}' +
'.sst-shadow{position:absolute;inset:0;z-index:-1;color:transparent;-webkit-text-fill-color:transparent;-webkit-text-stroke:0;' +
'-webkit-background-clip:text;background-clip:text;pointer-events:none;user-select:none;will-change:transform}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function reduced() { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; }
function setup(el) {
var d = el.dataset;
var color = d.color || DEFAULTS.color;
var depth = d.depth !== undefined ? +d.depth : DEFAULTS.depth;
var size = +d.size || DEFAULTS.size;
var speed = d.speed !== undefined ? +d.speed : DEFAULTS.speed;
var dir = d.direction === 'left' ? -1 : 1;
// Wrap the content and put a copy of it behind as the shadow.
var wrap = document.createElement('span');
wrap.className = 'sst-wrap';
while (el.firstChild) wrap.appendChild(el.firstChild);
var shadow = document.createElement('span');
shadow.className = 'sst-shadow';
shadow.setAttribute('aria-hidden', 'true');
shadow.textContent = wrap.textContent;
shadow.style.backgroundImage = 'linear-gradient(-45deg,' + color + ' 25%,transparent 25%,transparent 50%,' + color + ' 50%,' + color + ' 75%,transparent 75%)';
shadow.style.backgroundSize = size + 'em ' + size + 'em';
wrap.insertBefore(shadow, wrap.firstChild);
el.appendChild(wrap);
var push = { depth: depth };
function place() { shadow.style.transform = 'translate(' + push.depth + 'em,' + push.depth + 'em)'; }
place();
if (reduced()) return;
// Scroll the stripes: one tile per cycle loops seamlessly.
var timer = null;
if (speed > 0) {
timer = anime.createTimer({
duration: speed,
loop: true,
autoplay: false,
onUpdate: function (t) {
var p = (t.currentTime % speed) / speed;
shadow.style.backgroundPosition = (dir * p * size).toFixed(4) + 'em 0';
}
});
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) timer.play(); else timer.pause();
}).observe(el);
}
// Hover or touch pushes the shadow out, letting go settles it back.
function out(to) {
anime.animate(push, { depth: to, duration: to > depth ? 380 : 700, ease: to > depth ? 'outBack(2)' : 'outElastic(1, .5)', onUpdate: place });
}
el.addEventListener('pointerenter', function () { out(depth * 1.9 + 0.02); });
el.addEventListener('pointerleave', function () { out(depth); });
el.addEventListener('pointercancel', function () { out(depth); });
el.addEventListener('pointerup', function (e) { if (e.pointerType !== 'mouse') out(depth); });
el.addEventListener('pointerdown', function (e) { if (e.pointerType !== 'mouse') out(depth * 1.9 + 0.02); });
}
function init() {
document.querySelectorAll('[data-striped-shadow-text]').forEach(function (el) {
if (el._sst) return;
el._sst = true;
setup(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><h1 data-striped-shadow-text>Old school</h1>
<!-- a deeper orange shadow, scrolling slowly to the left: -->
<h1 data-striped-shadow-text data-color="#f97316" data-depth="0.16" data-speed="1400" data-direction="left">Old school</h1>