Blinking Cursor
A caret at the end of your text, blinking as if waiting for the next keystroke. Choose a thin bar, a terminal block or an underscore, and a soft fade or a hard on-off blink.
Your move
~/site $ deploy
READY
How to use it
- Paste the snippet just before
</head>. - Add
data-blinking-cursorto a heading or line of text. The caret goes at the end.
Options
Add any of these attributes to the same element.
data-shape | bar (default), block or underscore. |
data-blink | smooth (default) fades in and out. hard switches on and off like a terminal. |
data-rate | Time for one full blink. Default 1060 ms. |
data-color | The caret’s colour. Default the text’s. |
It only blinks while on screen. With reduced motion turned on the caret stays steady.
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
<script>
(function () {
// Blinking Cursor: a caret at the end of your text, blinking as if waiting
// for the next keystroke. Pick a thin bar, a terminal block or an
// underscore, and a soft fade or a hard on-off blink.
// Usage: <h1 data-blinking-cursor>Your move</h1>
// Optional: data-shape ("bar" (default), "block" or "underscore"),
// data-blink ("smooth" (default) or "hard"), data-rate (ms per blink,
// default 1060), data-color (the caret's colour; default the text's).
var DEFAULTS = { rate: 1060 };
var css =
'.bcr{display:inline-block;margin-left:0.06em;background:currentColor;border-radius:0.02em;pointer-events:none}' +
'.bcr-bar{width:0.07em;height:1em;vertical-align:-0.12em}' +
'.bcr-block{width:0.56em;height:1.05em;vertical-align:-0.18em;opacity:.85}' +
'.bcr-underscore{width:0.55em;height:0.08em;vertical-align:-0.06em}';
var style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
function add(el) {
var d = el.dataset;
var shape = /^(block|underscore)$/.test(d.shape) ? d.shape : 'bar';
var caret = document.createElement('span');
caret.className = 'bcr bcr-' + shape;
caret.setAttribute('aria-hidden', 'true');
if (d.color) caret.style.background = d.color;
el.appendChild(caret);
return caret;
}
function start(el, caret) {
var d = el.dataset;
var half = (+d.rate || DEFAULTS.rate) / 2;
var hard = d.blink === 'hard';
var base = parseFloat(getComputedStyle(caret).opacity) || 1;
return anime.createTimeline({ loop: true })
// On...
.add(caret, { opacity: base, duration: hard ? 1 : half * 0.3, ease: 'out(2)' }, 0)
// ...hold...
.add(caret, { opacity: 0, duration: hard ? 1 : half * 0.45, ease: 'inOut(2)' }, half)
// ...off, and hold until the next blink.
.add(caret, { opacity: 0, duration: 1 }, half * 2 - 1);
}
function init() {
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
document.querySelectorAll('[data-blinking-cursor]').forEach(function (el) {
if (el._bcr) return;
var caret = (el._bcr = add(el));
// Reduced motion: a steady caret.
if (reduced) return;
var tl = null;
// Blink only while visible, to save battery.
new IntersectionObserver(function (entries) {
if (!entries[0].isIntersecting) { if (tl) tl.pause(); return; }
if (tl) tl.play();
else tl = start(el, caret);
}).observe(el);
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
</script><h1 data-blinking-cursor>Your move</h1>
<!-- a terminal-style block: -->
<p data-blinking-cursor data-shape="block" data-blink="hard">~/site $</p>