Skip to content
  • Unlock Pro
  • Log in with GitHub
Solution
Submitted 5 months ago

Animated Flip-Clock using NextJs and TailwindCSS

next, react, tailwind-css
Anil Kumar Meena•70
@J33rry
A solution to the Launch countdown timer challenge
View live sitePreview (opens in new tab)View codeCode (opens in new tab)

Solution retrospective


What are you most proud of, and what would you do differently next time?

This Flipper component

"use client";
import React, { useEffect, useState, useRef } from "react";

function getUnitValue(unit) {
    const now = new Date();
    if (unit === "day") return now.getDate();
    if (unit === "second") return now.getSeconds();
    if (unit === "minute") return now.getMinutes();
    if (unit === "hour") return now.getHours();
    return 0;
}

function Flipper({ unit }) {
    const [current, setCurrent] = useState(getUnitValue(unit));
    const [shuffle, setShuffle] = useState(false);
    const timeoutRef = useRef(null);

    useEffect(() => {
        const interval = setInterval(() => {
            const currentValue = getUnitValue(unit);
            setCurrent((current) => {
                if (currentValue !== current) {
                    setShuffle(true);
                    timeoutRef.current = setTimeout(() => {
                        setShuffle(false);
                    }, 900);
                }
                return currentValue;
            });
        }, 1000);

        return () => {
            clearInterval(interval);
            if (timeoutRef.current) {
                clearTimeout(timeoutRef.current);
            }
        };
    }, [unit]);

    const currentDisplay = current.toString().padStart(2, "0");
    return (
        <div className="relative size-20 sm:size-24 md:size-38 lg:size-44 perspective-near perspective-origin-[50%_50%] rounded-lg text-third bg-mostly_black shadow-[0_10px_4px_0_rgba(0,0,0,0.4)] text-4xl md:text-6xl lg:text-8xl">
            {/* static */}
            {/* upper */}
            <div className="flex relative justify-between w-full h-[50%] overflow-hidden bg-Desaturated_blue items-end rounded-t-lg -z-10">
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-r-full translate-y-1/2"></div>
                <span className="translate-y-1/2">{currentDisplay}</span>
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-l-full translate-y-1/2"></div>
            </div>
            {/* lower */}
            <div className="flex relative justify-between w-full h-[50%] overflow-hidden bg-light_blue items-start rounded-b-lg -z-10 text-secondary ">
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-r-full -translate-y-1/2"></div>
                <span className="-translate-y-1/2">{currentDisplay}</span>
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-l-full -translate-y-1/2"></div>
            </div>
            {/* divider */}
            <div className="absolute top-1/2 h-[1px] bg-mostly_black left-0 right-0 z-10"></div>
            {/* animated */}
            <div
                className={`absolute flex left-0 w-full h-1/2 top-0 justify-between overflow-hidden backface-hidden items-end origin-[50%_100%] rotate-x-0 bg-Desaturated_blue rounded-t-lg transform-3d ${
                    shuffle ? "animate-fold" : ""
                }`}
            >
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-r-full translate-y-1/2"></div>
                <span className="translate-y-1/2">{currentDisplay}</span>
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-l-full translate-y-1/2"></div>
            </div>
            <div
                className={`absolute flex left-0 w-full h-1/2 top-1/2 justify-between overflow-hidden backface-hidden items-start origin-[50%_0%] rotate-x-180 bg-light_blue rounded-b-lg transform-3d text-secondary ${
                    shuffle ? "animate-unfold" : ""
                }`}
            >
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-r-full -translate-y-1/2"></div>
                <span className="-translate-y-1/2">{currentDisplay}</span>
                <div className="bg-mostly_black h-4 w-2 md:h-6 md:w-3 rounded-l-full -translate-y-1/2"></div>
            </div>
        </div>
    );
}

export default Flipper;
What challenges did you encounter, and how did you overcome them?

Making the animation and controlling the timings

What specific areas of your project would you like help with?

The clock state changes first than the animation plays, Tried using a different var(storing the prev state) but didn't work and broken the design. Open to suggestions

Code
Loading...

Please log in to post a comment

Log in with GitHub

Community feedback

No feedback yet. Be the first to give feedback on Anil Kumar Meena's solution.

Join our Discord community

Join thousands of Frontend Mentor community members taking the challenges, sharing resources, helping each other, and chatting about all things front-end!

Join our Discord

Stay up to datewith new challenges, featured solutions, selected articles, and our latest news

Frontend Mentor

  • Unlock Pro
  • Contact us
  • FAQs
  • Become a partner
  • Use cases

Explore

  • Learning paths
  • Challenges
  • Solutions
  • Articles

Community

  • Discord
  • Guidelines

For companies

  • Hire developers
  • Train developers
© Frontend Mentor 2019 - 2025
  • Terms
  • Cookie Policy
  • Privacy Policy
  • License