Data✨ Premium

Needle Gauge

Half-dial needle gauge: five coloured zones and a needle that swings, with a slight bounce, to the value set in data-value.

CSSTransformGauge

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Data has 28 effects, including 1 free. Explore the category →

3 usage examples

Guitar tuner — the needle tells whether the string is in tune — Needle Gauge example 1

① Guitar tuner — the needle tells whether the string is in tune

WhenThe main screen of a tuning app: the target note in large type, the gauge in the middle, the six strings at the bottom. While the string rings, the needle follows the offset in cents and settles in the green when the note is right. In the capture, the scene sends a series of offsets instead of listening to a microphone.
WhyA tuner is read at a glance, from a distance, guitar in hand: a needle wavering around a central green zone says "too low / too high / in tune" better than a bare number.
Settingsdata-min="-50" data-max="50" data-unit="cents", narrow green zone in the middle (--ndg-z1:12; --ndg-z2:40; --ndg-z3:60; --ndg-z4:88), --ndg-size:290px, light needle, --ndg-speed:.6s, transparent background.
Humidity of three cellars — one small gauge per card — Needle Gauge example 2

② Humidity of three cellars — one small gauge per card

WhenA wine merchant's monitoring page: one card per cellar with its name, humidity gauge, temperature and a badge. Readings come in one after another; when the storehouse needle leaves the green zone, its card gets an orange outline and switches to "Check".
WhyThree gauges side by side compare instantly; the ideal range (60 to 80%) is drawn on the dial itself, so the eye spots the drifting cellar without reading numbers.
SettingsThree independent .fx-0244-root roots, data-unit="%", zones --ndg-z1:45; --ndg-z2:60; --ndg-z3:80; --ndg-z4:88, --ndg-size:132px, dark brown needle and digits for the cream background.
Coffee balance — the mini gauge answers the grind slider — Needle Gauge example 3

③ Coffee balance — the mini gauge answers the grind slider

WhenA roaster's recipe page: the user drags the "Grind size" slider from fine to coarse, the small "Balance" gauge tips between sour and bitter and the advice under the slider changes. In the capture, the slider moves by itself until someone touches it.
WhyThe gauge turns an abstract setting (grind size) into a concrete consequence (taste); sitting next to the slider, it answers the gesture without leaving the page.
SettingsThe scene's <input type="range"> writes data-value; --ndg-size:92px, number and ticks hidden by the scene CSS, "sour / bitter" words added under the dial, zones 18 / 38 / 62 / 82, --ndg-speed:.7s.

How it works

The .ndg-arc dial is a box twice as wide as it is tall with rounded top corners: a conic-gradient centred at the bottom middle paints five zones (red, orange, green, orange, red) across 180°, and a radial-gradient mask hollows out the middle. The hole is truly transparent, so no dark disc is left behind on a light page.

The .ndg-needle rotates around its foot, which sits on the .ndg-hub at the bottom centre of the dial. The script computes (value − min) / (max − min) × 180° − 90°, and the transform 1s cubic-bezier(.34, 1.56, .64, 1) transition makes it overshoot slightly before settling. An out-of-range value is printed as is while the needle stops at the end of the dial.

Everything is set in the HTML: data-value, data-min and data-max (0 and 100 by default), data-unit, data-decimals, data-label. A MutationObserver watches these attributes, so root.setAttribute('data-value', 42) is enough to move the needle, with no global function. Each .fx-0244-root is self-contained and several gauges can share a page.

When the gauge scrolls into view (IntersectionObserver, 35% threshold) the needle starts from the left stop and sweeps to the value. The number and the min / middle / max ticks are formatted by Intl.NumberFormat in the page language (lang).

Accessibility

  • Meter role: the .ndg-gauge block gets role="meter" with aria-valuemin, aria-valuemax, aria-valuenow and aria-valuetext (formatted value and unit); its name comes from data-label. The dial and ticks are aria-hidden.
  • Colour backed by a number: the zone under the needle is never the only cue, the value is always written below the dial. If your zones carry meaning (too dry, ideal…), state it in text next to the gauge too.
  • Reduced motion: under prefers-reduced-motion: reduce the needle lands directly on the value, with no sweep or bounce, on first display and on every change.
  • No interaction of its own: the gauge displays a value, it is not a control. If users must pick the value, give them a real <input type="range"> that updates data-value, as in the coffee example.

Browser compatibility

Relies on conic-gradient, mask-image (plus its -webkit- form), aspect-ratio, IntersectionObserver and MutationObserver. Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): needle visible at rest and after a change, two independent gauges on one page, reduced motion, 390 px width, white background, no JS error.

Chrome 88+✓ Full (measured)
Firefox 89+✓ Full (measured)
Safari 15+✓ WebKit engine measured, not checked in real Safari
Edge 88+✓ Full (Chromium engine)
Safari iOS 15+✓ Not checked on a real device
Chrome Android 88+✓ Full (Chromium engine)

Without JavaScript the coloured dial, the value and the ticks written in the HTML stay visible; the needle rests at the left stop. Without IntersectionObserver the needle goes straight to the value.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0244-root" data-value="72" data-min="0" data-max="100" data-label="Level">
  <div class="ndg-gauge">
    <div class="ndg-dial" aria-hidden="true">
      <div class="ndg-arc"></div>
      <div class="ndg-needle"></div>
      <div class="ndg-hub"></div>
    </div>
    <div class="ndg-value">72</div>
    <div class="ndg-scale" aria-hidden="true">
      <span class="ndg-min">0</span>
      <span class="ndg-mid">50</span>
      <span class="ndg-max">100</span>
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
data-value, data-min, data-max (HTML, on the root) 72, 0, 100 Value and bounds. Changing data-value from JS swings the needle; the ticks show min, middle and max.
data-unit and data-decimals (HTML) no unit, 0 decimals % gives a percentage formatted for the language ("72%" / "72 %"); any other text is appended after the number ("12 cents"). Up to 3 decimals.
data-label (HTML) "Level" Gauge name read by screen readers.
--ndg-z1 to --ndg-z4 (CSS) 20, 40, 80, 90 Thresholds of the five zones as a percentage of the arc: red up to z1, orange up to z2, green up to z3, orange up to z4, red after that.
--ndg-low, --ndg-warn, --ndg-good (CSS) #ef4444, #f59e0b, #22c55e Colours of the outer, intermediate and central zones.
--ndg-size and --ndg-hole (CSS) 240px, 78% Maximum dial width (it shrinks with its container) and hole radius: 60% gives a thick arc, 88% a thin one.
--ndg-needle, --ndg-accent, --ndg-muted (CSS) indigo-violet gradient, #a78bfa, 50% white Needle, hub and number, ticks. Darken them on a light background.
--ndg-speed (CSS) 1s Duration of the needle swing.
--ndg-bg and --ndg-min-height (CSS) #0a0a0f, 300px Root background and minimum height: transparent and 0 to fit the gauge inside a card.

FAQ

Keep a reference to the root and write the new value into its attribute: document.querySelector('#my-gauge').setAttribute('data-value', reading). The script observes the attribute and swings the needle; no function call or global variable. Set data-min and data-max to the scale of your readings (−50 to 50, 0 to 3,000…).
That is the cubic-bezier(.34, 1.56, .64, 1) curve: its second value above 1 produces a bounce. For a movement without overshoot, replace it with ease-out in the .ndg-needle rule. The bounce disappears on its own for visitors who asked for less motion.
The four thresholds --ndg-z1 to --ndg-z4 are percentages of the arc. For a narrow centred green: 12, 40, 60, 88. For "higher is better": 20, 40, 100, 100 (red, orange, then green to the end). Colours change with --ndg-low, --ndg-warn and --ndg-good.