JSFiddle - React, Tailwind, and code Playground
by ckissi
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<div class="text-center">
<h1 class="text-4xl text-white mb-8">Digital Flip Clock</h1>
<div class="flex space-x-4">
<div class="hours">
<div class="flex space-x-2">
<div class="flip-card w-24 h-32">
<div class="top rounded-t-lg"></div>
<div class="bottom rounded-b-lg"></div>
</div>
<div class="flip-card w-24 h-32">
<div class="top rounded-t-lg"></div>
<div class="bottom rounded-b-lg"></div>
</div>
</div>
<p class="text-gray-400 mt-2">Hours</p>
</div>
<div class="text-5xl text-white flex items-center">:</div>
<div class="minutes">
<div class="flex space-x-2">
<div class="flip-card w-24 h-32">
<div class="top rounded-t-lg"></div>
<div class="bottom rounded-b-lg"></div>
</div>
<div class="flip-card w-24 h-32">
<div class="top rounded-t-lg"></div>
<div class="bottom rounded-b-lg"></div>
</div>
</div>
<p class="text-gray-400 mt-2">Minutes</p>
</div>
<div class="text-5xl text-white flex items-center">:</div>
<div class="seconds">
<div class="flex space-x-2">
<div class="flip-card w-24 h-32">
<div class="top rounded-t-lg"></div>
<div class="bottom rounded-b-lg"></div>
</div>
<div class="flip-card w-24 h-32">
<div class="top rounded-t-lg"></div>
<div class="bottom rounded-b-lg"></div>
</div>
</div>
<p class="text-gray-400 mt-2">Seconds</p>
</div>
</div>
</div>
CSS
.flip-card {
perspective: 600px;
position: relative;
}
.top, .bottom, .flip-top, .flip-bottom {
height: 50%;
overflow: hidden;
position: absolute;
width: 100%;
}
.top, .flip-top {
background: #2d3748;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
border-bottom: 1px solid #1a202c;
transform-origin: bottom;
top: 0;
}
.bottom, .flip-bottom {
background: #2d3748;
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
border-top: 1px solid #1a202c;
transform-origin: top;
bottom: 0;
}
.top .content, .bottom .content, .flip-top .content, .flip-bottom .content {
height: 200%;
position: absolute;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.875rem;
font-weight: bold;
color: white;
}
.top .content, .flip-top .content {
top: 0;
}
.bottom .content, .flip-bottom .content {
bottom: 0;
}
.flip-top, .flip-bottom {
position: absolute;
z-index: 2;
}
@keyframes flipTop {
0% { transform: rotateX(0deg); }
100% { transform: rotateX(-90deg); }
}
@keyframes flipBottom {
0% { transform: rotateX(90deg); }
100% { transform: rotateX(0deg); }
}
.animate-flipTop {
animation: flipTop 0.3s ease-in;
animation-fill-mode: forwards;
}
.animate-flipBottom {
animation: flipBottom 0.3s ease-out 0.3s;
animation-fill-mode: forwards;
}
JavaScript
function updateTime() {
const now = moment();
updateDigits('hours', now.format('HH'));
updateDigits('minutes', now.format('mm'));
updateDigits('seconds', now.format('ss'));
}
function updateDigits(unit, value) {
const cards = document.querySelector(`.${unit}`).querySelectorAll('.flip-card');
const digits = value.toString().padStart(2, '0').split('');
digits.forEach((digit, index) => {
const card = cards[index];
const currentValue = card.querySelector('.top').textContent;
if (currentValue !== digit) {
// Update top card
const top = card.querySelector('.top');
const bottom = card.querySelector('.bottom');
// Remove existing animation classes
top.classList.remove('flip-top');
bottom.classList.remove('flip-bottom');
// Set initial state
top.textContent = currentValue || '0';
bottom.textContent = digit;
// Force reflow
void top.offsetWidth;
// Add animation classes
top.classList.add('flip-top');
bottom.classList.add('flip-bottom');
// Update the values after animation
setTimeout(() => {
top.textContent = digit;
bottom.textContent = digit;
}, 300);
}
});
}
// Initialize the cards with the current time
const cards = document.querySelectorAll('.flip-card');
cards.forEach(card => {
const top = card.querySelector('.top');
const bottom = card.querySelector('.bottom');
top.classList.add('flex', 'items-center', 'justify-center', 'text-4xl', 'text-white', 'font-bold');
bottom.classList.add('flex', 'items-center', 'justify-center', 'text-4xl', 'text-white', 'font-bold');
top.textContent = '0';
bottom.textContent = '0';
});
// Update immediately and then...