A race timing tower: the lap clock runs from 1:18 to 1:23.46 and stops, sector bars fill and turn purple, green and yellow, and a green -0.21 delta lands. Two registered integers print the time.
Also known as: lap timer css, timer animation css, racing timer text, sector times animation.
<div class="txt-race-lap-timer" aria-label="Lap 23, 1:23.46, personal best by 0.21 seconds"> <span class="txt-race-lap-timer-head" aria-hidden="true"><b>LAP 23</b><span class="txt-race-lap-timer-delta">-0.21</span></span> <span class="txt-race-lap-timer-time" aria-hidden="true"></span> <span class="txt-race-lap-timer-sectors" aria-hidden="true"><i></i><i></i><i></i></span> </div>
@property --txt-race-lap-timer-s {
syntax: "<integer>";
initial-value: 23;
inherits: false;
}
@property --txt-race-lap-timer-h {
syntax: "<integer>";
initial-value: 46;
inherits: false;
}
.txt-race-lap-timer {
--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;
gap: 0.3em;
padding: 0.55em 0.75em 0.65em;
border-radius: 0.5em;
color: #f5f5f5;
background: linear-gradient(#17191f, #0e0f13);
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.07), 0 0.5em 1.2em -0.6em rgb(0 0 0 / 0.5);
--t: 8.4s;
--purple: #b26bff;
--green: #2fd36b;
--yellow: #f2c94c;}
.txt-race-lap-timer-head {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.42em;
font-weight: 700;
letter-spacing: 0.1em;
}
.txt-race-lap-timer-head b { opacity: 0.6; font-weight: 700; }
.txt-race-lap-timer-delta {
padding: 0.15em 0.5em;
border-radius: 0.3em;
color: #0b2614;
background: var(--green);
letter-spacing: 0.02em;
font-variant-numeric: tabular-nums;
animation: txt-race-lap-timer-delta var(--t) infinite;
}
.txt-race-lap-timer-time {
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;
counter-reset: txt-race-lap-timer-s var(--txt-race-lap-timer-s) txt-race-lap-timer-h var(--txt-race-lap-timer-h);
animation:
txt-race-lap-timer-s var(--t) step-end infinite,
txt-race-lap-timer-h var(--t) linear infinite,
txt-race-lap-timer-done var(--t) infinite;
}
.txt-race-lap-timer-time::before {
content: "1:" counter(txt-race-lap-timer-s) "." counter(txt-race-lap-timer-h, decimal-leading-zero);
}
.txt-race-lap-timer-sectors {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.12em;
}
.txt-race-lap-timer-sectors i {
position: relative;
height: 0.2em;
border-radius: 0.1em;
background: rgb(255 255 255 / 0.1);
overflow: hidden;
}
.txt-race-lap-timer-sectors i::after {
content: "";
position: absolute;
inset: 0;
background: var(--purple);
transform-origin: left;
}
.txt-race-lap-timer-sectors i:nth-child(2)::after { animation: txt-race-lap-timer-s2 var(--t) infinite; }
.txt-race-lap-timer-sectors i:nth-child(3)::after { animation: txt-race-lap-timer-s3 var(--t) infinite; }
@keyframes txt-race-lap-timer-s {
0% { --txt-race-lap-timer-s: 18; }
11.9% { --txt-race-lap-timer-s: 19; }
23.81% { --txt-race-lap-timer-s: 20; }
35.71% { --txt-race-lap-timer-s: 21; }
47.62% { --txt-race-lap-timer-s: 22; }
59.52% { --txt-race-lap-timer-s: 23; }
100% { --txt-race-lap-timer-s: 23; }
}
@keyframes txt-race-lap-timer-h {
0% { --txt-race-lap-timer-h: 0; }
11.79% { --txt-race-lap-timer-h: 99; }
11.9% { --txt-race-lap-timer-h: 0; }
23.69% { --txt-race-lap-timer-h: 99; }
23.81% { --txt-race-lap-timer-h: 0; }
35.6% { --txt-race-lap-timer-h: 99; }
35.71% { --txt-race-lap-timer-h: 0; }
47.5% { --txt-race-lap-timer-h: 99; }
47.62% { --txt-race-lap-timer-h: 0; }
59.4% { --txt-race-lap-timer-h: 99; }
59.52% { --txt-race-lap-timer-h: 0; }
65%, 100% { --txt-race-lap-timer-h: 46; }
}
@keyframes txt-race-lap-timer-done {
0%, 65% { color: #f5f5f5; }
66.19% { color: var(--green); }
79.29%, 100% { color: #f5f5f5; }
}
@keyframes txt-race-lap-timer-s2 {
0% { transform: scaleX(0); background: rgb(255 255 255 / 0.55); }
32.14% { transform: scaleX(1); background: rgb(255 255 255 / 0.55); }
34.52%, 100% { transform: scaleX(1); background: var(--green); }
}
@keyframes txt-race-lap-timer-s3 {
0%, 32.14% { transform: scaleX(0); background: rgb(255 255 255 / 0.55); }
65% { transform: scaleX(1); background: rgb(255 255 255 / 0.55); }
67.38%, 100% { transform: scaleX(1); background: var(--yellow); }
}
@keyframes txt-race-lap-timer-delta {
0%, 65% { opacity: 0; transform: translateX(0.4em); }
69.17%, 96.43% { opacity: 1; transform: none; }
100% { opacity: 0; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
.txt-race-lap-timer, .txt-race-lap-timer *, .txt-race-lap-timer *::before, .txt-race-lap-timer *::after { animation: none !important; }
.txt-race-lap-timer-sectors i:nth-child(2)::after { background: var(--green); }
.txt-race-lap-timer-sectors i:nth-child(3)::after { background: var(--yellow); }
}
@media (prefers-reduced-motion: reduce) {
.txt-race-lap-timer, .txt-race-lap-timer *, .txt-race-lap-timer::before, .txt-race-lap-timer::after, .txt-race-lap-timer *::before, .txt-race-lap-timer *::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.