Pomodoro Countdown Timer — Free HTML CSS JS Snippet

Pomodoro Countdown Timer · Tools · Plain HTML, CSS & JS · Live preview

What's included

Features

Drift-free countdown computed from a target end-timestamp (Date.now() + duration) each tick, not naive decrementing
Three modes — Focus (25m), Short Break (5m), Long Break (15m) — sharing one timing engine
Start/Pause toggle that preserves exact remaining time across pause/resume cycles
Reset button that restores the currently selected mode's full duration
"Time's up!" state with a visually distinct red countdown display
Automatic advance to the logical next mode a moment after a session completes
Tabular numeral formatting so the MM: SS display doesn't jitter in width as digits change
Fully self-contained vanilla JS with no external timer library

About this UI Snippet

Pomodoro Countdown Timer — Drift-Free Focus and Break Countdown

Screenshot of the Pomodoro Countdown Timer snippet rendered live

A naive countdown timer that just decrements a counter every second inside setInterval will drift over time, because setInterval isn't guaranteed to fire exactly every 1000ms — background tabs, GC pauses, and CPU load all introduce small delays that accumulate into visible skew over a 25-minute session. This timer avoids that by never trusting the interval's timing directly.

Timestamp-based countdown instead of a decrementing counter

When the timer starts, start() computes endTime = Date.now() + remainingMs once. From then on, every tick (running every 250ms for a responsive display) recomputes remainingMs = endTime - Date.now() fresh from the wall clock, rather than subtracting a fixed amount each tick. Because the remaining time is always derived from two real timestamps, any delay in when a particular tick actually fires has no effect on accuracy — the displayed time is always correct at the moment it's read.

Pause preserves exact remaining time

Pausing calls clearInterval and recomputes remainingMs one final time from endTime - Date.now() before stopping, so resuming later calls start() again with that exact remaining value and computes a fresh endTime — there's no accumulated rounding error across multiple pause/resume cycles.

Three modes, one shared engine

Focus (25 min), Short Break (5 min), and Long Break (15 min) are just three different durationMs values applied to the same setMode()/start()/tick() functions — switching tabs resets remainingMs to the new mode's duration and updates the active tab styling, but uses identical timing logic underneath.

Auto-advance on completion

When remainingMs reaches zero, the timer shows "Time's up!" and, after a short pause, automatically switches to the logically next mode (focus after a break, short break after a focus session) via a lookup table, so a full focus/break cycle can run without the user manually re-selecting a tab each time.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet's HTML, CSS, and JavaScript to an AI coding assistant like Claude and ask it to adapt "Pomodoro Countdown Timer" to your project — restyle it to match your design system, wire it up to real data instead of the static example, or port it to the framework you're building in.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a tools component called "Pomodoro Countdown Timer" using plain HTML, CSS, and vanilla JavaScript — no framework, no build step.

Requirements:
- Match the structure and behavior of the "Pomodoro Countdown Timer" snippet from the UI Snippets Library.
- Keep the markup semantic and the styling self-contained (no external dependencies beyond what the original snippet uses).
- Keep the JavaScript vanilla, with no framework runtime required.
- Make it easy to restyle via CSS custom properties or class overrides so it can be dropped into a real project.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="pcd-wrap">
  <div class="pcd-tabs" id="pcdTabs">
    <button class="pcd-tab pcd-tab-active" data-mode="focus" data-mins="25">Focus</button>
    <button class="pcd-tab" data-mode="short" data-mins="5">Short Break</button>
    <button class="pcd-tab" data-mode="long" data-mins="15">Long Break</button>
  </div>

  <div class="pcd-display" id="pcdDisplay">25:00</div>
  <div class="pcd-status" id="pcdStatus">Ready to focus</div>

  <div class="pcd-controls">
    <button class="pcd-btn pcd-btn-primary" id="pcdStartBtn">Start</button>
    <button class="pcd-btn" id="pcdResetBtn">Reset</button>
  </div>
</div>

Step by step

How to Use

  1. 1
    Load the snippetClick "Pomodoro Countdown Timer" in the sidebar to load its HTML, CSS, and JS into the editor panels. The preview updates instantly.
  2. 2
    Edit the codeModify any panel — HTML, CSS, or JS. The preview refreshes as you type. Use Reset in each panel header to restore the original.
  3. 3
    Preview on devicesClick the Mobile (375px), Tablet (768px), or Desktop buttons in the preview header to check responsiveness.
  4. 4
    Export in your formatClick "HTML" to download a standalone file, "JSX" for a React component, "Tailwind" for a React + Tailwind CSS component, "Tailwind HTML" for a standalone HTML file with Tailwind CDN, "Vue" for a Vue 3 SFC with <template>/<script setup>/<style scoped>, or "Angular" for a standalone Angular .component.ts file. "Copy all" copies the full code to clipboard.
  5. 5
    Save your versionClick "Save as", type a name, and press Enter. Your snippet saves to IndexedDB and appears in the Saved tab.

Real-world uses

Common Use Cases

Productivity and focus-tracking apps
A ready-made Pomodoro technique timer for any focus or study tool.
Browser extensions and desktop productivity widgets
Drop-in timer logic that stays accurate even if the tab is backgrounded.
Teaching drift-free timer implementation
A clear, practical example of why timestamp-based countdowns beat naive decrementing counters.
Study and habit-tracking dashboards
A clean, minimal timer component that fits into a broader personal productivity UI.

Got questions?

Frequently Asked Questions

setInterval callbacks are not guaranteed to fire at exactly the requested interval — delays accumulate over a 25-minute session and cause visible drift. By recomputing remainingMs as endTime - Date.now() on every tick, the displayed time is always correct regardless of exactly when a given tick fires.

Pausing captures the exact remaining milliseconds (endTime - Date.now()) and stops the interval. Resuming computes a brand new endTime from that exact remaining value, so no time is lost or gained across multiple pause/resume cycles.

Yes — when a session reaches zero, after a short "Time's up!" pause it automatically switches to the logically next mode (a break after focus, focus after a break) using a lookup table, though you can start or reset manually at any time.

Yes — edit the data-mins attribute on each tab button in the HTML (and the fallback minute values in the auto-advance logic in the JS) to whatever durations you want for each mode.