<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
<div class="container">
<h1>Countdown Timer</h1>
<p class="subtitle">Set your time and start counting down</p>
<div class="timer-display">
<div class="time-unit">
<div class="time-value" id="hours">00</div>
<div class="time-label">Hours</div>
</div>
<div class="time-unit">
<div class="time-value" id="minutes">00</div>
<div class="time-label">Minutes</div>
</div>
<div class="time-unit">
<div class="time-value" id="seconds">00</div>
<div class="time-label">Seconds</div>
</div>
</div>
<div class="input-section">
<div class="input-group">
<div class="input-wrapper">
<label>Hours</label>
<input type="number" id="inputHours" min="0" max="23" value="0">
</div>
<div class="input-wrapper">
<label>Minutes</label>
<input type="number" id="inputMinutes" min="0" max="59" value="5">
</div>
<div class="input-wrapper">
<label>Seconds</label>
<input type="number" id="inputSeconds" min="0" max="59" value="0">
</div>
</div>
<div class="controls">
<button class="btn" id="startBtn">
<ion-icon name="play-outline"></ion-icon>
Start
</button>
<button class="btn btn-secondary" id="pauseBtn" disabled>
<ion-icon name="pause-outline"></ion-icon>
Pause
</button>
<button class="btn btn-danger" id="resetBtn">
<ion-icon name="refresh-outline"></ion-icon>
Reset
</button>
</div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="progressBar"></div>
</div>
<div class="status-message" id="statusMessage">
🎉 Time's Up!
</div>
</div>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
padding: 50px 40px;
width: 100%;
max-width: 500px;
text-align: center;
}
h1 {
font-size: 28px;
color: #333;
margin-bottom: 10px;
font-weight: 600;
}
.subtitle {
color: #666;
font-size: 14px;
margin-bottom: 40px;
}
.timer-display {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 40px;
}
.time-unit {
background: linear-gradient(135deg, #2c5364 0%, #203a43 100%);
border-radius: 16px;
padding: 20px;
min-width: 100px;
box-shadow: 0 8px 20px rgba(44, 83, 100, 0.3);
}
.time-value {
font-size: 48px;
font-weight: 700;
color: #fff;
line-height: 1;
font-family: 'Courier New', monospace;
}
.time-label {
font-size: 12px;
color: #a0d0e0;
text-transform: uppercase;
letter-spacing: 1px;
margin-top: 8px;
font-weight: 500;
}
.input-section {
background: #f7f9fa;
border-radius: 12px;
padding: 30px;
margin-bottom: 30px;
}
.input-group {
display: flex;
gap: 15px;
justify-content: center;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.input-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.input-wrapper label {
font-size: 12px;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
}
input[type="number"] {
width: 80px;
padding: 12px;
font-size: 18px;
border: 2px solid #e0e0e0;
border-radius: 10px;
text-align: center;
font-weight: 600;
color: #333;
transition: border-color 0.3s;
}
input[type="number"]:focus {
outline: none;
border-color: #2c5364;
}
.controls {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.btn {
background: linear-gradient(135deg, #2c5364 0%, #203a43 100%);
color: white;
border: none;
border-radius: 12px;
padding: 14px 28px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
display: flex;
align-items: center;
gap: 8px;
min-width: 120px;
justify-content: center;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(44, 83, 100, 0.4);
}
.btn:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%);
}
.btn-danger {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
}
.status-message {
margin-top: 20px;
padding: 15px;
border-radius: 10px;
font-size: 16px;
font-weight: 500;
display: none;
}
.status-complete {
background: #d4edda;
color: #155724;
display: block;
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
.progress-bar {
width: 100%;
height: 6px;
background: #e0e0e0;
border-radius: 3px;
margin-top: 30px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #2c5364 0%, #0f2027 100%);
width: 0%;
transition: width 0.3s ease;
}
let countdown;
let totalSeconds = 0;
let remainingSeconds = 0;
let isPaused = false;
const hoursDisplay = document.getElementById('hours');
const minutesDisplay = document.getElementById('minutes');
const secondsDisplay = document.getElementById('seconds');
const inputHours = document.getElementById('inputHours');
const inputMinutes = document.getElementById('inputMinutes');
const inputSeconds = document.getElementById('inputSeconds');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resetBtn = document.getElementById('resetBtn');
const statusMessage = document.getElementById('statusMessage');
const progressBar = document.getElementById('progressBar');
function formatTime(value) {
return value < 10 ? '0' + value : value;
}
function updateDisplay() {
const hours = Math.floor(remainingSeconds / 3600);
const minutes = Math.floor((remainingSeconds % 3600) / 60);
const seconds = remainingSeconds % 60;
hoursDisplay.textContent = formatTime(hours);
minutesDisplay.textContent = formatTime(minutes);
secondsDisplay.textContent = formatTime(seconds);
updateProgressBar();
}
function updateProgressBar() {
if (totalSeconds > 0) {
const progress = ((totalSeconds - remainingSeconds) / totalSeconds) * 100;
progressBar.style.width = progress + '%';
}
}
function startTimer() {
if (remainingSeconds === 0) {
const hours = parseInt(inputHours.value) || 0;
const minutes = parseInt(inputMinutes.value) || 0;
const seconds = parseInt(inputSeconds.value) || 0;
totalSeconds = hours * 3600 + minutes * 60 + seconds;
remainingSeconds = totalSeconds;
if (totalSeconds === 0) {
alert('Please set a time greater than 0');
return;
}
}
statusMessage.style.display = 'none';
isPaused = false;
startBtn.disabled = true;
pauseBtn.disabled = false;
inputHours.disabled = true;
inputMinutes.disabled = true;
inputSeconds.disabled = true;
countdown = setInterval(() => {
remainingSeconds--;
updateDisplay();
if (remainingSeconds === 0) {
clearInterval(countdown);
statusMessage.style.display = 'block';
startBtn.disabled = false;
pauseBtn.disabled = true;
inputHours.disabled = false;
inputMinutes.disabled = false;
inputSeconds.disabled = false;
playSound();
}
}, 1000);
updateDisplay();
}
function pauseTimer() {
clearInterval(countdown);
isPaused = true;
startBtn.disabled = false;
pauseBtn.disabled = true;
startBtn.innerHTML = '<ion-icon name="play-outline"></ion-icon> Resume';
}
function resetTimer() {
clearInterval(countdown);
remainingSeconds = 0;
totalSeconds = 0;
isPaused = false;
hoursDisplay.textContent = '00';
minutesDisplay.textContent = '00';
secondsDisplay.textContent = '00';
startBtn.disabled = false;
pauseBtn.disabled = true;
inputHours.disabled = false;
inputMinutes.disabled = false;
inputSeconds.disabled = false;
statusMessage.style.display = 'none';
progressBar.style.width = '0%';
startBtn.innerHTML = '<ion-icon name="play-outline"></ion-icon> Start';
}
function playSound() {
// Create a simple beep sound
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800;
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.5);
}
startBtn.addEventListener('click', startTimer);
pauseBtn.addEventListener('click', pauseTimer);
resetBtn.addEventListener('click', resetTimer);
// Prevent invalid input
[inputHours, inputMinutes, inputSeconds].forEach(input => {
input.addEventListener('input', function() {
if (this.value < 0) this.value = 0;
if (this === inputHours && this.value > 23) this.value = 23;
if (this !== inputHours && this.value > 59) this.value = 59;
});
});
No comments yet. Be the first!