Word Fade Swap
One word in your sentence dissolves into the next: it blurs and lifts away while the new word sharpens into place. Calm, soft and easy to read.
Made for designers
How to use it
- Paste the snippet just before
</head>. - Wrap the changing word in a
<span>and list every word, separated by commas, indata-word-fade-swap.
Options
Add any of these attributes to the same element.
data-hold | How long each word stays. Default 2000 ms. |
data-duration | Time for each swap. Default 700 ms. |
data-blur | How blurry words get, in px. Default 10. |
The slot is as wide as the longest word, so the sentence never shifts.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Word Fade Swap: one word in a sentence dissolves into the next, blurring
// out as it lifts away while the new word sharpens into place.
// Usage: <h1>Made for <span data-word-fade-swap="designers, developers, everyone">designers</span></h1>
// Optional: data-hold (ms per word, default 2000), data-duration (ms per
// swap, default 700), data-blur (px, default 10).
var DEFAULTS = { hold: 2000, duration: 700, blur: 10 };
var css =
'.wfs{display:inline-grid;vertical-align:baseline;text-align:left}' +
'.wfs-word{grid-area:1/1;white-space:nowrap}' +
'.wfs-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function build(el) {
var words = (el.getAttribute('data-word-fade-swap') || el.textContent)
.split(',').map(function (w) { return w.trim(); }).filter(Boolean);
el.classList.add('wfs');
el.textContent = '';
var sr = document.createElement('span');
sr.className = 'wfs-sr';
sr.textContent = words.join(', ');
el.appendChild(sr);
// Every word sits in the same grid cell, so the box is as wide as the
// longest one and the sentence never shifts.
return words.map(function (w, i) {
var s = document.createElement('span');
s.className = 'wfs-word';
s.textContent = w;
s.setAttribute('aria-hidden', 'true');
if (i) s.style.opacity = 0;
el.appendChild(s);
return s;
});
}
function run(el, spans) {
var d = el.dataset;
var duration = +d.duration || DEFAULTS.duration;
var blur = (+d.blur || DEFAULTS.blur) + 'px';
var i = 0;
setInterval(function () {
var out = spans[i];
i = (i + 1) % spans.length;
var inn = spans[i];
anime.animate(out, {
opacity: [1, 0], filter: ['blur(0px)', 'blur(' + blur + ')'], y: ['0em', '-0.25em'],
duration: duration, ease: 'in(2)'
});
anime.animate(inn, {
opacity: [0, 1], filter: ['blur(' + blur + ')', 'blur(0px)'], y: ['0.25em', '0em'],
duration: duration, delay: duration * 0.35, ease: 'out(3)'
});
}, (+d.hold || DEFAULTS.hold) + duration);
}
function init() {
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
io.unobserve(e.target);
run(e.target, e.target._wfs);
});
}, { threshold: 0.5 });
document.querySelectorAll('[data-word-fade-swap]').forEach(function (el) {
if (el._wfs) return;
el._wfs = build(el);
if (!reduced && el._wfs.length > 1) io.observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><h1>
Made for
<span data-word-fade-swap="designers, developers, everyone">designers</span>
</h1>