A mission clock counts T-00:00:10 down to zero as ten segments go dark one by one, then flips to T+ with all segments lit and Liftoff in your color. One inherited integer drives the clock and the bar.
Also known as: css countdown animation, countdown css animation, rocket launch countdown, 10 to 0 countdown.
<div class="txt-t-minus-launch-countdown" aria-label="Launch countdown from T minus 10 seconds"> <span class="txt-t-minus-launch-countdown-label" aria-hidden="true"><span>Launch window open</span><span>Liftoff</span></span> <span class="txt-t-minus-launch-countdown-clock" aria-hidden="true"><span class="txt-t-minus-launch-countdown-sign"><span>T-</span><span>T+</span></span><span class="txt-t-minus-launch-countdown-num"></span></span> <span class="txt-t-minus-launch-countdown-segs" aria-hidden="true"><i style="--i: 0"></i><i style="--i: 1"></i><i style="--i: 2"></i><i style="--i: 3"></i><i style="--i: 4"></i><i style="--i: 5"></i><i style="--i: 6"></i><i style="--i: 7"></i><i style="--i: 8"></i><i style="--i: 9"></i></span> </div>
@property --txt-t-minus-launch-countdown-n {
syntax: "<integer>";
initial-value: 0;
inherits: true;
}
@property --txt-t-minus-launch-countdown-up {
syntax: "<integer>";
initial-value: 1;
inherits: true;
}
.txt-t-minus-launch-countdown {
--c: var(--txt-color, #3b82f6);
font-size: calc(var(--txt-size, 32px) * 0.9);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
display: inline-grid;
justify-items: start;
gap: 0.3em;
--t: 13s;
animation:
txt-t-minus-launch-countdown-n var(--t) step-end infinite,
txt-t-minus-launch-countdown-up var(--t) step-end infinite;}
.txt-t-minus-launch-countdown-label {
display: grid;
font-size: 0.42em;
font-weight: 650;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.txt-t-minus-launch-countdown-label span {
grid-area: 1 / 1;
}
.txt-t-minus-launch-countdown-label span:last-child {
color: var(--c);
opacity: var(--txt-t-minus-launch-countdown-up);
}
.txt-t-minus-launch-countdown-label span:first-child {
opacity: calc(0.55 - var(--txt-t-minus-launch-countdown-up) * 0.55);
}
.txt-t-minus-launch-countdown-clock {
display: flex;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 1.45em;
font-weight: 600;
letter-spacing: -0.03em;
font-variant-numeric: tabular-nums;
}
.txt-t-minus-launch-countdown-sign {
display: grid;
}
.txt-t-minus-launch-countdown-sign span {
grid-area: 1 / 1;
opacity: calc(1 - var(--txt-t-minus-launch-countdown-up));
}
.txt-t-minus-launch-countdown-sign span:last-child {
color: var(--c);
opacity: var(--txt-t-minus-launch-countdown-up);
}
.txt-t-minus-launch-countdown-num {
counter-reset: txt-t-minus-launch-countdown-s var(--txt-t-minus-launch-countdown-n);
}
.txt-t-minus-launch-countdown-num::before {
content: "00:00:" counter(txt-t-minus-launch-countdown-s, decimal-leading-zero);
}
.txt-t-minus-launch-countdown-segs {
display: flex;
gap: 0.12em;
}
.txt-t-minus-launch-countdown-segs i {
width: 0.48em;
height: 0.2em;
border-radius: 0.1em;
background: var(--c);
/* lit while the count is above this segment, or after liftoff */
opacity: clamp(0.14, calc(var(--txt-t-minus-launch-countdown-n) - var(--i) + var(--txt-t-minus-launch-countdown-up) * 10), 1);
box-shadow: 0 0 calc(var(--txt-t-minus-launch-countdown-up) * 0.4em) var(--c);
transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s;
}
@keyframes txt-t-minus-launch-countdown-n {
0% { --txt-t-minus-launch-countdown-n: 10; }
7.69% { --txt-t-minus-launch-countdown-n: 9; }
15.38% { --txt-t-minus-launch-countdown-n: 8; }
23.08% { --txt-t-minus-launch-countdown-n: 7; }
30.77% { --txt-t-minus-launch-countdown-n: 6; }
38.46% { --txt-t-minus-launch-countdown-n: 5; }
46.15% { --txt-t-minus-launch-countdown-n: 4; }
53.85% { --txt-t-minus-launch-countdown-n: 3; }
61.54% { --txt-t-minus-launch-countdown-n: 2; }
69.23% { --txt-t-minus-launch-countdown-n: 1; }
76.92% { --txt-t-minus-launch-countdown-n: 0; }
84.62% { --txt-t-minus-launch-countdown-n: 1; }
92.31% { --txt-t-minus-launch-countdown-n: 2; }
}
@keyframes txt-t-minus-launch-countdown-up {
0% { --txt-t-minus-launch-countdown-up: 0; }
76.92% { --txt-t-minus-launch-countdown-up: 1; }
}
@media (prefers-reduced-motion: reduce) {
.txt-t-minus-launch-countdown, .txt-t-minus-launch-countdown *, .txt-t-minus-launch-countdown *::before, .txt-t-minus-launch-countdown *::after { animation: none !important; }
}
@media (prefers-reduced-motion: reduce) {
.txt-t-minus-launch-countdown, .txt-t-minus-launch-countdown *, .txt-t-minus-launch-countdown::before, .txt-t-minus-launch-countdown::after, .txt-t-minus-launch-countdown *::before, .txt-t-minus-launch-countdown *::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.