Split-flap board
CSS + JSA departure board: each character is four half-height leaves, and a change drops the old top half and lands the new bottom half. JS only swaps the letters and restarts the CSS animation.
Every letter does the same flip; the stagger turns it into a wave.
Your edited version
<span> with display: inline-block — transforms are ignored on plain inline boxes.animation-delay: calc(var(--i) * 0.12s) offsets each letter, and the offsets read as a travelling wave.aria-label and hide the spans from screen readers, or it is read letter by letter.inline-block lettersanimation-delay from --irotateY flip<div class="scene">
<h1 class="wave" aria-label="WAVE 3D">
<span style="--i:0" aria-hidden="true">W</span>
<span style="--i:1" aria-hidden="true">A</span>
<span style="--i:2" aria-hidden="true">V</span>
<span style="--i:3" aria-hidden="true">E</span>
<span style="--i:4" aria-hidden="true">·</span>
<span style="--i:5" aria-hidden="true">3</span>
<span style="--i:6" aria-hidden="true">D</span>
</h1>
</div>
.scene {
perspective: 600px;
}
.wave {
display: flex;
gap: 2px;
margin: 0;
font: 900 4rem system-ui;
transform-style: preserve-3d;
}
.wave span {
display: inline-block;
color: hsl(calc(255 + var(--i) * 18) 90% 70%);
animation: wave 2.8s ease-in-out infinite;
animation-delay: calc(var(--i) * 0.12s);
}
@keyframes wave {
0% { transform: translateZ(0) rotateY(0deg); }
22% { transform: translateZ(60px) rotateY(180deg); }
45%, 100% { transform: translateZ(0) rotateY(360deg); }
}
.d-waveletters {
display: flex;
gap: 2px;
font-size: 46px;
font-weight: 900;
transform-style: preserve-3d;
span {
display: inline-block; // transforms are ignored on plain inline boxes
color: hsl(calc(255 + var(--i) * 18) 90% 70%);
text-shadow: 0 6px 14px rgb(0 0 0 / 0.5);
animation: d-waveletters 2.8s ease-in-out infinite;
animation-delay: calc(var(--i) * 0.12s);
}
}
@keyframes d-waveletters {
0% {
transform: translateZ(0) rotateY(0deg);
}
22% {
transform: translateZ(60px) rotateY(180deg);
}
45%,
100% {
transform: translateZ(0) rotateY(360deg);
}
}