Components / Motion / Text reveal
Text reveal beta
A headline that arrives word by word, each word rising into place. Settles once, then stays.
Parts you copy. Code you own. Motion with a machined finish.
<p data-reveal style="color:var(--k-ink);font-size:clamp(22px,4vw,30px);font-weight:600;letter-spacing:-.02em;line-height:1.35;max-width:22em">Parts you copy. Code you own. Motion with a machined finish.</p>
/* KATA Text reveal */
.k-rv-w{display:inline-block;overflow:hidden;vertical-align:bottom;padding-bottom:.12em;margin-bottom:-.12em}
.k-rv-i{display:inline-block;transform:translateY(115%);transition:transform .7s cubic-bezier(.3,.7,.3,1);transition-delay:calc(var(--i,0)*45ms)}
[data-reveal].in .k-rv-i{transform:none}
@media (prefers-reduced-motion: reduce){.k-rv-i{transform:none;transition:none}}
// KATA Text reveal — split into words, rise once when visible.
document.querySelectorAll('[data-reveal]').forEach(function (el) {
var words = el.textContent.trim().split(/\s+/);
el.setAttribute('aria-label', el.textContent.trim());
el.textContent = '';
words.forEach(function (w, i) {
var outer = document.createElement('span');
outer.className = 'k-rv-w';
outer.setAttribute('aria-hidden', 'true');
var inner = document.createElement('span');
inner.className = 'k-rv-i';
inner.style.setProperty('--i', i);
inner.textContent = w;
outer.appendChild(inner);
el.appendChild(outer);
el.appendChild(document.createTextNode(' '));
});
if (!('IntersectionObserver' in window) || window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.classList.add('in');
return;
}
var io = new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) {
el.classList.add('in');
io.disconnect();
}
}, { threshold: 0.35 });
io.observe(el);
});
Usage
For one headline per page — the hero, a section opening. The script splits the text into words; the stagger is 45ms per word and the whole thing runs once.
Accessibility
Without the script, the text is simply text. Screen readers read the original sentence; reduced-motion users get it static.