A focus timer counts down from 24:59 in mm:ss, every digit rolling down into place as the second turns, with a Focus chip and session dots. Four digit strips on 10s, 60s and 600s loops.
Also known as: countdown timer css, mm:ss timer animation, pomodoro timer css, css countdown animation.
<div class="txt-pomodoro-focus-timer" aria-label="Focus timer, 25 minute session">
<span class="txt-pomodoro-focus-timer-head" aria-hidden="true">
<span class="txt-pomodoro-focus-timer-chip">Focus</span>
<span class="txt-pomodoro-focus-timer-sets"><i></i><i></i><i></i><i></i></span>
</span>
<span class="txt-pomodoro-focus-timer-time" aria-hidden="true"><span class="txt-pomodoro-focus-timer-col"><span class="txt-pomodoro-focus-timer-m10">21</span></span><span class="txt-pomodoro-focus-timer-col"><span class="txt-pomodoro-focus-timer-m1">43210987654</span></span><span class="txt-pomodoro-focus-timer-colon">:</span><span class="txt-pomodoro-focus-timer-col"><span class="txt-pomodoro-focus-timer-s10">5432105</span></span><span class="txt-pomodoro-focus-timer-col"><span class="txt-pomodoro-focus-timer-s1">98765432109</span></span></span>
</div>.txt-pomodoro-focus-timer {
--c: var(--txt-color, #3b82f6);
font-size: calc(var(--txt-size, 32px) * 0.95);
font-family: system-ui, sans-serif;
font-weight: 700;
line-height: 1.2;
display: inline-grid;
justify-items: center;
gap: 0.25em;}
.txt-pomodoro-focus-timer-head {
display: flex;
align-items: center;
gap: 0.6em;
font-size: 0.42em;
}
.txt-pomodoro-focus-timer-chip {
display: inline-flex;
align-items: center;
gap: 0.45em;
padding: 0.3em 0.75em 0.3em 0.6em;
border-radius: 99em;
font-weight: 650;
color: var(--c);
background: color-mix(in srgb, var(--c) 12%, transparent);
}
.txt-pomodoro-focus-timer-chip::before {
content: "";
width: 0.55em;
height: 0.55em;
border-radius: 50%;
background: currentColor;
animation: txt-pomodoro-focus-timer-breathe 2s ease-in-out infinite;
}
.txt-pomodoro-focus-timer-sets {
display: flex;
gap: 0.35em;
}
.txt-pomodoro-focus-timer-sets i {
width: 0.5em;
height: 0.5em;
border-radius: 50%;
border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.txt-pomodoro-focus-timer-sets i:first-child {
border-color: transparent;
background: var(--c);
}
.txt-pomodoro-focus-timer-time {
display: inline-flex;
font-size: 2em;
font-weight: 600;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
}
.txt-pomodoro-focus-timer-col {
display: inline-block;
height: 1.15em;
overflow: hidden;
line-height: 1.15em;
-webkit-mask-image: linear-gradient(transparent, #000 18%, #000 82%, transparent);
mask-image: linear-gradient(transparent, #000 18%, #000 82%, transparent);
}
.txt-pomodoro-focus-timer-col > span {
display: block;
width: 1ch;
text-align: center;
word-break: break-all;
}
.txt-pomodoro-focus-timer-colon {
line-height: 1.1em;
padding: 0 0.04em;
opacity: 0.35;
}
.txt-pomodoro-focus-timer-s1 { animation: txt-pomodoro-focus-timer-s1 10s infinite; }
.txt-pomodoro-focus-timer-s10 { animation: txt-pomodoro-focus-timer-s10 60s infinite; }
.txt-pomodoro-focus-timer-m1 { animation: txt-pomodoro-focus-timer-m1 600s infinite; }
.txt-pomodoro-focus-timer-m10 { animation: txt-pomodoro-focus-timer-m10 600s infinite; }
@keyframes txt-pomodoro-focus-timer-breathe {
50% { opacity: 0.35; }
}
@keyframes txt-pomodoro-focus-timer-s1 {
0% { transform: translateY(0); }
6.5% { transform: translateY(0); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
10% { transform: translateY(-1.15em); }
16.5% { transform: translateY(-1.15em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
20% { transform: translateY(-2.3em); }
26.5% { transform: translateY(-2.3em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
30% { transform: translateY(-3.45em); }
36.5% { transform: translateY(-3.45em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
40% { transform: translateY(-4.6em); }
46.5% { transform: translateY(-4.6em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
50% { transform: translateY(-5.75em); }
56.5% { transform: translateY(-5.75em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
60% { transform: translateY(-6.9em); }
66.5% { transform: translateY(-6.9em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
70% { transform: translateY(-8.05em); }
76.5% { transform: translateY(-8.05em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
80% { transform: translateY(-9.2em); }
86.5% { transform: translateY(-9.2em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
90% { transform: translateY(-10.35em); }
96.5% { transform: translateY(-10.35em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
100% { transform: translateY(-11.5em); }
100% { transform: translateY(-11.5em); }
}
@keyframes txt-pomodoro-focus-timer-s10 {
0% { transform: translateY(0); }
16.08% { transform: translateY(0); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
16.67% { transform: translateY(-1.15em); }
32.75% { transform: translateY(-1.15em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
33.33% { transform: translateY(-2.3em); }
49.42% { transform: translateY(-2.3em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
50% { transform: translateY(-3.45em); }
66.08% { transform: translateY(-3.45em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
66.67% { transform: translateY(-4.6em); }
82.75% { transform: translateY(-4.6em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
83.33% { transform: translateY(-5.75em); }
99.42% { transform: translateY(-5.75em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
100% { transform: translateY(-6.9em); }
100% { transform: translateY(-6.9em); }
}
@keyframes txt-pomodoro-focus-timer-m1 {
0% { transform: translateY(0); }
9.94% { transform: translateY(0); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
10% { transform: translateY(-1.15em); }
19.94% { transform: translateY(-1.15em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
20% { transform: translateY(-2.3em); }
29.94% { transform: translateY(-2.3em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
30% { transform: translateY(-3.45em); }
39.94% { transform: translateY(-3.45em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
40% { transform: translateY(-4.6em); }
49.94% { transform: translateY(-4.6em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
50% { transform: translateY(-5.75em); }
59.94% { transform: translateY(-5.75em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
60% { transform: translateY(-6.9em); }
69.94% { transform: translateY(-6.9em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
70% { transform: translateY(-8.05em); }
79.94% { transform: translateY(-8.05em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
80% { transform: translateY(-9.2em); }
89.94% { transform: translateY(-9.2em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
90% { transform: translateY(-10.35em); }
99.94% { transform: translateY(-10.35em); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
100% { transform: translateY(-11.5em); }
100% { transform: translateY(-11.5em); }
}
@keyframes txt-pomodoro-focus-timer-m10 {
0% { transform: translateY(0); }
49.94% { transform: translateY(0); animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); }
50% { transform: translateY(-1.15em); }
100% { transform: translateY(-1.15em); }
}
@media (prefers-reduced-motion: reduce) {
.txt-pomodoro-focus-timer, .txt-pomodoro-focus-timer *, .txt-pomodoro-focus-timer *::before, .txt-pomodoro-focus-timer *::after { animation: none !important; }
}
@media (prefers-reduced-motion: reduce) {
.txt-pomodoro-focus-timer, .txt-pomodoro-focus-timer *, .txt-pomodoro-focus-timer::before, .txt-pomodoro-focus-timer::after, .txt-pomodoro-focus-timer *::before, .txt-pomodoro-focus-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.