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.
Table of Contents
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:
- A basic understanding of HTML, CSS, and JavaScript
- A text editor (like VS Code or Sublime Text)
- A web browser to test your code
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 format00: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.
- Start Button:
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
, withsans-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
andstroke-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 andtransform: 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
, andresetBtn
). - Input and Display Elements:
timeInput
: The input field where the user enters the countdown time (in seconds).countdownText
: The element that displays the countdown inMM: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
orfalse
).
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
- 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 } });
- Pause Button (
pauseBtn
):- Pauses the countdown if the timer is running.
pauseBtn.addEventListener('click', () => { if (!isPaused) { clearInterval(timer); // Stop the timer isPaused = true; } });
- 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:
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 😊