webdevpuneet.com

Donation Thermometer — Free Fundraising Progress Bar HTML CSS JS

Donation Thermometer · Charts · Plain HTML, CSS & JS · Live preview

CategoryCharts

Copy & paste

Source Code

<div class="dt-card">
  <div class="dt-header">
    <h3>Winter Coat Drive</h3>
    <p>Help us keep 500 kids warm this season</p>
  </div>

  <div class="dt-body">
    <div class="dt-tube-wrap">
      <div class="dt-tube" id="dtTube" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" aria-label="Funds raised toward goal">
        <div class="dt-tick dt-tick-100"><span>$10,000</span></div>
        <div class="dt-tick dt-tick-75"><span>$7,500</span></div>
        <div class="dt-tick dt-tick-50"><span>$5,000</span></div>
        <div class="dt-tick dt-tick-25"><span>$2,500</span></div>
        <div class="dt-fill" id="dtFill"></div>
      </div>
      <div class="dt-bulb"><div class="dt-bulb-fill" id="dtBulb"></div></div>
    </div>

    <div class="dt-stats">
      <div class="dt-stat">
        <span class="dt-stat-label">Raised</span>
        <span class="dt-stat-value" id="dtRaised">$0</span>
      </div>
      <div class="dt-stat">
        <span class="dt-stat-label">Goal</span>
        <span class="dt-stat-value" id="dtGoal">$10,000</span>
      </div>
      <div class="dt-stat">
        <span class="dt-stat-label">To goal</span>
        <span class="dt-stat-value dt-pct" id="dtPct">0%</span>
      </div>
    </div>

    <div class="dt-controls">
      <button type="button" class="dt-btn" id="dtAdd">+ Add $500 gift</button>
      <button type="button" class="dt-btn dt-btn-ghost" id="dtReset">Reset</button>
    </div>
  </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

Tube-and-bulb visual
A two-part fill mimics a real mercury thermometer, with the bulb reading full at any nonzero amount.
Milestone ticks
25/50/75/100% marks highlight in amber as the campaign crosses each one.
Live stat row
Raised, goal, and percent-to-goal update together from one number.
Single render() source of truth
Every visual derives from the raised value, so state never drifts.
Smooth eased rise
A cubic-bezier transition makes each gift feel like a physical rise in liquid.
Accessible progressbar role
aria-valuenow updates so assistive tech announces real progress.
Locale-formatted currency
toLocaleString formats amounts with thousands separators.
Simple config
GOAL and STEP are the only numbers you need to change for a real campaign.

About this UI Snippet

Donation Thermometer — A Vertical Fundraising Gauge With Milestone Ticks

Screenshot of the Donation Thermometer snippet rendered live

The donation thermometer is the oldest fundraising visual in the book — a mercury-style tube that rises as gifts come in, instantly communicating "how close are we" without anyone reading a number. This snippet rebuilds it in plain HTML, CSS, and vanilla JavaScript: a tube-and-bulb fill, milestone tick marks at 25/50/75/100%, and a live stats row.

Tube plus bulb, like the real thing

The visual is two parts: a tall .dt-tube and a rounded .dt-bulb beneath it, each with its own fill layer whose height is driven by the raised-to-goal ratio. The bulb fill is offset 30 points ahead of the tube's percentage so it never reads as empty at $0 — a small realism touch borrowed from actual glass thermometers, where the bulb always holds some mercury.

Milestone ticks that light up

Four dashed tick lines sit at 25%, 50%, 75%, and 100% of the tube's height, each labeled with its dollar amount. As raised crosses a threshold, that tick's .dt-passed class turns its line and label amber — a lightweight way to celebrate progress at a glance without a separate animation for every milestone.

One render() function, one source of truth

raised is a single number. Every visual — the fill heights, the raised/goal/percent stat row, and the milestone tick states — is derived from it inside one render() call, the same pattern as the rest of this library: change the number, call render(), and everything stays consistent.

Smooth, physical-feeling rise

The fill's height transitions over 1.1s with a custom cubic-bezier that overshoots slightly before settling, so each new gift feels like a real rise in liquid rather than an instant jump — pair this with a count-up on the raised figure for an even more tactile update.

Accessible by default

The tube carries role="progressbar" with live aria-valuenow, so screen readers announce the current percentage as it changes — important for a widget whose entire point is otherwise visual.

Customizing it

Swap GOAL and STEP for your campaign's real numbers, wire dtAdd to your donation webhook instead of a demo button, and change the milestone percentages or add more ticks. Pair it with a donation amount picker or a progress bar for a flatter alternative.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA tube-and-bulb thermometer renders at $0 of a $10,000 goal.
  2. 2
    Click "Add $500 gift"The fill rises smoothly and the stats update.
  3. 3
    Watch the milestonesTick lines at 25/50/75/100% turn amber once passed.
  4. 4
    Click ResetThe thermometer empties back to $0.
  5. 5
    Change GOAL and STEPSet your real campaign goal and gift increment.
  6. 6
    Wire it to real donationsCall render() after adding to raised from your payment webhook.

Real-world uses

Common Use Cases

Nonprofit campaign pages
Use the classic fundraising visual to show how close a capital campaign is to its goal, with milestone ticks highlighting at 25, 50, 75 and 100%.
Crowdfunding trackers
Show progress toward stretch goals, with the stat row updating raised, goal and percent together from one number through a single render() function.
School and PTA fundraisers
Offer a familiar, low-effort progress display, and place a donation amount picker next to it so visitors can give straight away.
Giving days and telethons
Pair with a live visitor counter to show both momentum and audience while the campaign is running.
Team goals and sales targets
Reuse the mercury-style fill for internal targets, or compare it with a simpler gradient progress bar.
Related: Lollipop Chart
See the Lollipop Chart for a related charts pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The bulb's fill height is set to the tube's percentage plus a fixed 30-point offset, capped at 100%. That means even at 0% overall progress the bulb shows some fill, matching how a real thermometer's bulb always holds mercury — purely a visual realism choice, not tied to any real minimum amount.

Each tick element is checked against the current percentage on every render: if the raised percentage is at or above the tick's threshold (25, 50, 75, or 100), a dt-passed class is added, which switches its dashed line and label color to amber via CSS transitions. No separate timers or animations are needed — it is a direct comparison recalculated each render.

Replace the demo dtAdd button handler with your payment webhook or polling logic: whenever a new gift is confirmed, add its amount to the raised variable and call render(). Everything else — fill heights, stats, and milestone highlighting — updates automatically from that one number.

Yes. GOAL controls the denominator for every percentage calculation, so changing it rescales the whole gauge. The milestone percentages are a plain array (25, 50, 75, 100) inside the forEach loop — add, remove, or change values there, and add matching .dt-tick elements with the corresponding bottom offset in CSS.

Hold raised in component state (useState, ref(), or a component field) and derive the percentage in a computed value; bind it to the fill and bulb heights and the stat text. The render() function's logic maps directly to a computed/derived value in any framework — only the DOM-writing lines change to framework bindings.