webdevpuneet.com

Water Intake Tracker — Free HTML CSS JS Snippet

Water Intake Tracker · Dashboards · Plain HTML, CSS & JS · Live preview

CategoryDashboards

Copy & paste

Source Code

<div class="wit-card">
  <div class="wit-head">
    <div class="wit-ring-wrap">
      <svg class="wit-ring" viewBox="0 0 90 90">
        <circle class="wit-ring-track" cx="45" cy="45" r="39"></circle>
        <circle class="wit-ring-fill" id="witRingFill" cx="45" cy="45" r="39"></circle>
      </svg>
      <div class="wit-ring-center">
        <span class="wit-ring-pct" id="witPct">0%</span>
        <span class="wit-ring-label">of goal</span>
      </div>
    </div>
    <div class="wit-stats">
      <div class="wit-amount"><span id="witAmount">0</span> <small>/ 2000 ml</small></div>
      <div class="wit-sub">Daily goal &middot; resets at midnight</div>
      <div class="wit-bottle-wrap">
        <div class="wit-bottle">
          <div class="wit-bottle-fill" id="witBottleFill"></div>
        </div>
      </div>
    </div>
  </div>

  <div class="wit-actions">
    <button type="button" class="wit-add" data-ml="250">+250 ml</button>
    <button type="button" class="wit-add" data-ml="500">+500 ml</button>
    <button type="button" class="wit-reset" id="witReset">Reset</button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Single source of truth
One current value drives the ring, the bottle, and the total — never out of sync.
SVG progress ring
A stroke-dashoffset animation fills the ring smoothly from the top, clockwise.
Bottom-up bottle fill
A bottle silhouette fills from the base like a real container, not a generic bar.
Habit-sized quick-add
+250 ml and +500 ml map to a standard glass and bottle for one-tap logging.
Capped percentage
Progress never exceeds 100% visually even if intake goes past the daily goal.
Smooth animated transitions
Ring offset and bottle height both animate with an eased cubic-bezier curve.
Data-attribute driven buttons
Quick-add amounts read from data-ml, so adding a size is a markup-only change.
Explicit reset note
Copy and behavior make clear the tracker is meant to reset at midnight daily.

About this UI Snippet

Water Intake Tracker — Fillable Bottle, Progress Ring & Quick-Add Logging

Screenshot of the Water Intake Tracker snippet rendered live

Hydration tracking works best when logging a glass of water takes one tap and the current state is legible at a glance. This widget combines three familiar visual metaphors — a progress ring, a filling bottle, and a running total — all driven by the same number, plus two quick-add buttons sized to how people actually drink water (a glass, a bottle). It pairs naturally with a BMI calculator or a broader health dashboard.

One number, three views

current is the single source of truth for how many milliliters have been logged today. render() derives everything else from it: the percentage of the daily goal, the progress ring's stroke offset, the bottle's fill height, and the raw milliliter total — so there's never a case where the ring says one thing and the bottle says another, because both are just different renderings of the same percentage.

A ring drawn with stroke-dashoffset

The progress ring is two overlapping SVG circles: a static track and a colored fill circle whose stroke-dasharray is set to its full circumference and whose stroke-dashoffset is animated down as progress increases. Rotating the SVG -90deg makes the fill start from the top rather than the 3 o'clock position, matching the convention used by most goal-ring UI (including Apple's Activity rings).

A bottle that fills like a real container

The bottle is a simple bordered rectangle with a small "cap" pseudo-element and an absolutely positioned fill div anchored to the bottom, whose height animates as a percentage. Because the fill grows from the bottom up rather than a generic left-to-right bar, it reads intuitively as liquid filling a vessel — closer to how a real water bottle empties and fills.

Quick-add sized to real habits

The +250 ml and +500 ml buttons map to a standard glass and a standard bottle respectively, so logging intake takes one tap for the common cases rather than opening a number input every time. Both buttons funnel through the same click handler pattern reading a data-ml attribute, so adding a third quick-add size (say, +750 ml for a large bottle) is a one-line HTML change with no new JavaScript.

Daily reset

This demo's Reset button simulates the day rolling over; in production you'd instead reset current to zero automatically at local midnight (comparing the logged date to today's date on load, or scheduling a reset), and persist the running total to localStorage or your backend so it survives a page reload during the day.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA hydration card renders at 0 ml with an empty ring and bottle.
  2. 2
    Click +250 ml or +500 mlThe total, ring, and bottle fill all update together with a smooth animated transition.
  3. 3
    Watch the goal ringThe ring fills clockwise from the top as you approach the 2000 ml daily goal.
  4. 4
    Click ResetThe tracker returns to 0 ml — simulating the daily reset.
  5. 5
    Change the goalEdit the GOAL constant to match a different daily target.
  6. 6
    Persist itStore current in localStorage or your backend, keyed to today's date, for a real app.

Real-world uses

Common Use Cases

Health and fitness dashboards
Log hydration beside other daily metrics with one tap, using +250 ml and +500 ml buttons that map to a standard glass and bottle.
Wearable companion apps
Add manual water entries to complement automatic tracking, with one value driving the ring, the filling bottle and the running total.
Workplace wellness programmes
Pair with a workout interval timer in a workplace wellness hub, rewarding small daily habits with visible progress toward a goal.
Health calculator suites
Place beside a BMI calculator so people can calculate a goal and then track progress toward it.
SVG ring and fill reference
See how stroke-dashoffset animates the progress ring and a bottom-up bottle fill gives a second, more tactile signal.

Got questions?

Frequently Asked Questions

The ring fill is an SVG circle with stroke-dasharray set to its full circumference, so the visible stroke length is controlled entirely by stroke-dashoffset — a lower offset reveals more of the circle. render() sets that offset based on the current percentage, and a CSS transition on stroke-dashoffset animates the change smoothly whenever current updates.

The SVG is rotated -90 degrees with a CSS transform. SVG circles start drawing at the 3 o'clock position by default, so without the rotation the ring would begin filling from the right side. Rotating it -90 degrees moves the starting point to 12 o'clock, matching the convention used by most circular goal-ring UI.

The raw milliliter total displayed keeps counting up accurately, but the ring and bottle fill percentage are both clamped to 100% via Math.min(100, ...) so neither visual overflows or wraps. This means the ring and bottle always represent "how much of my goal is met," capped sensibly, while the number still shows your true total.

Store the logged total together with the date it was logged (e.g. in localStorage as { date: "2026-08-22", ml: 1250 }). On load, compare the stored date to today's date; if they differ, reset current to zero before rendering. You can also schedule a setTimeout to the next local midnight to reset the UI live if the page stays open overnight.

Keep current as component state (a number), and derive the percentage, ring offset, and bottle height with computed values (useMemo, a Vue computed property, or an Angular getter) rather than direct DOM writes. The quick-add buttons become state updaters (setCurrent(c => c + amount)), and persistence to localStorage or an API is a straightforward effect keyed on current changing.