<!--------- Osmo [https://osmo.supply/] --------->
<section class="cloneable">
<div class="text-demo-wrap">
<h1 data-split="heading" class="text-demo-h">We’re using GSAP’s SplitText to break this content into lines, words, and individual characters. Experiment with staggered tweens, custom ease functions, and dynamic transforms to bring your headlines to life.</h1>
<div class="text-demo-buttons">
<button data-split="button" data-split-type="lines" class="text-demo-button"><span>Lines</span></button>
<button data-split="button" data-split-type="words" class="text-demo-button"><span>Words</span></button>
<button data-split="button" data-split-type="letters" class="text-demo-button"><span>Letters</span></button>
</div>
</div>
</section>
<div class="osmo-credits">
<p class="osmo-credits__p">Resource by <a target="_blank" href="https://www.osmo.supply?utm_source=codepen&utm_medium=pen&utm_campaign=masked-text-reveal" class="osmo-credits__p-a">Osmo</a>
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/SplitText.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/CustomEase.min.js"></script>
/* ------- Osmo [https://osmo.supply/] ------- */
/* Osmo UI: https://slater.app/10324/23333.css */
body {
background-color: #d8e1ed;
font-family: PP Neue Montreal, Arial, sans-serif;
color: #340824;
font-size: 1vw;
margin: 0;
padding: 0;
overscroll-behavior: none;
min-height: 100%;
cursor: url("https://cdn.prod.website-files.com/6708f85ff3d3cba6aff436fb/671251b239d7aeb290a31ac5_cursor-default%402x.svg")
2 0,
auto;
}
a,
button {
cursor: url("https://cdn.prod.website-files.com/6708f85ff3d3cba6aff436fb/671251b212e6b71494aa67ff_cursor-pointer%402x.svg")
12 0,
pointer;
}
.cloneable {
justify-content: center;
align-items: center;
min-height: 100vh;
display: flex;
}
.text-demo-wrap {
grid-column-gap: 4.5em;
grid-row-gap: 4.5em;
flex-flow: column;
justify-content: flex-start;
align-items: center;
width: 100%;
max-width: 80em;
margin-left: auto;
margin-right: auto;
padding-left: 1.25em;
padding-right: 1.25em;
display: flex;
}
.text-demo-h {
text-align: center;
margin-top: 0;
margin-bottom: 0;
font-size: 3.75vw;
font-weight: 500;
line-height: 1.15;
}
.text-demo-buttons {
grid-column-gap: 1.25em;
grid-row-gap: 1.25em;
justify-content: center;
align-items: center;
display: flex;
}
.text-demo-button {
background-color: #fff;
border-radius: 100em;
padding: .625em 1.25em;
font-size: 2em;
line-height: 1;
transition: color .15s, background-color .15s;
border: 0;
outline: 0;
box-shadhow: 0;
}
.text-demo-button:hover {
color: #fff;
background-color: #340824;
}
.osmo-credits {
z-index: 999;
pointer-events: none;
flex-flow: column;
justify-content: center;
align-items: center;
width: 100%;
height: 4em;
padding: 1em;
display: flex;
position: fixed;
bottom: 0;
left: 0;
font-size: 1vw;
}
.osmo-credits__p {
pointer-events: auto;
color: rgba(19, 19, 19, 0.5);
text-align: center;
margin: 0;
font-family: PP Neue Montreal, Arial, sans-serif;
font-size: 1.125em;
font-weight: 500;
line-height: 1.3;
}
.osmo-credits__p-a {
color: #340824;
}
@font-face {
font-family: 'PP Neue Montreal';
src: url('https://cdn.prod.website-files.com/6819ed8312518f61b84824df/6819ed8312518f61b84825ba_PPNeueMontreal-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
// ------- Osmo [https://osmo.supply/] ------- //
gsap.registerPlugin(SplitText, CustomEase)
CustomEase.create("osmo-ease","0.625, 0.05, 0, 1");
function initMaskedTextReveal() {
const heading = document.querySelector('[data-split="heading"]');
const buttons = document.querySelectorAll('[data-split="button"]');
SplitText.create(heading, {
type: "lines, words, chars",
mask: "lines",
linesClass: "line",
wordsClass: "word",
charsClass: "letter"
});
let currentTween, currentTargets;
const config = {
lines: { duration: 0.8, stagger: 0.08 },
words: { duration: 0.6, stagger: 0.06 },
letters: { duration: 0.4, stagger: 0.008 }
};
function animate(type) {
if(currentTween){
currentTween.kill();
gsap.set(currentTargets, { yPercent: 0 });
}
const { duration, stagger } = config[type] || {};
const className = type === "lines"
? "line"
: type === "words"
? "word"
: "letter";
const targets = heading.querySelectorAll(`.${className}`);
currentTargets = targets;
currentTween = gsap.fromTo(
targets,
{ yPercent: 110 },
{ yPercent: 0, duration, stagger, ease: "osmo-ease" }
);
}
buttons.forEach(btn => {
btn.addEventListener("click", () => {
animate(btn.dataset.splitType);
});
});
}
// Initialize Masked Text Reveal
document.fonts.ready.then(function () {
initMaskedTextReveal();
})
This Pen doesn't use any external JavaScript resources.