Highlighter Reveal
A marker stroke swipes across behind a phrase, as if someone just highlighted the bit that matters. It follows the text across line breaks.
We ship something new every week, not once a year.
How to use it
- Paste the snippet just before
</head>. - Wrap the phrase in
<mark data-highlighter-reveal>(or any inline element).
Options
Add any of these attributes to the same element.
data-color | Marker colour. Default soft purple. |
data-height | How much of the line it covers, e.g. 40% for a low marker line. Default 100%. |
data-loop | Swipe away and back again. |
data-duration | Time for the swipe. Default 900 ms. |
data-hold | How long it stays, when looping. Default 1800 ms. |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Highlighter Reveal: a marker stroke swipes across behind a phrase, like
// someone highlighting the important bit. Wraps across lines naturally.
// Usage: <p>We ship <mark data-highlighter-reveal>every single week</mark>.</p>
// Optional: data-color (default soft purple), data-height (how much of the line
// the stroke covers, e.g. "40%" for a low marker line; default 100%),
// data-duration (ms), data-loop (swipe away and back again), data-hold (ms).
var DEFAULTS = { color: 'rgba(168, 85, 247, 0.32)', height: '100%', duration: 900, hold: 1800 };
var css =
'[data-highlighter-reveal]{background-color:transparent;color:inherit;background-repeat:no-repeat;' +
'background-position:0 100%;-webkit-box-decoration-break:clone;box-decoration-break:clone;padding:0 0.12em;border-radius:0.15em}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function run(el) {
var d = el.dataset;
var h = d.height || DEFAULTS.height;
var duration = +d.duration || DEFAULTS.duration;
var loop = d.loop !== undefined && d.loop !== 'false';
function swipeIn() {
el.style.backgroundPosition = '0 100%';
anime.animate(el, {
backgroundSize: ['0% ' + h, '100% ' + h],
duration: duration,
ease: 'inOut(3)',
onComplete: loop ? function () { setTimeout(swipeOut, +d.hold || DEFAULTS.hold); } : undefined
});
}
function swipeOut() {
// Leave to the right, the way a pen would lift off.
el.style.backgroundPosition = '100% 100%';
anime.animate(el, {
backgroundSize: ['100% ' + h, '0% ' + h],
duration: duration * 0.7,
ease: 'inOut(3)',
onComplete: function () { setTimeout(swipeIn, 400); }
});
}
swipeIn();
}
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);
});
}, { threshold: 0.6 });
document.querySelectorAll('[data-highlighter-reveal]').forEach(function (el) {
if (el._hr) return;
el._hr = true;
var d = el.dataset;
var color = d.color || DEFAULTS.color;
el.style.backgroundImage = 'linear-gradient(' + color + ',' + color + ')';
el.style.backgroundSize = (reduced ? '100% ' : '0% ') + (d.height || DEFAULTS.height);
if (!reduced) io.observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><p>We ship <mark data-highlighter-reveal>every single week</mark>.</p>
<!-- a low yellow marker line: -->
<mark data-highlighter-reveal data-color="#fde047" data-height="40%">important</mark>