A bus stop flip-dot board turns from CITY to 2 MIN and back, each disc flipping edge-on and landing on its other face in a wave across the columns. A 5 by 7 bitmap built into the markup.
Also known as: flip dot display css, bus destination sign animation, dot matrix text animation, pixel text animation.
<div class="txt-flip-dot-sign" aria-label="City, due in 2 minutes"> <span class="txt-flip-dot-sign-board" aria-hidden="true"><i></i><i class="txt-flip-dot-sign-up" style="--x: 1"></i><i class="txt-flip-dot-sign-up" style="--x: 2"></i><i class="txt-flip-dot-sign-up" style="--x: 3"></i><i class="txt-flip-dot-sign-dn" style="--x: 4"></i><i class="txt-flip-dot-sign-dn" style="--x: 5"></i><i class="txt-flip-dot-sign-dn" style="--x: 6"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 10"></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-on"></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 15"></i><i class="txt-flip-dot-sign-on"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i class="txt-flip-dot-sign-dn" style="--x: 18"></i><i class="txt-flip-dot-sign-on"></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i class="txt-flip-dot-sign-on"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 24"></i><i class="txt-flip-dot-sign-dn" style="--x: 25"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i><i class="txt-flip-dot-sign-up" style="--x: 0"></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 3"></i><i class="txt-flip-dot-sign-up" style="--x: 4"></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 7"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-up" style="--x: 12"></i><i class="txt-flip-dot-sign-up" style="--x: 13"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 15"></i><i class="txt-flip-dot-sign-up" style="--x: 16"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i class="txt-flip-dot-sign-dn" style="--x: 21"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 24"></i><i class="txt-flip-dot-sign-on"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 3"></i><i class="txt-flip-dot-sign-up" style="--x: 4"></i><i></i><i></i><i></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-up" style="--x: 12"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 14"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 16"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 22"></i><i></i><i class="txt-flip-dot-sign-on"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 26"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-on"></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-up" style="--x: 12"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 14"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 16"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 23"></i><i class="txt-flip-dot-sign-up" style="--x: 24"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 27"></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 2"></i><i class="txt-flip-dot-sign-dn" style="--x: 3"></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-up" style="--x: 12"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 16"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 23"></i><i class="txt-flip-dot-sign-up" style="--x: 24"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 1"></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 3"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 7"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-up" style="--x: 12"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 16"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 23"></i><i class="txt-flip-dot-sign-up" style="--x: 24"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i><i class="txt-flip-dot-sign-up" style="--x: 0"></i><i class="txt-flip-dot-sign-up" style="--x: 1"></i><i class="txt-flip-dot-sign-up" style="--x: 2"></i><i class="txt-flip-dot-sign-up" style="--x: 3"></i><i class="txt-flip-dot-sign-on"></i><i class="txt-flip-dot-sign-dn" style="--x: 5"></i><i class="txt-flip-dot-sign-dn" style="--x: 6"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 10"></i><i class="txt-flip-dot-sign-dn" style="--x: 11"></i><i class="txt-flip-dot-sign-on"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 16"></i><i class="txt-flip-dot-sign-dn" style="--x: 17"></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 19"></i><i class="txt-flip-dot-sign-up" style="--x: 20"></i><i class="txt-flip-dot-sign-up" style="--x: 21"></i><i></i><i class="txt-flip-dot-sign-dn" style="--x: 23"></i><i class="txt-flip-dot-sign-up" style="--x: 24"></i><i></i><i></i><i></i><i class="txt-flip-dot-sign-up" style="--x: 28"></i></span> </div>
.txt-flip-dot-sign {
--c: var(--txt-color, #3b82f6);
font-size: calc(var(--txt-size, 32px) * 1);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
display: inline-block;
padding: 0.3em;
border-radius: 0.35em;
background: linear-gradient(#2a2a2a, #171717);
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.07), 0 0.4em 1em -0.5em rgb(0 0 0 / 0.5);
--t: 7s;
--on: color-mix(in srgb, var(--c) 15%, #f4e04a);}
.txt-flip-dot-sign-board {
display: grid;
grid-template-columns: repeat(29, 0.24em);
grid-auto-rows: 0.24em;
gap: 0.05em;
padding: 0.2em 0.25em;
border-radius: 0.15em;
background: #0a0a0a;
}
.txt-flip-dot-sign-board i {
border-radius: 50%;
background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515);
}
.txt-flip-dot-sign-board .txt-flip-dot-sign-on {
background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%);
}
.txt-flip-dot-sign-up, .txt-flip-dot-sign-dn {
animation: txt-flip-dot-sign-up var(--t) infinite;
animation-delay: calc(var(--x) * 0.025s);
}
.txt-flip-dot-sign-dn { animation-name: txt-flip-dot-sign-dn; }
@keyframes txt-flip-dot-sign-up {
0%, 42% { transform: none; background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); animation-timing-function: ease-in; }
44% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); }
44.01% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); animation-timing-function: cubic-bezier(0.34, 1.4, 0.64, 1); }
47%, 92% { transform: none; background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); animation-timing-function: ease-in; }
94% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); }
94.01% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); animation-timing-function: cubic-bezier(0.34, 1.4, 0.64, 1); }
97%, 100% { transform: none; background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); }
}
@keyframes txt-flip-dot-sign-dn {
0%, 42% { transform: none; background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); animation-timing-function: ease-in; }
44% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); }
44.01% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); animation-timing-function: cubic-bezier(0.34, 1.4, 0.64, 1); }
47%, 92% { transform: none; background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); animation-timing-function: ease-in; }
94% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, #2c2c2c, #151515); }
94.01% { transform: scaleX(0.05); background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); animation-timing-function: cubic-bezier(0.34, 1.4, 0.64, 1); }
97%, 100% { transform: none; background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); }
}
@media (prefers-reduced-motion: reduce) {
.txt-flip-dot-sign, .txt-flip-dot-sign *, .txt-flip-dot-sign *::before, .txt-flip-dot-sign *::after { animation: none !important; }
.txt-flip-dot-sign-board .txt-flip-dot-sign-dn { background: radial-gradient(circle at 35% 35%, color-mix(in srgb, var(--on) 60%, #fff), var(--on) 70%); }
}
@media (prefers-reduced-motion: reduce) {
.txt-flip-dot-sign, .txt-flip-dot-sign *, .txt-flip-dot-sign::before, .txt-flip-dot-sign::after, .txt-flip-dot-sign *::before, .txt-flip-dot-sign *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--txt-color on the element or a parent.
Currently previewing #3b82f6.--txt-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 40px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.