|
| 1 | +# GDG Pomodoro Timer |
| 2 | + |
| 3 | +A beautiful, modern Pomodoro Timer web application built with vanilla HTML, CSS, and JavaScript. This project was created as part of the **GDG PUP JavaScript Workshop** to demonstrate fundamental web development concepts. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 📖 Table of Contents |
| 8 | + |
| 9 | +- [Overview](#overview) |
| 10 | +- [Features](#features) |
| 11 | +- [Project Structure](#project-structure) |
| 12 | +- [How It Works](#how-it-works) |
| 13 | +- [JavaScript Functions Reference](#javascript-functions-reference) |
| 14 | +- [Getting Started](#getting-started) |
| 15 | +- [Technologies Used](#technologies-used) |
| 16 | + |
| 17 | +--- |
| 18 | + |
| 19 | +## 🎯 Overview |
| 20 | + |
| 21 | +The **GDG Pomodoro Timer** is a productivity tool based on the Pomodoro Technique, a time management method developed by Francesco Cirillo. The technique uses a timer to break work into intervals, traditionally 25 minutes in length, separated by short breaks. |
| 22 | + |
| 23 | +This application allows users to: |
| 24 | +- Focus for **25 minutes** during work sessions |
| 25 | +- Take **5-minute short breaks** between sessions |
| 26 | +- Enjoy **15-minute long breaks** after completing multiple sessions |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## ✨ Features |
| 31 | + |
| 32 | +| Feature | Description | |
| 33 | +|---------|-------------| |
| 34 | +| 🎨 **Modern UI** | Glass-morphism design with Google's Material Design elements | |
| 35 | +| 🔄 **Three Timer Modes** | Focus (25 min), Short Break (5 min), Long Break (15 min) | |
| 36 | +| ⏯️ **Start/Pause Toggle** | Single button to control timer state | |
| 37 | +| 🔁 **Reset Functionality** | Quickly reset the current timer | |
| 38 | +| 📊 **Visual Progress Ring** | SVG-based circular progress indicator | |
| 39 | +| 🎨 **Dynamic Theming** | Color changes based on the selected mode | |
| 40 | +| 📱 **Responsive Design** | Works on desktop and mobile devices | |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## 📁 Project Structure |
| 45 | + |
| 46 | +``` |
| 47 | +Live Project/ |
| 48 | +├── pomodoro.html # Main HTML file (entry point) |
| 49 | +├── README.md # This documentation file |
| 50 | +├── js/ |
| 51 | +│ └── main.js # JavaScript logic for timer functionality |
| 52 | +└── styles/ |
| 53 | + └── pomodoro.css # Stylesheet with modern glassmorphism design |
| 54 | +``` |
| 55 | + |
| 56 | +### File Descriptions |
| 57 | + |
| 58 | +| File | Purpose | |
| 59 | +|------|---------| |
| 60 | +| `pomodoro.html` | Contains the HTML structure including the timer display, mode buttons, control buttons, and SVG progress ring | |
| 61 | +| `js/main.js` | Contains all JavaScript logic including timer functions, state management, and event handling | |
| 62 | +| `styles/pomodoro.css` | Contains all CSS styling including the glassmorphism effect, animations, and responsive design | |
| 63 | + |
| 64 | +--- |
| 65 | + |
| 66 | +## ⚙️ How It Works |
| 67 | + |
| 68 | +### Timer Modes |
| 69 | + |
| 70 | +The application supports three distinct modes, each with a different duration and color theme: |
| 71 | + |
| 72 | +| Mode | Duration | Color | Purpose | |
| 73 | +|------|----------|-------|---------| |
| 74 | +| **Focus** | 25 minutes | 🔵 Blue | Concentrated work session | |
| 75 | +| **Short Break** | 5 minutes | 🟢 Green | Quick rest between sessions | |
| 76 | +| **Long Break** | 15 minutes | 🟡 Yellow | Extended rest after multiple sessions | |
| 77 | + |
| 78 | +### State Management |
| 79 | + |
| 80 | +The timer maintains the following state variables: |
| 81 | + |
| 82 | +```javascript |
| 83 | +let timeLeft = FOCUS_TIME; // Remaining time in seconds |
| 84 | +let isRunning = false; // Whether the timer is active |
| 85 | +let currentMode = "focus"; // Current timer mode |
| 86 | +let timerInterval = null; // Reference to the interval |
| 87 | +``` |
| 88 | + |
| 89 | +--- |
| 90 | + |
| 91 | +## 📚 JavaScript Functions Reference |
| 92 | + |
| 93 | +Below is a comprehensive breakdown of each function in `main.js`: |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +### 1. `updateTimerDisplay()` |
| 98 | + |
| 99 | +**Purpose:** Updates the timer display and progress ring based on the current time remaining. |
| 100 | + |
| 101 | +**How it works:** |
| 102 | +1. Calculates minutes and seconds from `timeLeft` (which is in seconds) |
| 103 | +2. Formats the time as `MM:SS` with leading zeros (e.g., `05:09`) |
| 104 | +3. Updates the timer display text content |
| 105 | +4. Calculates and updates the SVG ring progress based on elapsed time |
| 106 | + |
| 107 | +**Code Location:** Lines 46-66 |
| 108 | + |
| 109 | +```javascript |
| 110 | +function updateTimerDisplay() { |
| 111 | + // Calculate minutes and seconds |
| 112 | + const minutes = Math.floor(timeLeft / 60); |
| 113 | + const seconds = timeLeft % 60; |
| 114 | + |
| 115 | + // Format as "MM:SS" (e.g., "05:09") |
| 116 | + const formattedTime = |
| 117 | + minutes.toString().padStart(2, "0") + |
| 118 | + ":" + |
| 119 | + seconds.toString().padStart(2, "0"); |
| 120 | + |
| 121 | + timerDisplay.textContent = formattedTime; |
| 122 | + |
| 123 | + // Update the ring progress |
| 124 | + let totalTime = FOCUS_TIME; |
| 125 | + if (currentMode === "short-break") totalTime = SHORT_BREAK_TIME; |
| 126 | + if (currentMode === "long-break") totalTime = LONG_BREAK_TIME; |
| 127 | + |
| 128 | + const progress = 1 - timeLeft / totalTime; |
| 129 | + ringProgress.style.strokeDashoffset = progress; |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +**Key Concepts:** |
| 134 | +- `Math.floor()` - Rounds down to get whole minutes |
| 135 | +- `% (modulo)` - Gets remaining seconds |
| 136 | +- `padStart(2, "0")` - Ensures two-digit formatting |
| 137 | +- `strokeDashoffset` - Controls SVG circle animation |
| 138 | + |
| 139 | +--- |
| 140 | + |
| 141 | +### 2. `startTimer()` |
| 142 | + |
| 143 | +**Purpose:** Toggles the timer between running and paused states. |
| 144 | + |
| 145 | +**How it works:** |
| 146 | +1. If timer is running → Pauses it (clears interval, updates UI) |
| 147 | +2. If timer is paused → Starts it (sets interval, updates UI) |
| 148 | +3. Uses `setInterval()` to decrement time every 1000ms (1 second) |
| 149 | +4. When time reaches 0, stops the timer and shows an alert |
| 150 | + |
| 151 | +**Code Location:** Lines 68-98 |
| 152 | + |
| 153 | +```javascript |
| 154 | +function startTimer() { |
| 155 | + if (isRunning) { |
| 156 | + // If already running, pause it |
| 157 | + clearInterval(timerInterval); |
| 158 | + isRunning = false; |
| 159 | + toggleIcon.textContent = "play_arrow"; |
| 160 | + timerLabel.textContent = "Paused"; |
| 161 | + } else { |
| 162 | + // Start the timer |
| 163 | + isRunning = true; |
| 164 | + toggleIcon.textContent = "pause"; |
| 165 | + timerLabel.textContent = |
| 166 | + currentMode === "focus" ? "Stay focused" : "Take a break"; |
| 167 | + |
| 168 | + timerInterval = setInterval(() => { |
| 169 | + if (timeLeft > 0) { |
| 170 | + timeLeft = timeLeft - 1; |
| 171 | + updateTimerDisplay(); |
| 172 | + } else { |
| 173 | + // Timer finished |
| 174 | + clearInterval(timerInterval); |
| 175 | + isRunning = false; |
| 176 | + toggleIcon.textContent = "play_arrow"; |
| 177 | + alert("Time is up!"); |
| 178 | + } |
| 179 | + }, 1000); |
| 180 | + } |
| 181 | +} |
| 182 | +``` |
| 183 | + |
| 184 | +**Key Concepts:** |
| 185 | +- `setInterval()` - Executes code repeatedly at specified intervals |
| 186 | +- `clearInterval()` - Stops the interval when pausing |
| 187 | +- Ternary operator (`? :`) - Conditional text based on mode |
| 188 | + |
| 189 | +--- |
| 190 | + |
| 191 | +### 3. `resetTimer()` |
| 192 | + |
| 193 | +**Purpose:** Stops the timer and resets it to the initial duration of the current mode. |
| 194 | + |
| 195 | +**How it works:** |
| 196 | +1. Clears any running interval |
| 197 | +2. Sets `isRunning` to `false` |
| 198 | +3. Resets the toggle icon to play |
| 199 | +4. Sets `timeLeft` based on the current mode |
| 200 | +5. Updates the display |
| 201 | + |
| 202 | +**Code Location:** Lines 100-111 |
| 203 | + |
| 204 | +```javascript |
| 205 | +function resetTimer() { |
| 206 | + clearInterval(timerInterval); |
| 207 | + isRunning = false; |
| 208 | + toggleIcon.textContent = "play_arrow"; |
| 209 | + |
| 210 | + // Reset time based on current mode |
| 211 | + if (currentMode === "focus") timeLeft = FOCUS_TIME; |
| 212 | + else if (currentMode === "short-break") timeLeft = SHORT_BREAK_TIME; |
| 213 | + else if (currentMode === "long-break") timeLeft = LONG_BREAK_TIME; |
| 214 | + |
| 215 | + updateTimerDisplay(); |
| 216 | +} |
| 217 | +``` |
| 218 | + |
| 219 | +**Key Concepts:** |
| 220 | +- Conditional statements for mode-specific durations |
| 221 | +- State reset pattern |
| 222 | + |
| 223 | +--- |
| 224 | + |
| 225 | +### 4. `setMode(mode)` |
| 226 | + |
| 227 | +**Purpose:** Switches between timer modes (Focus, Short Break, Long Break). |
| 228 | + |
| 229 | +**Parameters:** |
| 230 | +- `mode` (string): One of `"focus"`, `"short-break"`, or `"long-break"` |
| 231 | + |
| 232 | +**How it works:** |
| 233 | +1. Updates the `currentMode` state variable |
| 234 | +2. Removes "active" class from all mode buttons |
| 235 | +3. Adds "active" class to the selected mode button |
| 236 | +4. Changes the theme color using CSS custom properties |
| 237 | +5. Updates the timer label text |
| 238 | +6. Stops any running timer |
| 239 | +7. Resets time to the new mode's duration |
| 240 | +8. Updates the display |
| 241 | + |
| 242 | +**Code Location:** Lines 113-151 |
| 243 | + |
| 244 | +```javascript |
| 245 | +function setMode(mode) { |
| 246 | + currentMode = mode; |
| 247 | + |
| 248 | + // Update buttons style |
| 249 | + focusBtn.classList.remove("active"); |
| 250 | + shortBreakBtn.classList.remove("active"); |
| 251 | + longBreakBtn.classList.remove("active"); |
| 252 | + |
| 253 | + const root = document.documentElement; |
| 254 | + |
| 255 | + if (mode === "focus") { |
| 256 | + timeLeft = FOCUS_TIME; |
| 257 | + focusBtn.classList.add("active"); |
| 258 | + root.style.setProperty("--theme-primary", COLOR_BLUE); |
| 259 | + timerLabel.textContent = "Ready to focus?"; |
| 260 | + } else if (mode === "short-break") { |
| 261 | + timeLeft = SHORT_BREAK_TIME; |
| 262 | + shortBreakBtn.classList.add("active"); |
| 263 | + root.style.setProperty("--theme-primary", COLOR_GREEN); |
| 264 | + timerLabel.textContent = "Time for a break"; |
| 265 | + } else if (mode === "long-break") { |
| 266 | + timeLeft = LONG_BREAK_TIME; |
| 267 | + longBreakBtn.classList.add("active"); |
| 268 | + root.style.setProperty("--theme-primary", COLOR_YELLOW); |
| 269 | + timerLabel.textContent = "Time for a long break"; |
| 270 | + } |
| 271 | + |
| 272 | + // Stop timer when switching modes |
| 273 | + clearInterval(timerInterval); |
| 274 | + isRunning = false; |
| 275 | + toggleIcon.textContent = "play_arrow"; |
| 276 | + |
| 277 | + updateTimerDisplay(); |
| 278 | +} |
| 279 | +``` |
| 280 | + |
| 281 | +**Key Concepts:** |
| 282 | +- `classList.add()` / `classList.remove()` - DOM class manipulation |
| 283 | +- `document.documentElement` - Access to root HTML element |
| 284 | +- `style.setProperty()` - Dynamically change CSS variables |
| 285 | +- CSS Custom Properties (`--theme-primary`) - Dynamic theming |
| 286 | + |
| 287 | +--- |
| 288 | + |
| 289 | +## 🚀 Getting Started |
| 290 | + |
| 291 | +### Prerequisites |
| 292 | + |
| 293 | +- A modern web browser (Chrome, Firefox, Safari, Edge) |
| 294 | +- No build tools or package managers required! |
| 295 | + |
| 296 | +### Running the Application |
| 297 | + |
| 298 | +1. **Clone or download** the repository |
| 299 | +2. **Navigate** to the `Live Project` folder |
| 300 | +3. **Open** `pomodoro.html` in your web browser |
| 301 | + |
| 302 | +```bash |
| 303 | +# If using a local server (optional) |
| 304 | +cd "Live Project" |
| 305 | +npx serve . |
| 306 | +``` |
| 307 | + |
| 308 | +Or simply double-click `pomodoro.html` to open it in your default browser. |
| 309 | + |
| 310 | +--- |
| 311 | + |
| 312 | +## 🛠️ Technologies Used |
| 313 | + |
| 314 | +| Technology | Purpose | |
| 315 | +|------------|---------| |
| 316 | +| **HTML5** | Semantic structure and accessibility | |
| 317 | +| **CSS3** | Modern styling with glassmorphism and animations | |
| 318 | +| **JavaScript (ES6+)** | Timer logic and DOM manipulation | |
| 319 | +| **Google Fonts** | Typography (Google Sans, Roboto Mono) | |
| 320 | +| **Material Symbols** | Icon library for UI elements | |
| 321 | +| **SVG** | Scalable progress ring animation | |
| 322 | + |
| 323 | +--- |
| 324 | + |
| 325 | +## 📝 Learning Objectives |
| 326 | + |
| 327 | +This project demonstrates the following JavaScript concepts: |
| 328 | + |
| 329 | +1. **Variables & Constants** - Using `const` and `let` for state management |
| 330 | +2. **DOM Selection** - `document.getElementById()` for element access |
| 331 | +3. **Functions** - Modular code organization |
| 332 | +4. **Event Listeners** - Responding to user interactions |
| 333 | +5. **Timers** - `setInterval()` and `clearInterval()` |
| 334 | +6. **String Methods** - `padStart()` for formatting |
| 335 | +7. **Math Operations** - `Math.floor()`, modulo operator |
| 336 | +8. **CSS Manipulation** - Dynamic class and style changes |
| 337 | +9. **Conditional Logic** - `if/else` statements |
| 338 | +10. **Arrow Functions** - Modern function syntax |
| 339 | + |
| 340 | +--- |
| 341 | + |
| 342 | +## 📄 License |
| 343 | + |
| 344 | +This project is open source and available for educational purposes. |
| 345 | + |
| 346 | +--- |
| 347 | + |
| 348 | +<div align="center"> |
| 349 | + |
| 350 | +**Made with ❤️ by GDG PUP Web Dev Team** |
| 351 | + |
| 352 | +*Happy Coding! 🚀* |
| 353 | + |
| 354 | +</div> |
0 commit comments