Citaat dat zich onthult New
A customer quote where the opening mark swings in large first, then the words rise one by one out of a mask, the closing mark lands and the name slides in with a short rule. Plays when the quote enters the viewport. For case studies and reviews on service pages; built as figure, blockquote and figcaption.
Binnen een week hing de nieuwe cv-ketel, en na afloop hebben ze zelfs de trap gestofzuigd.
How to use it
- Paste the HTML where the text animations should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Citaat dat zich onthult · NedDev · eigen werk. -->
<div class="jouw-container">
<figure class="cto">
<span class="cto__m cto__m--open" aria-hidden="true">“</span>
<blockquote class="cto__q"><p class="cto__p">Binnen een week hing de nieuwe cv-ketel, en na afloop hebben ze zelfs de trap gestofzuigd.</p></blockquote>
<figcaption class="cto__c"><span class="cto__lijn" aria-hidden="true"></span><span><strong>Ingrid de Wit</strong>, Leeuwarden · cv-ketel vervangen</span></figcaption>
<button class="cto__knop" type="button">Opnieuw afspelen</button>
</figure>
</div>/* Citaat dat zich onthult · NedDev · eigen werk. */
.cto{position:absolute;inset:0;margin:0;display:flex;flex-direction:column;justify-content:center;padding:16px 22px 16px 24px;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;background:#fff;text-align:left}
.cto__m{display:block;height:34px;font-size:84px;font-weight:700;line-height:.9;color:#F5480F;transform-origin:30% 70%}
.cto__q{margin:0}
.cto__p{margin:0;font-size:clamp(17px,4.6cqw,21px);font-weight:600;line-height:1.3;letter-spacing:-.012em}
.cto{container-type:inline-size}
.cto__wd{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:.08em;margin-bottom:-.08em}
.cto__wi{display:inline-block}
.cto__dicht{color:#F5480F;font-weight:700}
.cto__c{display:flex;align-items:center;gap:8px;margin-top:12px;font-size:12.5px;line-height:1.35;color:#4a4a4a}
.cto__c strong{color:#0a0a0a;font-weight:600}
.cto__lijn{flex:none;width:22px;height:2px;background:#F5480F;transform-origin:left}
.cto__knop{position:absolute;right:12px;top:12px;min-height:28px;padding:5px 9px;border:1px solid rgba(10,10,10,.25);border-radius:4px;background:#fff;color:#0a0a0a;font:500 12px/1 'Switzer',system-ui,sans-serif;cursor:pointer}
.cto__knop:hover{border-color:#0a0a0a}
.cto__knop:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.cto.is-js .cto__m--open{opacity:0;transform:scale(.4) rotate(-14deg)}
.cto.is-js .cto__wi{transform:translateY(105%)}
.cto.is-js .cto__dicht{opacity:0}
.cto.is-js .cto__c{opacity:0;transform:translateX(-8px)}
.cto.is-js .cto__lijn{transform:scaleX(0)}
.cto.is-js.is-in .cto__m--open{opacity:1;transform:none;transition:opacity .35s ease,transform .6s cubic-bezier(.3,1.4,.5,1)}
.cto.is-js.is-in .cto__wi{transform:none;transition:transform .55s cubic-bezier(.2,.8,.2,1);transition-delay:calc(.3s + var(--i) * 45ms)}
.cto.is-js.is-in .cto__dicht{opacity:1;transition:opacity .3s ease;transition-delay:calc(.35s + var(--n) * 45ms)}
.cto.is-js.is-in .cto__c{opacity:1;transform:none;transition:opacity .35s ease,transform .35s ease;transition-delay:calc(.5s + var(--n) * 45ms)}
.cto.is-js.is-in .cto__lijn{transform:none;transition:transform .4s ease;transition-delay:calc(.6s + var(--n) * 45ms)}
@media (prefers-reduced-motion:reduce){.cto .cto__m--open,.cto .cto__wi,.cto .cto__dicht,.cto .cto__c,.cto .cto__lijn{opacity:1!important;transform:none!important;transition:none!important}.cto__knop{display:none}}// Citaat dat zich onthult · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
const w = root.querySelector('.cto'); if (!w) return;
const p = w.querySelector('.cto__p');
const woorden = p.textContent.trim().split(/\s+/);
p.textContent = '';
woorden.forEach((wd, i) => {
const o = document.createElement('span'); o.className = 'cto__wd';
const b = document.createElement('span'); b.className = 'cto__wi'; b.style.setProperty('--i', i);
b.textContent = wd; o.appendChild(b); p.appendChild(o);
if (i < woorden.length - 1) p.appendChild(document.createTextNode(' '));
});
const d = document.createElement('span'); d.className = 'cto__dicht'; d.setAttribute('aria-hidden', 'true'); d.textContent = '”';
p.lastChild.firstChild.appendChild(d);
w.style.setProperty('--n', woorden.length);
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
w.classList.add('is-js');
const speel = () => { w.classList.remove('is-in'); void w.offsetWidth; requestAnimationFrame(() => w.classList.add('is-in')); };
if ('IntersectionObserver' in window) {
const io = new IntersectionObserver(e => { if (e[0].isIntersecting) { speel(); io.disconnect(); } }, { threshold: .5 });
io.observe(w);
} else w.classList.add('is-in');
w.querySelector('.cto__knop').addEventListener('click', speel);
}
init(document.querySelector('.jouw-container'));
More text animations
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →