6 CSS Countdown Timers 06 / 06
Retro Flip-Clock Timers
A classic airport split-flap board using 3D CSS transforms and perspective for mechanical flip motion.
Best forpremium product launches, gallery openings, festival countdowns, and anywhere the deadline itself deserves to be the visual centerpiece.
The code
<section class="cdt-flp" aria-label="Retro flip-clock countdown timer demo">
<div class="cdt-flp__marquee">Departure In</div>
<div class="cdt-flp__clock" role="timer" aria-live="off" aria-atomic="true">
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="d0"></div>
<div class="cdt-flp__flap" data-cdt-flp="d1"></div>
</div>
<span class="cdt-flp__glabel">Days</span>
</div>
<span class="cdt-flp__colon" aria-hidden="true">:</span>
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="h0"></div>
<div class="cdt-flp__flap" data-cdt-flp="h1"></div>
</div>
<span class="cdt-flp__glabel">Hours</span>
</div>
<span class="cdt-flp__colon" aria-hidden="true">:</span>
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="m0"></div>
<div class="cdt-flp__flap" data-cdt-flp="m1"></div>
</div>
<span class="cdt-flp__glabel">Minutes</span>
</div>
<span class="cdt-flp__colon" aria-hidden="true">:</span>
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="s0"></div>
<div class="cdt-flp__flap" data-cdt-flp="s1"></div>
</div>
<span class="cdt-flp__glabel">Seconds</span>
</div>
</div>
</section> <section class="cdt-flp" aria-label="Retro flip-clock countdown timer demo">
<div class="cdt-flp__marquee">Departure In</div>
<div class="cdt-flp__clock" role="timer" aria-live="off" aria-atomic="true">
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="d0"></div>
<div class="cdt-flp__flap" data-cdt-flp="d1"></div>
</div>
<span class="cdt-flp__glabel">Days</span>
</div>
<span class="cdt-flp__colon" aria-hidden="true">:</span>
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="h0"></div>
<div class="cdt-flp__flap" data-cdt-flp="h1"></div>
</div>
<span class="cdt-flp__glabel">Hours</span>
</div>
<span class="cdt-flp__colon" aria-hidden="true">:</span>
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="m0"></div>
<div class="cdt-flp__flap" data-cdt-flp="m1"></div>
</div>
<span class="cdt-flp__glabel">Minutes</span>
</div>
<span class="cdt-flp__colon" aria-hidden="true">:</span>
<div class="cdt-flp__group">
<div class="cdt-flp__digits">
<div class="cdt-flp__flap" data-cdt-flp="s0"></div>
<div class="cdt-flp__flap" data-cdt-flp="s1"></div>
</div>
<span class="cdt-flp__glabel">Seconds</span>
</div>
</div>
</section>/* ─── 06 Retro Flip Clock — 3D split-flap mechanism ────────── */
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Oswald:wght@300;400;500&display=swap');
.cdt-flp {
--cdt-flp-bg: #1a1410;
--cdt-flp-flap: #181818;
--cdt-flp-flap-line: #000;
--cdt-flp-digit: #f5f0e6;
--cdt-flp-accent: #e6b450;
--cdt-flp-label: #8c7a5c;
position: relative;
width: 100%;
min-height: 520px;
background:
radial-gradient(circle at 50% 20%, rgba(230,180,80,0.10), transparent 55%),
repeating-linear-gradient(0deg, rgba(0,0,0,0.15) 0 2px, transparent 2px 4px),
var(--cdt-flp-bg);
font-family: 'Oswald', sans-serif;
color: var(--cdt-flp-digit);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
padding: 40px 20px;
box-sizing: border-box;
overflow: hidden;
}
.cdt-flp *, .cdt-flp *::before, .cdt-flp *::after { box-sizing: border-box; margin: 0; padding: 0; }
.cdt-flp__marquee {
font-family: 'Bebas Neue', sans-serif;
font-size: clamp(20px, 3vw, 30px);
letter-spacing: 6px; color: var(--cdt-flp-accent);
margin-bottom: 30px;
text-shadow: 0 2px 12px rgba(230,180,80,0.4);
}
.cdt-flp__clock { display: flex; align-items: center; gap: 10px; }
.cdt-flp__group { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.cdt-flp__digits { display: flex; gap: 5px; }
.cdt-flp__glabel {
font-size: 11px; letter-spacing: 4px; text-transform: uppercase;
color: var(--cdt-flp-label); font-weight: 400;
}
.cdt-flp__flap {
position: relative;
width: 56px; height: 80px;
perspective: 320px;
font-family: 'Bebas Neue', sans-serif;
font-size: 60px; line-height: 80px; text-align: center;
}
.cdt-flp__flap .cdt-flp__card {
position: absolute; left: 0; right: 0;
height: 40px; overflow: hidden;
background: var(--cdt-flp-flap);
box-shadow: inset 0 0 14px rgba(0,0,0,0.6);
}
.cdt-flp__flap .cdt-flp__top {
top: 0; border-radius: 7px 7px 0 0;
border-bottom: 1px solid var(--cdt-flp-flap-line);
align-items: flex-end;
}
.cdt-flp__flap .cdt-flp__top span { display: block; }
.cdt-flp__flap .cdt-flp__bottom {
bottom: 0; border-radius: 0 0 7px 7px; line-height: 0;
}
.cdt-flp__flap .cdt-flp__bottom span { display: block; line-height: 80px; margin-top: -40px; }
.cdt-flp__flap .cdt-flp__flip-top,
.cdt-flp__flap .cdt-flp__flip-bottom {
position: absolute; left: 0; right: 0;
height: 40px; overflow: hidden;
background: var(--cdt-flp-flap);
backface-visibility: hidden;
}
.cdt-flp__flap .cdt-flp__flip-top {
top: 0; border-radius: 7px 7px 0 0;
border-bottom: 1px solid var(--cdt-flp-flap-line);
transform-origin: bottom;
box-shadow: inset 0 0 14px rgba(0,0,0,0.6);
}
.cdt-flp__flap .cdt-flp__flip-bottom {
bottom: 0; border-radius: 0 0 7px 7px; line-height: 0;
transform-origin: top; transform: rotateX(90deg);
box-shadow: inset 0 0 14px rgba(0,0,0,0.6);
}
.cdt-flp__flap .cdt-flp__flip-bottom span { display: block; line-height: 80px; margin-top: -40px; }
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-top { animation: cdt-flp-flip-top 0.3s ease-in forwards; }
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-bottom { animation: cdt-flp-flip-bottom 0.3s 0.3s ease-out forwards; }
@keyframes cdt-flp-flip-top { 0% { transform: rotateX(0); } 100% { transform: rotateX(-90deg); } }
@keyframes cdt-flp-flip-bottom { 0% { transform: rotateX(90deg); } 100% { transform: rotateX(0); } }
.cdt-flp__flap::after {
content: ''; position: absolute; left: 0; right: 0; top: 50%;
height: 2px; background: rgba(0,0,0,0.55);
transform: translateY(-1px); z-index: 5;
}
.cdt-flp__colon {
font-family: 'Bebas Neue', sans-serif;
font-size: 48px; color: var(--cdt-flp-accent);
align-self: flex-start; margin-top: 8px;
animation: cdt-flp-pulse 1s steps(1) infinite;
}
@keyframes cdt-flp-pulse { 50% { opacity: 0.25; } }
@media (max-width: 560px) {
.cdt-flp__flap { width: 38px; height: 56px; font-size: 42px; line-height: 56px; }
.cdt-flp__flap .cdt-flp__card,
.cdt-flp__flap .cdt-flp__flip-top,
.cdt-flp__flap .cdt-flp__flip-bottom { height: 28px; }
.cdt-flp__flap .cdt-flp__bottom span,
.cdt-flp__flap .cdt-flp__flip-bottom span { line-height: 56px; margin-top: -28px; }
.cdt-flp__colon { font-size: 34px; }
.cdt-flp__clock { gap: 6px; }
.cdt-flp__digits { gap: 3px; }
}
@media (prefers-reduced-motion: reduce) {
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-top,
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-bottom,
.cdt-flp__colon { animation: none; }
} /* ─── 06 Retro Flip Clock — 3D split-flap mechanism ────────── */
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Oswald:wght@300;400;500&display=swap');
.cdt-flp {
--cdt-flp-bg: #1a1410;
--cdt-flp-flap: #181818;
--cdt-flp-flap-line: #000;
--cdt-flp-digit: #f5f0e6;
--cdt-flp-accent: #e6b450;
--cdt-flp-label: #8c7a5c;
position: relative;
width: 100%;
min-height: 520px;
background:
radial-gradient(circle at 50% 20%, rgba(230,180,80,0.10), transparent 55%),
repeating-linear-gradient(0deg, rgba(0,0,0,0.15) 0 2px, transparent 2px 4px),
var(--cdt-flp-bg);
font-family: 'Oswald', sans-serif;
color: var(--cdt-flp-digit);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
padding: 40px 20px;
box-sizing: border-box;
overflow: hidden;
}
.cdt-flp *, .cdt-flp *::before, .cdt-flp *::after { box-sizing: border-box; margin: 0; padding: 0; }
.cdt-flp__marquee {
font-family: 'Bebas Neue', sans-serif;
font-size: clamp(20px, 3vw, 30px);
letter-spacing: 6px; color: var(--cdt-flp-accent);
margin-bottom: 30px;
text-shadow: 0 2px 12px rgba(230,180,80,0.4);
}
.cdt-flp__clock { display: flex; align-items: center; gap: 10px; }
.cdt-flp__group { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.cdt-flp__digits { display: flex; gap: 5px; }
.cdt-flp__glabel {
font-size: 11px; letter-spacing: 4px; text-transform: uppercase;
color: var(--cdt-flp-label); font-weight: 400;
}
.cdt-flp__flap {
position: relative;
width: 56px; height: 80px;
perspective: 320px;
font-family: 'Bebas Neue', sans-serif;
font-size: 60px; line-height: 80px; text-align: center;
}
.cdt-flp__flap .cdt-flp__card {
position: absolute; left: 0; right: 0;
height: 40px; overflow: hidden;
background: var(--cdt-flp-flap);
box-shadow: inset 0 0 14px rgba(0,0,0,0.6);
}
.cdt-flp__flap .cdt-flp__top {
top: 0; border-radius: 7px 7px 0 0;
border-bottom: 1px solid var(--cdt-flp-flap-line);
align-items: flex-end;
}
.cdt-flp__flap .cdt-flp__top span { display: block; }
.cdt-flp__flap .cdt-flp__bottom {
bottom: 0; border-radius: 0 0 7px 7px; line-height: 0;
}
.cdt-flp__flap .cdt-flp__bottom span { display: block; line-height: 80px; margin-top: -40px; }
.cdt-flp__flap .cdt-flp__flip-top,
.cdt-flp__flap .cdt-flp__flip-bottom {
position: absolute; left: 0; right: 0;
height: 40px; overflow: hidden;
background: var(--cdt-flp-flap);
backface-visibility: hidden;
}
.cdt-flp__flap .cdt-flp__flip-top {
top: 0; border-radius: 7px 7px 0 0;
border-bottom: 1px solid var(--cdt-flp-flap-line);
transform-origin: bottom;
box-shadow: inset 0 0 14px rgba(0,0,0,0.6);
}
.cdt-flp__flap .cdt-flp__flip-bottom {
bottom: 0; border-radius: 0 0 7px 7px; line-height: 0;
transform-origin: top; transform: rotateX(90deg);
box-shadow: inset 0 0 14px rgba(0,0,0,0.6);
}
.cdt-flp__flap .cdt-flp__flip-bottom span { display: block; line-height: 80px; margin-top: -40px; }
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-top { animation: cdt-flp-flip-top 0.3s ease-in forwards; }
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-bottom { animation: cdt-flp-flip-bottom 0.3s 0.3s ease-out forwards; }
@keyframes cdt-flp-flip-top { 0% { transform: rotateX(0); } 100% { transform: rotateX(-90deg); } }
@keyframes cdt-flp-flip-bottom { 0% { transform: rotateX(90deg); } 100% { transform: rotateX(0); } }
.cdt-flp__flap::after {
content: ''; position: absolute; left: 0; right: 0; top: 50%;
height: 2px; background: rgba(0,0,0,0.55);
transform: translateY(-1px); z-index: 5;
}
.cdt-flp__colon {
font-family: 'Bebas Neue', sans-serif;
font-size: 48px; color: var(--cdt-flp-accent);
align-self: flex-start; margin-top: 8px;
animation: cdt-flp-pulse 1s steps(1) infinite;
}
@keyframes cdt-flp-pulse { 50% { opacity: 0.25; } }
@media (max-width: 560px) {
.cdt-flp__flap { width: 38px; height: 56px; font-size: 42px; line-height: 56px; }
.cdt-flp__flap .cdt-flp__card,
.cdt-flp__flap .cdt-flp__flip-top,
.cdt-flp__flap .cdt-flp__flip-bottom { height: 28px; }
.cdt-flp__flap .cdt-flp__bottom span,
.cdt-flp__flap .cdt-flp__flip-bottom span { line-height: 56px; margin-top: -28px; }
.cdt-flp__colon { font-size: 34px; }
.cdt-flp__clock { gap: 6px; }
.cdt-flp__digits { gap: 3px; }
}
@media (prefers-reduced-motion: reduce) {
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-top,
.cdt-flp__flap.cdt-flp--go .cdt-flp__flip-bottom,
.cdt-flp__colon { animation: none; }
}(() => {
const root = document.querySelector('.cdt-flp');
if (!root) return;
function buildFlap(el, val) {
el.dataset.val = val;
el.innerHTML =
'<div class="cdt-flp__card cdt-flp__top"><span>' + val + '</span></div>' +
'<div class="cdt-flp__card cdt-flp__bottom"><span>' + val + '</span></div>' +
'<div class="cdt-flp__flip-top"><span>' + val + '</span></div>' +
'<div class="cdt-flp__flip-bottom"><span>' + val + '</span></div>';
}
function setFlap(el, next) {
const cur = el.dataset.val;
if (cur === next) return;
const top = el.querySelector('.cdt-flp__top span');
const bottom = el.querySelector('.cdt-flp__bottom span');
const flipTop = el.querySelector('.cdt-flp__flip-top span');
const flipBottom = el.querySelector('.cdt-flp__flip-bottom span');
flipTop.textContent = cur;
flipBottom.textContent = next;
bottom.textContent = next;
top.textContent = cur;
el.classList.remove('cdt-flp--go');
void el.offsetWidth;
el.classList.add('cdt-flp--go');
setTimeout(() => {
top.textContent = next;
el.dataset.val = next;
}, 600);
}
const flaps = {
d0: root.querySelector('[data-cdt-flp="d0"]'), d1: root.querySelector('[data-cdt-flp="d1"]'),
h0: root.querySelector('[data-cdt-flp="h0"]'), h1: root.querySelector('[data-cdt-flp="h1"]'),
m0: root.querySelector('[data-cdt-flp="m0"]'), m1: root.querySelector('[data-cdt-flp="m1"]'),
s0: root.querySelector('[data-cdt-flp="s0"]'), s1: root.querySelector('[data-cdt-flp="s1"]'),
};
Object.values(flaps).forEach((f) => buildFlap(f, '0'));
const target = Date.now() + (3*86400 + 14*3600 + 25*60 + 50) * 1000;
const pad = (n) => String(n).padStart(2, '0');
function render() {
const diff = Math.max(0, target - Date.now());
const d = pad(Math.floor(diff / 86400000));
const h = pad(Math.floor(diff % 86400000 / 3600000));
const m = pad(Math.floor(diff % 3600000 / 60000));
const s = pad(Math.floor(diff % 60000 / 1000));
setFlap(flaps.d0, d[0]); setFlap(flaps.d1, d[1]);
setFlap(flaps.h0, h[0]); setFlap(flaps.h1, h[1]);
setFlap(flaps.m0, m[0]); setFlap(flaps.m1, m[1]);
setFlap(flaps.s0, s[0]); setFlap(flaps.s1, s[1]);
}
render();
setInterval(render, 1000);
})(); (() => {
const root = document.querySelector('.cdt-flp');
if (!root) return;
function buildFlap(el, val) {
el.dataset.val = val;
el.innerHTML =
'<div class="cdt-flp__card cdt-flp__top"><span>' + val + '</span></div>' +
'<div class="cdt-flp__card cdt-flp__bottom"><span>' + val + '</span></div>' +
'<div class="cdt-flp__flip-top"><span>' + val + '</span></div>' +
'<div class="cdt-flp__flip-bottom"><span>' + val + '</span></div>';
}
function setFlap(el, next) {
const cur = el.dataset.val;
if (cur === next) return;
const top = el.querySelector('.cdt-flp__top span');
const bottom = el.querySelector('.cdt-flp__bottom span');
const flipTop = el.querySelector('.cdt-flp__flip-top span');
const flipBottom = el.querySelector('.cdt-flp__flip-bottom span');
flipTop.textContent = cur;
flipBottom.textContent = next;
bottom.textContent = next;
top.textContent = cur;
el.classList.remove('cdt-flp--go');
void el.offsetWidth;
el.classList.add('cdt-flp--go');
setTimeout(() => {
top.textContent = next;
el.dataset.val = next;
}, 600);
}
const flaps = {
d0: root.querySelector('[data-cdt-flp="d0"]'), d1: root.querySelector('[data-cdt-flp="d1"]'),
h0: root.querySelector('[data-cdt-flp="h0"]'), h1: root.querySelector('[data-cdt-flp="h1"]'),
m0: root.querySelector('[data-cdt-flp="m0"]'), m1: root.querySelector('[data-cdt-flp="m1"]'),
s0: root.querySelector('[data-cdt-flp="s0"]'), s1: root.querySelector('[data-cdt-flp="s1"]'),
};
Object.values(flaps).forEach((f) => buildFlap(f, '0'));
const target = Date.now() + (3*86400 + 14*3600 + 25*60 + 50) * 1000;
const pad = (n) => String(n).padStart(2, '0');
function render() {
const diff = Math.max(0, target - Date.now());
const d = pad(Math.floor(diff / 86400000));
const h = pad(Math.floor(diff % 86400000 / 3600000));
const m = pad(Math.floor(diff % 3600000 / 60000));
const s = pad(Math.floor(diff % 60000 / 1000));
setFlap(flaps.d0, d[0]); setFlap(flaps.d1, d[1]);
setFlap(flaps.h0, h[0]); setFlap(flaps.h1, h[1]);
setFlap(flaps.m0, m[0]); setFlap(flaps.m1, m[1]);
setFlap(flaps.s0, s[0]); setFlap(flaps.s1, s[1]);
}
render();
setInterval(render, 1000);
})();