Quick and Easy Countdown Timer with HTML, CSS, and JavaScript

Faraz

By Faraz -

Learn how to create a countdown timer using HTML, CSS, and JavaScript. This easy tutorial covers step-by-step instructions to build a stylish timer for your website.


quick-and-easy-countdown-timer-with-html-css-and-javascript.webp

Table of Contents

  1. Project Introduction
  2. HTML Code
  3. CSS Code
  4. JavaScript Code
  5. Conclusion
  6. Preview

A countdown timer is a simple yet effective way to keep visitors engaged on your website. It can help create excitement by showing how much time is left for an event, product launch, or special offer. Adding a visual timer can improve user experience and encourage them to take action before time runs out.

In this tutorial, we'll walk you through how to create a countdown timer using HTML, CSS, and JavaScript. This guide is perfect for beginners who know basic web development but are looking to add dynamic features to their websites. By following this tutorial, you'll learn how to build the timer's structure, style it to look neat, and add real-time functionality that updates automatically.

Prerequisites
Before we start, make sure you have:

Let’s get started and create a countdown timer that will make your website more interesting and interactive.

Source Code

Step 1 (HTML Code):

Create a new file named index.html. This will be the structure of your countdown timer. Here's an explanation of each section:

<!DOCTYPE html>

  • This declares the document as an HTML5 document.

<html lang="en">

  • Starts the HTML document and specifies the language as English.

<head>

  • Contains metadata and links for styles, fonts, and external scripts.
    • <meta charset="UTF-8">: Defines the character set as UTF-8.
    • <meta name="viewport" content="width=device-width, initial-scale=1.0">: Ensures the page is responsive on different devices.
    • <title>Countdown Timer</title>: Sets the title of the webpage as "Countdown Timer."
    • <link href="https://fonts.googleapis.com/...>: Links to Google Fonts for the page to use the "Orbitron" and "Poppins" fonts for a modern, digital look.
    • <script src="https://cdn.lordicon.com/lordicon.js"></script>: Links to an external script for using animated icons from Lordicon.
    • <link rel="stylesheet" href="styles.css">: Links to an external CSS file (not provided in this code) for styling the page.

<body>

  • The content of the page is contained within the body.

Container (<div class="container">)

  • Wraps the countdown timer and control buttons.

Countdown Timer (<div class="countdown-container">)

  • Contains the SVG circle that visually represents the countdown and the text displaying the remaining time.
    • <svg class="countdown-svg" viewBox="0 0 100 100">: This SVG element defines a circular timer graphic.
    • <defs> and <linearGradient>: Defines a gradient fill for the circle using green and yellow colors.
    • <circle class="countdown-circle" cx="50" cy="50" r="45"></circle>: A circle with a radius of 45 is drawn inside the SVG.
    • <div class="countdown-text" id="countdown">00:00</div>: This div displays the countdown time in the format 00:00.

Controls (<div class="controls">)

  • Contains the user input field and control buttons for the timer.
    • <input type="number" id="timeInput" placeholder="Enter time in seconds" min="1">: A numeric input field where users can enter the time in seconds.
    • Buttons: Three buttons control the countdown timer:
      • Start Button: <button id="startBtn"> triggers the countdown.
      • Pause Button: <button id="pauseBtn"> pauses the countdown.
      • Reset Button: <button id="resetBtn"> resets the timer to the initial state.

    Each button contains an animated Lordicon icon:

    • The start button uses an animated "play" icon from Lordicon.
    • The pause button uses an animated "pause" icon.
    • The reset button uses an animated "reset" icon.

<script src="script.js"></script>

  • Links to an external JavaScript file (script.js) where the logic for the countdown timer will be written. The script will handle actions like starting, pausing, and resetting the timer.

Step 2 (CSS Code):

Create a file named styles.css to add some style to your countdown timer. Here's a breakdown of each part:

Global Reset

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
  • This resets the default browser styles (margin and padding) for all elements (*) to ensure consistency across different browsers.
  • box-sizing: border-box; ensures that padding and border are included in the element's total width and height.

body

body {
  font-family: 'Poppins', sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background-color: #825CFF;
}
  • The font is set to Poppins, with sans-serif as a fallback.
  • The body is styled as a flex container, centering its content both vertically and horizontally.
  • min-height: 100vh; makes the body take up at least the full height of the viewport.
  • The background color is set to a purple shade #825CFF.

.container

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}
  • The container holds the countdown and controls.
  • It's a flex container with a column layout and centers its child elements horizontally.

.countdown-container

.countdown-container {
  position: relative;
  width: 200px;
  height: 200px;
  margin-bottom: 20px;
}
  • The countdown timer is 200px wide and tall, and has position: relative; so its inner elements (like the countdown text) can be positioned inside.
  • There’s a margin of 20px at the bottom to create space between the countdown and the control buttons.

.countdown-svg

.countdown-svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg); 
}
  • This SVG takes up 100% of the width and height of its container (200px by 200px).
  • It is rotated by -90deg to make the countdown start from the top.

.countdown-circle

.countdown-circle {
  fill: none;
  stroke: url(#warm-gradient); 
  stroke-width: 10;
  stroke-dasharray: 282.743;
  stroke-dashoffset: 282.743;
  stroke-linecap: round;
  transition: stroke-dashoffset 1s linear;
}
  • The circle's fill is set to none, and its stroke color is a gradient (#warm-gradient) defined in the HTML.
  • stroke-width: 10; makes the circle outline 10px thick.
  • stroke-dasharray and stroke-dashoffset are used to control the length and visibility of the stroke, allowing for the animation of the countdown.
  • The stroke-linecap: round; gives the stroke rounded edges.
  • The transition property ensures smooth animation when the stroke-dashoffset changes.

.countdown-text

.countdown-text {
  font-family: 'Orbitron', Helvetica, sans-serif;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 28px;
  font-weight: bold;
  color: #fff; 
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 
}
  • The countdown time text is styled with the 'Orbitron' font and positioned at the center of the countdown circle using absolute positioning and transform: translate(-50%, -50%);.
  • The text is large (28px), bold, white, and has a shadow to give it a slight 3D effect.

.controls

.controls {
  text-align: center;
  width: 100%;
}
  • The controls (input and buttons) are centered and take up the full width of the container.

Input Field (input[type="number"])

input[type="number"] {
  padding: 10px;
  font-size: 18px;
  font-family: 'Orbitron', Helvetica, sans-serif;
  width: 80%;
  border: 1px solid #fff;
  margin-bottom: 15px;
  outline: none;
  text-align: center;
  background-color: transparent;
  color: white;
}
  • The input field has padding for spacing, an 18px font size, and uses the 'Orbitron' font.
  • The field takes up 80% of the container width and has a white border.
  • The background is transparent, and the text is white.
  • The outline: none; removes the default focus outline.

Placeholder and Focus Styles

input[type="number"]:focus {
  border-color: #FFFB7D;
}

input[type="number"]::placeholder {
  color: rgb(190, 189, 189);
  opacity: 1;
}
  • When focused, the input field's border color changes to yellow #FFFB7D.
  • The placeholder text is a light gray color.

Removing Spin Buttons for Number Input

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
  • This removes the default up/down arrows (spin buttons) for the number input in WebKit browsers.

Buttons (.buttons and button)

.buttons, button {
  display: flex;
  justify-content: center;
  gap: 10px;
}

button {
  position: relative;
  font-family: 'Poppins', sans-serif;
  font-size: 0.875rem;
  background-color: transparent;
  border: 1px solid #fff;
  color: #fff;
  padding: 0.5rem 1rem;
  cursor: pointer;
  transition: color 0.3s;
}
  • Buttons are displayed in a row with justify-content: center and a gap of 10px between them.
  • The buttons are styled with the 'Poppins' font, white text, and transparent backgrounds.
  • The border is white, and padding is added for spacing.
  • A smooth transition is applied for color changes when hovering.

Hover and Active Effects

button:hover {
  color: black;
}

button::before {
  content: '';
  position: absolute;
  display: block;
  background-color: #fff;
  top: -1px;
  bottom: -1px;
  left: -1px;
  right: 100%;
  z-index: -10;
  transition: right 0.3s;
}

button:hover::before {
  right: -1px;
}

button:active {
  transform: translateY(2px);
}
  • When hovered, the button text color changes to black.
  • The ::before pseudo-element creates a white background animation that slides in from the left when hovering.
  • The button:active rule adds a slight downward movement (2px) to simulate a press effect when the button is clicked.

Icon Size

button lord-icon {
  width: 20px;
  height: 20px;
}
  • The size of the Lordicon icons inside the buttons is set to 20px by 20px.
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Poppins', sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background-color: #825CFF;
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.countdown-container {
  position: relative;
  width: 200px;
  height: 200px;
  margin-bottom: 20px;
}

.countdown-svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg); 
}

.countdown-circle {
  fill: none;
  stroke: url(#warm-gradient); 
  stroke-width: 10;
  stroke-dasharray: 282.743;
  stroke-dashoffset: 282.743;
  stroke-linecap: round;
  transition: stroke-dashoffset 1s linear;
}

.countdown-text {
  font-family: 'Orbitron', Helvetica, sans-serif;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 28px;
  font-weight: bold;
  color: #fff; 
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 
}

.controls {
  text-align: center;
  width: 100%;
}

input[type="number"] {
  padding: 10px;
  font-size: 18px;
  font-family: 'Orbitron', Helvetica, sans-serif;
  width: 80%;
  border: 1px solid #fff;
  margin-bottom: 15px;
  outline: none;
  text-align: center;
  background-color: transparent;
  color: white; 
}

input[type="number"]:focus {
  border-color: #FFFB7D;
}

input[type="number"]::placeholder {
  color: rgb(190, 189, 189);
  opacity: 1; 
}

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.buttons, button {
  display: flex;
  justify-content: center;
  gap: 10px;
}

button {
  position: relative;
  font-family: 'Poppins', sans-serif;
  font-size: 0.875rem;
  background-color: transparent; 
  border: 1px solid #fff;
  color: #fff;
  padding: 0.5rem 1rem;
  cursor: pointer;
  transition: color 0.3s;
}

button:hover {
  color: black;
}

button lord-icon{
  width:20px;
  height:20px;
}

button::before {
  content: '';
  position: absolute;
  display: block;
  background-color: #fff; 
  top: -1px;
  bottom: -1px;
  left: -1px; 
  right: 100%; 
  z-index: -10;
  transition: right 0.3s; 
}

button:hover::before {
  right: -1px;
}

button:active {
  transform: translateY(2px);
} 

Step 3 (JavaScript Code):

Create a file named script.js to handle the countdown logic. Here's a detailed breakdown:

Key Elements

  • Button Elements: The buttons for starting, pausing, and resetting the timer are selected from the DOM using their IDs (startBtn, pauseBtn, and resetBtn).
  • Input and Display Elements:
    • timeInput: The input field where the user enters the countdown time (in seconds).
    • countdownText: The element that displays the countdown in MM:SS format.
    • countdownCircle: The visual circular progress bar representing the remaining time.

Variables

  • timer: Holds the reference to the interval timer created with setInterval.
  • totalTime: The total countdown time (in seconds), entered by the user.
  • remainingTime: The time left in the countdown (in seconds).
  • isPaused: A flag that indicates if the timer is paused (true or false).

Button Hover Effects

This part of the code manages the visual effects when the user hovers over the buttons:

document.querySelectorAll('.buttons button').forEach(button => {
    const icon = button.querySelector('lord-icon');
    button.addEventListener('mouseover', () => {
        icon.setAttribute('colors', 'primary:#000000'); // Change icon color to black on hover
    });

    button.addEventListener('mouseout', () => {
        icon.setAttribute('colors', 'primary:#ffffff'); // Reset icon color to white
    });
});
  • Each button contains an icon (using lord-icon). On hover, the icon color changes to black, and on mouseout, it resets to white.

Timer Functions

1. formatTime(seconds)

This function converts the countdown time from seconds to a MM:SS format, ensuring that the minutes and seconds always have two digits:

function formatTime(seconds) {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
}

2. updateTimer()

This function is called every second to update the remaining time and the progress of the circular stroke on the countdown circle:

function updateTimer() {
    if (remainingTime <= 0) {
        clearInterval(timer); // Stop the timer when time is up
        countdownCircle.style.strokeDashoffset = '0'; // Complete the progress circle
        countdownText.textContent = '00:00'; // Show "00:00" when the countdown ends
        return;
    }

    remainingTime--;
    countdownText.textContent = formatTime(remainingTime); // Update the text display

    const dashOffset = (282.743 * (remainingTime / totalTime)).toFixed(3); // Calculate the stroke offset for the circle
    countdownCircle.style.strokeDashoffset = dashOffset;
}
  • Stroke Offset: The value 282.743 is the initial circumference of the circular SVG. The stroke offset is dynamically updated to visually represent the remaining time.

Event Listeners

  1. Start Button (startBtn):
    • New Timer: If the timer is not paused, it will reset the timer, get the input value, and start counting down.
    • Continue: If the timer is paused, it simply resumes the countdown.
    startBtn.addEventListener('click', () => {
        if (isPaused && timer) {
            isPaused = false;
            timer = setInterval(updateTimer, 1000); // Resume the paused timer
            return;
        }
    
        clearInterval(timer); // Clear any existing timer
        totalTime = parseInt(timeInput.value) || 0;
        remainingTime = totalTime;
    
        if (totalTime > 0) {
            countdownText.textContent = formatTime(totalTime);
            countdownCircle.style.strokeDashoffset = 282.743; // Reset the circular progress
            timer = setInterval(updateTimer, 1000); // Start a new timer
        }
    });
  2. Pause Button (pauseBtn):
    • Pauses the countdown if the timer is running.
    pauseBtn.addEventListener('click', () => {
        if (!isPaused) {
            clearInterval(timer); // Stop the timer
            isPaused = true;
        }
    });
  3. Reset Button (resetBtn):
    • Stops the timer and resets the display and progress bar.
    resetBtn.addEventListener('click', () => {
        clearInterval(timer); // Stop the timer
        countdownText.textContent = '00:00'; // Reset display to "00:00"
        countdownCircle.style.strokeDashoffset = 282.743; // Reset the progress bar
        isPaused = false;
    });
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resetBtn = document.getElementById('resetBtn');
const timeInput = document.getElementById('timeInput');
const countdownText = document.getElementById('countdown');
const countdownCircle = document.querySelector('.countdown-circle');

let timer;
let totalTime;
let remainingTime;
let isPaused = false;

document.querySelectorAll('.buttons button').forEach(button => {
    const icon = button.querySelector('lord-icon');
    button.addEventListener('mouseover', () => {
        icon.setAttribute('colors', 'primary:#000000');
    });

    button.addEventListener('mouseout', () => {
        icon.setAttribute('colors', 'primary:#ffffff'); 
    }); 
});

function formatTime(seconds) {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
}

function updateTimer() {
    if (remainingTime <= 0) {
        clearInterval(timer);
        countdownCircle.style.strokeDashoffset = '0'; 
        countdownText.textContent = '00:00';
        return;
    }

    remainingTime--;
    countdownText.textContent = formatTime(remainingTime);

    const dashOffset = (282.743 * (remainingTime / totalTime)).toFixed(3);
    countdownCircle.style.strokeDashoffset = dashOffset;
}

startBtn.addEventListener('click', () => {
    if (isPaused && timer) {
        isPaused = false;
        timer = setInterval(updateTimer, 1000);
        return;
    }

    clearInterval(timer);
    totalTime = parseInt(timeInput.value) || 0;
    remainingTime = totalTime;

    if (totalTime > 0) {
        countdownText.textContent = formatTime(totalTime);
        countdownCircle.style.strokeDashoffset = 282.743;
        timer = setInterval(updateTimer, 1000);
    }
});

pauseBtn.addEventListener('click', () => {
    if (!isPaused) {
        clearInterval(timer);
        isPaused = true;
    }
});

resetBtn.addEventListener('click', () => {
    clearInterval(timer);
    countdownText.textContent = '00:00';
    countdownCircle.style.strokeDashoffset = 282.743; 
    isPaused = false;
});

Final Output:

quick-and-easy-countdown-timer-with-html-css-and-javascript.gif

Conclusion:

Creating a countdown timer using HTML, CSS, and JavaScript is a straightforward way to add interactive features to your website. Throughout this tutorial, you’ve learned how to structure your HTML for the timer’s layout, style it with CSS to make it look appealing, and use JavaScript to implement real-time functionality.

Feel free to experiment with different styles, colors, and formats to fit your website’s design and purpose. You might also explore adding features like notifications or animations to make your timer even more engaging. The skills you've gained here can be applied to a variety of other web development projects, enhancing your ability to create interactive and user-friendly websites.

Keep practicing and experimenting with new ideas to continually improve your coding skills. With these basics in place, you’re well on your way to building more advanced and interactive web elements.

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Thanks!
Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox


Latest Post

Please allow ads on our site🥺