<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>⏱️ 5 Minute Timer</h1>
<p class="subtitle">Focus timer for productivity</p>
<div class="timer-circle">
<svg class="circle-bg" viewBox="0 0 320 320">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#e74c3c;stop-opacity:1" />
<stop offset="100%" style="stop-color:#c0392b;stop-opacity:1" />
</linearGradient>
</defs>
<circle class="circle-bg-path" cx="160" cy="160" r="150"></circle>
<circle class="circle-progress-path" id="progressCircle" cx="160" cy="160" r="150"></circle>
</svg>
<div class="timer-display">
<div id="timeDisplay">05:00</div>
<div class="timer-label">MINUTES</div>
</div>
</div>
<div class="controls">
<button class="btn btn-success" 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-secondary" id="resetBtn">
<ion-icon name="refresh-outline"></ion-icon>
Reset
</button>
</div>
<div class="preset-buttons">
<button class="preset-btn" data-time="1">1 min</button>
<button class="preset-btn" data-time="3">3 min</button>
<button class="preset-btn" data-time="5">5 min</button>
<button class="preset-btn" data-time="10">10 min</button>
<button class="preset-btn" data-time="15">15 min</button>
<button class="preset-btn" data-time="25">25 min</button>
</div>
<div class="status-message" id="statusMessage">
🎉 Time's Up! Great work!
</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, #e74c3c 0%, #c0392b 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 30px;
box-shadow: 0 25px 70px rgba(0, 0, 0, 0.3);
padding: 60px 50px;
text-align: center;
max-width: 600px;
width: 100%;
}
h1 {
font-size: 32px;
color: #333;
margin-bottom: 15px;
font-weight: 700;
}
.subtitle {
color: #666;
font-size: 16px;
margin-bottom: 40px;
}
.timer-circle {
position: relative;
width: 320px;
height: 320px;
margin: 0 auto 40px;
}
.circle-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.circle-progress {
transform: rotate(-90deg);
transform-origin: center;
}
.circle-bg-path {
fill: none;
stroke: #f0f0f0;
stroke-width: 12;
}
.circle-progress-path {
fill: none;
stroke: url(#gradient);
stroke-width: 12;
stroke-linecap: round;
transition: stroke-dashoffset 1s linear;
}
.timer-display {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 72px;
font-weight: 700;
color: #333;
font-family: 'Courier New', monospace;
line-height: 1;
}
.timer-label {
font-size: 16px;
color: #999;
margin-top: 10px;
font-weight: 400;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;
}
.controls {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 30px;
}
.btn {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
color: white;
border: none;
border-radius: 50px;
padding: 16px 32px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 10px;
min-width: 140px;
justify-content: center;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 10px 25px rgba(231, 76, 60, 0.4);
}
.btn:active {
transform: translateY(-1px);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%);
}
.btn-secondary:hover {
box-shadow: 0 10px 25px rgba(149, 165, 166, 0.4);
}
.btn-success {
background: linear-gradient(135deg, #27ae60 0%, #229954 100%);
}
.btn-success:hover {
box-shadow: 0 10px 25px rgba(39, 174, 96, 0.4);
}
.status-message {
padding: 15px;
border-radius: 12px;
margin-top: 20px;
font-size: 16px;
font-weight: 600;
display: none;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.status-complete {
background: #d4edda;
color: #155724;
border: 2px solid #c3e6cb;
display: block;
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
.preset-buttons {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
margin-top: 30px;
}
.preset-btn {
background: #f8f9fa;
color: #333;
border: 2px solid #e0e0e0;
border-radius: 25px;
padding: 10px 20px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.preset-btn:hover {
background: #e74c3c;
color: white;
border-color: #e74c3c;
transform: translateY(-2px);
}
@media (max-width: 768px) {
.container {
padding: 40px 30px;
}
h1 {
font-size: 26px;
}
.timer-circle {
width: 260px;
height: 260px;
}
.timer-display {
font-size: 56px;
}
.controls {
flex-direction: column;
}
.btn {
width: 100%;
}
}
let countdown;
let totalSeconds = 300; // 5 minutes = 300 seconds
let remainingSeconds = 300;
let isPaused = false;
let isRunning = false;
const timeDisplay = document.getElementById('timeDisplay');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resetBtn = document.getElementById('resetBtn');
const statusMessage = document.getElementById('statusMessage');
const progressCircle = document.getElementById('progressCircle');
const presetButtons = document.querySelectorAll('.preset-btn');
const circumference = 2 * Math.PI * 150;
progressCircle.style.strokeDasharray = circumference;
progressCircle.style.strokeDashoffset = 0;
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 updateDisplay() {
timeDisplay.textContent = formatTime(remainingSeconds);
updateProgressCircle();
}
function updateProgressCircle() {
const progress = remainingSeconds / totalSeconds;
const offset = circumference * (1 - progress);
progressCircle.style.strokeDashoffset = offset;
}
function startTimer() {
if (remainingSeconds === 0) {
alert('Please reset the timer first');
return;
}
isRunning = true;
statusMessage.style.display = 'none';
startBtn.disabled = true;
pauseBtn.disabled = false;
countdown = setInterval(() => {
remainingSeconds--;
updateDisplay();
if (remainingSeconds === 0) {
clearInterval(countdown);
statusMessage.style.display = 'block';
startBtn.disabled = false;
pauseBtn.disabled = true;
isRunning = false;
playSound();
}
}, 1000);
}
function pauseTimer() {
clearInterval(countdown);
isPaused = true;
isRunning = false;
startBtn.disabled = false;
pauseBtn.disabled = true;
startBtn.innerHTML = '<ion-icon name="play-outline"></ion-icon> Resume';
}
function resetTimer() {
clearInterval(countdown);
remainingSeconds = totalSeconds;
isPaused = false;
isRunning = false;
updateDisplay();
startBtn.disabled = false;
pauseBtn.disabled = true;
statusMessage.style.display = 'none';
startBtn.innerHTML = '<ion-icon name="play-outline"></ion-icon> Start';
}
function setPresetTime(minutes) {
if (isRunning) {
if (!confirm('Timer is running. Do you want to change the time?')) {
return;
}
clearInterval(countdown);
}
totalSeconds = minutes * 60;
remainingSeconds = totalSeconds;
isRunning = false;
isPaused = false;
updateDisplay();
startBtn.disabled = false;
pauseBtn.disabled = true;
statusMessage.style.display = 'none';
startBtn.innerHTML = '<ion-icon name="play-outline"></ion-icon> Start';
}
function playSound() {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// Create three beeps
for (let i = 0; i < 3; i++) {
setTimeout(() => {
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.3);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.3);
}, i * 400);
}
}
startBtn.addEventListener('click', startTimer);
pauseBtn.addEventListener('click', pauseTimer);
resetBtn.addEventListener('click', resetTimer);
presetButtons.forEach(btn => {
btn.addEventListener('click', () => {
const minutes = parseInt(btn.getAttribute('data-time'));
setPresetTime(minutes);
});
});
// Initialize display
updateDisplay();
No comments yet. Be the first!