Skip to content

Instantly share code, notes, and snippets.

@stanwmusic
Created May 20, 2025 23:26
Show Gist options
  • Save stanwmusic/8223a3bc21e972c4536df9e808d9a3e9 to your computer and use it in GitHub Desktop.
Save stanwmusic/8223a3bc21e972c4536df9e808d9a3e9 to your computer and use it in GitHub Desktop.
Looping words with GSAP - Osmo
<li class="looping-words__list">
<p class="looping-words__p">Looping</p>
</li><!--------- Osmo [https://osmo.supply/] --------->
<section class="cloneable">
<div class="looping-words">
<div class="looping-words__containers">
<ul data-looping-words-list="" class="looping-words__list">
<li class="looping-words__list">
<a href="https://www.stanwilliams.org/blog/" target="_blank"><p class="looping-words__p">stanwilliams.org</p></a>
</li>
<li class="looping-words__list">
<p class="looping-words__p">Looping</p>
</li>
<li class="looping-words__list">
<p class="looping-words__p">Words</p>
</li>
<li class="looping-words__list">
<p class="looping-words__p">Selector</p>
</li>
<li class="looping-words__list">
<p class="looping-words__p">Made with</p>
<li class="looping-words__list">
<p class="looping-words__p">a Keyboard</p>
</li>
<li class="looping-words__list">
<p class="looping-words__p">Aint It</p>
</li>
</li>
</ul>
</div>
<div class="looping-words__fade"></div>
<div data-looping-words-selector="" class="looping-words__selector">
<div class="looping-words__edge"></div>
<div class="looping-words__edge is--2"></div>
<div class="looping-words__edge is--3"></div>
<div class="looping-words__edge is--4"></div>
</div>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
// ------- Osmo [https://osmo.supply/] ------- //
document.addEventListener('DOMContentLoaded', function() {
const wordList = document.querySelector('[data-looping-words-list]');
const words = Array.from(wordList.children);
const totalWords = words.length;
const wordHeight = 100 / totalWords; // Offset as a percentage
const edgeElement = document.querySelector('[data-looping-words-selector]');
let currentIndex = 0;
function updateEdgeWidth() {
const centerIndex = (currentIndex + 1) % totalWords;
const centerWord = words[centerIndex];
const centerWordWidth = centerWord.getBoundingClientRect().width;
const listWidth = wordList.getBoundingClientRect().width;
const percentageWidth = (centerWordWidth / listWidth) * 100;
gsap.to(edgeElement, {
width: `${percentageWidth}%`,
duration: 0.5,
ease: 'Expo.easeOut',
});
}
function moveWords() {
currentIndex++;
gsap.to(wordList, {
yPercent: -wordHeight * currentIndex,
duration: 1.2,
ease: 'elastic.out(1, 0.85)',
onStart: updateEdgeWidth,
onComplete: function() {
if (currentIndex >= totalWords - 3) {
wordList.appendChild(wordList.children[0]);
currentIndex--;
gsap.set(wordList, { yPercent: -wordHeight * currentIndex });
words.push(words.shift());
}
}
});
}
updateEdgeWidth();
gsap.timeline({ repeat: -1, delay: 1 })
.call(moveWords)
.to({}, { duration: 2 })
.repeat(-1);
});
/* ------- Osmo [https://osmo.supply/] ------- */
/* Osmo UI: https://slater.app/10324/23333.css */
body {
background-color: var(--color-neutral-300);
color: var(--color-dark);
font-size: var(--size-font);
}
.cloneable {
padding: var(--container-padding);
justify-content: center;
align-items: center;
min-height: 100vh;
display: flex;
position: relative;
}
.looping-words {
height: 2.7em;
padding-left: .1em;
padding-right: .1em;
font-size: 11vw;
line-height: .9;
position: relative;
}
.looping-words__list {
text-align: center;
text-transform: uppercase;
white-space: nowrap;
flex-flow: column;
align-items: center;
margin: 0;
padding: 0;
font-family: PP Neue Corp, sans-serif;
font-weight: 700;
list-style: none;
display: flex;
position: relative;
}
.looping-words__list.is--primary {
color: var(--color-primary);
}
.looping-words__list.is--gray {
color: var(--color-neutral-500);
}
.looping-words__fade {
background-image: linear-gradient(180deg, var(--color-neutral-300) 5%, transparent 40%, transparent 60%, var(--color-neutral-300) 95%);
pointer-events: none;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
.looping-words__fade.is--radial {
background-image: radial-gradient(circle closest-side at 50% 50%, transparent 64%, var(--color-neutral-400) 93%);
width: 140%;
display: block;
left: -20%;
}
.looping-words__selector {
pointer-events: none;
width: 100%;
height: .9em;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.looping-words__edge {
border-top: .035em solid var(--color-primary);
border-left: .035em solid var(--color-primary);
width: .125em;
height: .125em;
position: absolute;
top: 0;
left: 0;
}
.looping-words__edge.is--2 {
left: auto;
right: 0;
transform: rotate(90deg);
}
.looping-words__edge.is--3 {
inset: auto 0 0 auto;
transform: rotate(180deg);
}
.looping-words__edge.is--4 {
top: auto;
bottom: 0;
transform: rotate(270deg);
}
.looping-words__containers {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.looping-words__p {
margin: 0;
}
@font-face {
font-family: 'PP Neue Corp';
src: url('https://cdn.prod.website-files.com/6717aac16c9ea22eeef1e79e/6717de2d56e40b921572d2d9_PPNeueCorp-TightUltrabold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
<link href="https://slater.app/10324/23333.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment