Building a Pomodoro Timer Application using HTML, CSS, and JS

In this tutorial, we will walk you through the step-by-step process of creating a fully functional Pomodoro Timer Application from scratch. This productivity-boosting web app will be developed using core web technologies including HTML for structuring the content, CSS for styling and layout, and JavaScript for implementing interactive and dynamic timer functionality. Whether you're a beginner learning front-end development or looking to enhance your JavaScript skills, this project-based guide is perfect for mastering the fundamentals of web development while building a practical time management tool.

What is Pomodoro Timer?

The Pomodoro Timer is a highly effective time management technique designed to improve focus, productivity, and efficiency. This method utilizes a countdown timer to divide work sessions into short, focused intervals—traditionally 25 minutes in length—known as "Pomodoros", followed by brief 5-minute breaks to help maintain mental clarity and reduce fatigue. By incorporating regular breaks and structured work periods, the Pomodoro Technique helps users stay motivated, avoid burnout, and manage their time more effectively. It is widely used by students, professionals, developers, and anyone looking to enhance their productivity through better time management strategies.

How does Pomodoro Timer Application work?

The Pomodoro Timer Application we are about to build is a simple yet powerful productivity tool designed to help users manage their time more effectively. This intuitive web-based timer features three distinct time intervals: the Work Timer, the Short Break Timer, and the Long Break Timer. Once the user starts the timer, the application begins with a 25-minute focused work session. After completing this session, the app automatically transitions to a 5-minute short break to help users recharge. These work and break sessions continue to alternate in a loop to promote consistent productivity and focus.

After completing four consecutive Pomodoro work intervals, the application initiates a longer 15-minute long break session, allowing users to relax more deeply before resuming their tasks. To enhance usability, the application includes a Pause Button that lets users temporarily stop the countdown, and a Reset Button that restarts the timer cycle at any point. Whether you’re a developer, student, or remote worker, this JavaScript Pomodoro App is a practical project for improving time management and maintaining productivity throughout the day.

Let's Start the Coding

Step 1: Creating the Interface

First, let's create a new HTML file and save it as index.html. This HTML file contains the relevant elements and HTML structure of our Pomodoro Timer App. Checkout the following script.

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.  
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Pomodoro Timer App using HTML, CSS, and JS</title>
  7.     <link rel="stylesheet" href="style.css">
  8. </head>
  9.  
  10.     <div id="main">
  11.         <h1 id="page-title">Pomodoro Timer App using HTML, CSS, and JS</h1>
  12.         <div id="timer-container">
  13.             <div id="timer"></div>
  14.             <div id="timer-text">
  15.                 <div id="timer-time">00:00</div>
  16.                 <div id="timer-desc">Work</div>
  17.             </div>
  18.         </div>
  19.         <div id="timer-actions">
  20.             <button type="button" id="startBtn">Start</button>
  21.             <button type="button" id="pauseBtn">Pause</button>
  22.             <button type="button" id="resetBtn">Reset</button>
  23.         </div>
  24.     </div>
  25.     <script src="script.js"></script>
  26. </body>
  27.  
  28. </html>

Step 2: Designing the Interface

Next, let's create a new Cascading Stylesheet (CSS) file and save it as style.css. This file contains the scripts for the layout and structure of the elements. It also contains the script for designing the timer, buttons, and other elements. Checkout the script below.

  1. @import url('https://fonts.googleapis.com/css2?family=Carter+One&display=swap');
  2.  
  3. * {
  4.     font-family: "Carter One", system-ui;
  5.     font-weight: 400;
  6.     font-style: normal;
  7. }
  8.  
  9. html,
  10. body {
  11.     padding: unset;
  12.     margin: unset;
  13.     height: 100%;
  14.     width: 100%;
  15.     background-color: #2A4759;
  16.     box-sizing: border-box;
  17. }
  18.  
  19. body {
  20.     display: flex;
  21.     flex-flow: column wrap;
  22.     padding: 25px 30px;
  23.     align-items: center;
  24.     justify-content: center;
  25. }
  26.  
  27. /* App Title */
  28. #page-title {
  29.     color: #fff;
  30.     text-shadow: 3px 3px 5px rgba(0, 0, 0, 0.212);
  31.     text-align: center;
  32.     width: 100%;
  33.     max-width: 500px;
  34. }
  35.  
  36. /* Timer Container */
  37. #timer-container {
  38.     position: relative;
  39.     width: 100%;
  40.     max-width: 500px;
  41. }
  42.  
  43. /* Timer Circular Countdown Spinner */
  44. #timer {
  45.     width: 250px;
  46.     height: 250px;
  47.     margin: auto;
  48.     border-radius: 50% 50%;
  49.     border: 10px solid transparent;
  50.     background: conic-gradient(#a7a7a7 0,
  51.             #a7a7a7 var(--countdown),
  52.             #fff var(--countdown)) border-box;
  53.     mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  54.     mask-composite: exclude;
  55.     transition: all .2s ease-in-out;
  56.     animation-name: countDown;
  57.     animation-timing-function: linear;
  58.     animation-fill-mode: forwards;
  59.     animation-iteration-count: infinite;
  60.     animation-duration: 10s;
  61.     animation-play-state: paused;
  62. }
  63.  
  64. /* Timer Content */
  65. #timer-text {
  66.     position: absolute;
  67.     top: 0;
  68.     left: 0;
  69.     width: 100%;
  70.     margin: auto;
  71.     height: 100%;
  72.     display: flex;
  73.     flex-flow: column wrap;
  74.     align-items: center;
  75.     justify-content: center;
  76.     color: #fff;
  77.     text-shadow: 0px 3px 3px #020202a8;
  78.     letter-spacing: 3px;
  79.     font-weight: bolder;
  80. }
  81.  
  82. /* Timer Countdown Text */
  83. #timer-time {
  84.     font-size: 3rem;
  85. }
  86.  
  87. /* Timer Description */
  88. #timer-desc {
  89.     font-size: 1.5rem;
  90.     letter-spacing: 1px;
  91. }
  92.  
  93. /* Timer Action Buttons Container */
  94. #timer-actions {
  95.     width: 100%;
  96.     max-width: 500px;
  97.     display: flex;
  98.     flex-flow: row wrap;
  99.     align-items: center;
  100.     justify-content: center;
  101.     padding: 30px 0px;
  102. }
  103.  
  104. /* Timer Action Buttons */
  105. #timer-actions>button {
  106.     font-size: 1rem;
  107.     letter-spacing: 2px;
  108.     margin: 0px 5px;
  109.     border: unset;
  110.     border-radius: 3px;
  111.     cursor: pointer;
  112.     outline: unset;
  113.     padding: 5px 15px;
  114.     color: #fff;
  115.     box-shadow: 3px 3px 3px #ffffff14;
  116.     transition: all .2s ease-in-out;
  117. }
  118.  
  119. /* Timer Start Button */
  120. #timer-actions>button#startBtn {
  121.     background-color: #0A5EB0;
  122. }
  123.  
  124. /* Timer Pause Button */
  125. #timer-actions>button#pauseBtn {
  126.     background-color: #F95454;
  127. }
  128.  
  129. /* Timer Reset Button */
  130. #timer-actions>button#resetBtn {
  131.     background-color: #3f5b70;
  132. }
  133.  
  134. /* Timer Buttons when Hovered */
  135. #timer-actions>button:hover {
  136.     filter: brightness(1.2);
  137. }
  138.  
  139. /* Countdown Percentage Property for Border Animation */
  140. @property --countdown {
  141.     syntax: "<percentage>";
  142.     initial-value: 0%;
  143.     inherits: false;
  144. }
  145.  
  146. @keyframes countDown {
  147.     to {
  148.         --countdown: 100%;
  149.     }
  150. }

Step 3: Creating the Functionality

Lastly, let’s create a new JavaScript file and save it as script.js. This file contains all the custom JavaScript functions that power the core functionality of our Pomodoro Timer Application. It includes the logic for starting, pausing, and resetting the timers, as well as handling the seamless transition between work sessions, short breaks, and long breaks.

In addition, the script.js file manages the circular countdown animation, looping mechanisms for Pomodoro cycles, and user interface interactions that make the timer intuitive and interactive. This modular and well-organized JavaScript file plays a critical role in ensuring the app functions smoothly. Check out the code snippet below to gain a deeper understanding of how each part works.

  1.     let TimerContainer, // Timer Container Element
  2.     TimerText,      // Timer Text Element i.e.(25:00)
  3.     TimerDesc,      // Timer Description Element i.e(Take A Breadk)
  4.     TimerCircle,    // Timer Circular Countdown Element
  5.     StartBtn,       // Strat Button
  6.     PauseBtn,       // Pause Button
  7.     ResetBtn;       // Reset Button
  8.  
  9. var WorkTime = (60 * 60 * 25),      // Work Time Duration (25 mintues)
  10.     BreakTime = (60 * 60 * 5),      // Break Time Duration (5 minutes)
  11.     LongBreakTime = (60 * 60 * 15),  // Long Break Time Duration (15 minutes)
  12.     CurrentWorkTime,                // Current Work Time Duration
  13.     CurrentBreakTime,               // Current Break Time Duration
  14.     CurrentLongBreakTime,           // Current Break Time Duration
  15.     WorkInterval,                   // Work Timer Interval
  16.     BreakInterval,                  // Break Timer Interval
  17.     LongBreakInterval,              // Long Break Timer Interval
  18.     WorkCount = 0;
  19.  
  20. /*
  21. // For Testing only
  22. WorkTime = (60 * 10);     //(10 seconds)
  23. BreakTime = (60 * 5);     //(5 seconds)
  24. LongBreakTime = (60 * 7); //(7 seconds)
  25. */
  26.  
  27. /* On Window Load */
  28. window.addEventListener("load", function () {
  29.     TimerContainer = this.document.getElementById("timer-container");
  30.     TimerText = this.document.getElementById("timer-time");
  31.     TimerDesc = this.document.getElementById("timer-desc");
  32.     TimerCircle = this.document.getElementById("timer");
  33.     StartBtn = this.document.getElementById("startBtn");
  34.     PauseBtn = this.document.getElementById("pauseBtn");
  35.     ResetBtn = this.document.getElementById("resetBtn");
  36.  
  37.     /* Set Default Time */
  38.     CurrentWorkTime = WorkTime;
  39.     CurrentBreakTime = BreakTime;
  40.  
  41.     TimerContainer.classList.add("work");
  42.     TimerText.innerText = ConvertToTime(CurrentWorkTime);
  43.     TimerDesc.innerText = "Work";
  44.  
  45.     TimerCircle.style.setProperty("animation-duration", ((WorkTime / 60) - 1) + `s`);
  46.  
  47.     /* Timer Buttons Event Listeners */
  48.     StartBtn.addEventListener("click", StartTimer);
  49.     PauseBtn.addEventListener("click", PauseTimer);
  50.     ResetBtn.addEventListener("click", ResetTimer);
  51.     PauseBtn.style.display = 'none';
  52.  
  53. })
  54.  
  55.  
  56. /* To Start the Timer */
  57. function StartTimer() {
  58.  
  59.     if (TimerContainer.classList.contains("started"))
  60.         return;
  61.  
  62.     StartBtn.style.display = 'none';
  63.     PauseBtn.style.display = null;
  64.     TimerContainer.classList.add("started");
  65.  
  66.     if (TimerContainer.classList.contains("work")) {
  67.         StartWork();
  68.     } else if (TimerContainer.classList.contains("break")) {
  69.         StartBreak();
  70.     } else if (TimerContainer.classList.contains("long-break")) {
  71.         StartLongBreak();
  72.     }
  73.  
  74.  
  75. }
  76.  
  77. /* To Start the Work Timer */
  78. function StartWork() {
  79.     WorkCount++;
  80.     WorkInterval = setInterval(function () {
  81.         CurrentWorkTime = CurrentWorkTime - 60;
  82.         TimerText.innerText = ConvertToTime(CurrentWorkTime);
  83.         var countDownPerc = 100 - ((CurrentWorkTime / WorkTime) * 100);
  84.         countDownPerc = countDownPerc.toFixed(2);
  85.  
  86.         TimerCircle.style.setProperty("animation-play-state", `running`);
  87.  
  88.         if (CurrentWorkTime <= 0) {
  89.             clearInterval(WorkInterval);
  90.             changeTimer();
  91.             return;
  92.         }
  93.     }, 1000);
  94. }
  95.  
  96. /* To Start the Break Timer */
  97. function StartBreak() {
  98.     BreakInterval = setInterval(function () {
  99.         CurrentBreakTime = CurrentBreakTime - 60;
  100.         TimerText.innerText = ConvertToTime(CurrentBreakTime);
  101.         var countDownPerc = 100 - ((CurrentBreakTime / BreakTime) * 100);
  102.         countDownPerc = countDownPerc.toFixed(2);
  103.  
  104.         TimerCircle.style.setProperty("animation-play-state", `running`);
  105.  
  106.         if (CurrentBreakTime <= 0) {
  107.             clearInterval(BreakInterval);
  108.             changeTimer();
  109.             return;
  110.         }
  111.     }, 1000);
  112. }
  113. /* To Start the Long Break Timer */
  114. function StartLongBreak() {
  115.     WorkCount = 0;
  116.     LongBreakInterval = setInterval(function () {
  117.         CurrentLongBreakTime = CurrentLongBreakTime - 60;
  118.         TimerText.innerText = ConvertToTime(CurrentLongBreakTime);
  119.         var countDownPerc = 100 - ((CurrentLongBreakTime / LongBreakTime) * 100);
  120.         countDownPerc = countDownPerc.toFixed(2);
  121.  
  122.         TimerCircle.style.setProperty("animation-play-state", `running`);
  123.  
  124.         if (CurrentLongBreakTime <= 0) {
  125.             clearInterval(LongBreakInterval);
  126.             changeTimer();
  127.             return;
  128.         }
  129.     }, 1000);
  130. }
  131.  
  132.  
  133. /* Timer Type Transition */
  134. function changeTimer() {
  135.     TimerCircle.removeAttribute("style");
  136.     TimerCircle.style.setProperty('animation-name', 'none');
  137.     TimerCircle.offsetHeight;
  138.     TimerCircle.style.setProperty('animation-name', null);
  139.     if (TimerContainer.classList.contains("break") || TimerContainer.classList.contains("long-break")) {
  140.         CurrentWorkTime = WorkTime;
  141.         if (TimerContainer.classList.contains("break"))
  142.             TimerContainer.classList.remove("break");
  143.         if (TimerContainer.classList.contains("long-break"))
  144.             TimerContainer.classList.remove("long-break");
  145.         TimerContainer.classList.add("work");
  146.         TimerText.innerText = ConvertToTime(CurrentWorkTime);
  147.         TimerDesc.innerText = "Work";
  148.         TimerCircle.style.setProperty("animation-duration", ((WorkTime / 60) - 1) + `s`);
  149.         StartWork();
  150.     } else if (TimerContainer.classList.contains("work") && WorkCount >= 4) {
  151.         CurrentLongBreakTime = LongBreakTime;
  152.         TimerContainer.classList.remove("work");
  153.         TimerContainer.classList.add("long-break");
  154.         TimerText.innerText = ConvertToTime(CurrentLongBreakTime);
  155.         TimerDesc.innerText = "Take a Long Break";
  156.         TimerCircle.style.setProperty("animation-duration", ((LongBreakTime / 60) - 1) + `s`);
  157.         StartLongBreak();
  158.     } else if (TimerContainer.classList.contains("work")) {
  159.         CurrentBreakTime = BreakTime;
  160.         TimerContainer.classList.remove("work");
  161.         TimerContainer.classList.add("break");
  162.         TimerText.innerText = ConvertToTime(CurrentBreakTime);
  163.         TimerDesc.innerText = "Take a Break";
  164.         TimerCircle.style.setProperty("animation-duration", ((BreakTime / 60) - 1) + `s`);
  165.         StartBreak();
  166.     }
  167. }
  168.  
  169. /* Pause Time */
  170. function PauseTimer() {
  171.     PauseBtn.style.display = 'none';
  172.     StartBtn.style.display = null;
  173.     if (TimerContainer.classList.contains("break")) {
  174.         clearInterval(BreakInterval);
  175.     } else if (TimerContainer.classList.contains("long-break")) {
  176.         clearInterval(LongBreakInterval);
  177.     } else if (TimerContainer.classList.contains("work")) {
  178.         clearInterval(WorkInterval);
  179.     }
  180.     TimerCircle.style.setProperty("animation-play-state", `paused`);
  181.  
  182.  
  183.     if (TimerContainer.classList.contains("started"))
  184.         TimerContainer.classList.remove("started");
  185. }
  186.  
  187. /** Reset Timer */
  188. function ResetTimer() {
  189.     PauseTimer();
  190.     CurrentWorkTime = WorkTime;
  191.     TimerContainer.classList.remove("break");
  192.     TimerContainer.classList.add("work");
  193.     TimerText.innerText = ConvertToTime(CurrentWorkTime);
  194.     TimerDesc.innerText = "Work";
  195.  
  196.     TimerCircle.style.setProperty("animation-duration", ((WorkTime / 60) - 1) + `s`);
  197.     TimerCircle.removeAttribute("style");
  198.     TimerCircle.style.setProperty('animation-name', 'none');
  199.     TimerCircle.offsetHeight;
  200.     TimerCircle.style.setProperty('animation-name', null);
  201. }
  202.  
  203. /** Convert Time to Text */
  204. function ConvertToTime(timer) {
  205.     var converted,
  206.         min,
  207.         sec;
  208.  
  209.     min = timer / (60 * 60);
  210.     min = Math.floor(min);
  211.  
  212.     sec = (timer - (min * 60 * 60)) / 60;
  213.     sec = Math.floor(sec);
  214.  
  215.     min = String(min).padStart(2, "0");
  216.     sec = String(sec).padStart(2, "0");
  217.  
  218.     converted = `${min}:${sec}`;
  219.  
  220.     return (converted);
  221.  
  222. }

And there you have it! The complete source code provided above will produce a fully functional Pomodoro Timer Application with an interactive user interface. The final output should closely resemble the example images shown below, demonstrating the layout, timer functionality, and animated countdown features in action. This is a great project to strengthen your skills in HTML, CSS, and JavaScript, while also building a practical tool for boosting productivity and managing your time efficiently.

Interface

Pomodoro Timer Application Tutorial

Work Timer

Pomodoro Timer Application Tutorial

Break Timer

Pomodoro Timer Application Tutorial

Long Break Timer

Pomodoro Timer Application Tutorial

I have also provided the complete source code zip file on this website and is free to download. Feel free to download and modify it the way you wanted. The download button is located below this article content.

There you go. I hope this Pomodoro Timer Application Tutorial using HTML, CSS, and JavaScript will help you with what you are looking for and you'll find something useful from the source code.

Explore more on this website for more Tutorials, Free Source Codes, and Articles covering various programming languages.

Happy Coding =)

Add new comment