Wishful Coding

Didn't you ever wish your
computer understood you?

Lightweight pure CSS gauge

The other day I was helping my brother with the second version of his performance installation DEMARRAGE, which involves serving a web page from an ESP32 that displays the energy produced by two dynamo bikes.

For this page we wanted a simple gauge, so I figured a solution would be an internet search away. But all the examples I found seemed really complicated, with verbose markup, opaque CSS, and sometimes an entire JS library. So I decidede to make my own.

a dashboard showing a simple CSS gauge

My goals were

  • Very minimal HTML without any extra dummy elements.
  • CSS that is easy to understand and modify.
  • Easy to update the value from JS.
  • A gauge of 270° rather than a semi-circle.
  • (browser compat was not a goal)

The HTML is as simple as it gets, just a div with custom properties and the textual value.

<div class="gauge" style="--value:0.3; font-size:2rem;">30%</div>

Conceptually, the CSS isn’t very complicated either.

  • set the size of the gauge div
  • set a border-radius to make a circle
  • draw a conic-gradiant to make a pie-chart
  • draw a radial-gradient to cut out the center
  • center the text with text-align and line-height

The code CSS makes use of calc and var, primarily to adjust the conic-gradient angle based on a custom property, but also to parameterize the dimensions of the gauge. This means you can override --size and friends to style the gauge without changing hardcoded values.

.gauge {
  --size: 200px;
  --cutout: 50%;
  --color: red;
  --background: white;
  width:var(--size);
  height:var(--size);
  border-radius:calc(var(--size) / 2);
  background:
    radial-gradient(
        var(--background) 0 var(--cutout),
        transparent var(--cutout) 100%),
    conic-gradient(from -135deg, 
        var(--color) calc(270deg*var(--value)),
        grey calc(270deg*var(--value)) 270deg, 
        transparent 270deg);
  text-align: center;
  line-height: var(--size);
}

The JavaScript for changing the gauge value is pretty simple too, given some gauge DOM element el you can change the gauge value and text content by simply doing

el.style.setProperty("--value", 0.8)
el.innerHTML = "80%"

Below is a codepen to play with the code. I hope it’s useful to someone.

See the Pen Pure CSS gauge by Pepijn de Vos (@pepijndevos) on CodePen.

After a bit of chatting on the Recurse Center Zulip, I came up with the following alternative gradients that provide a 3D effect or that goes from red to orange to green. The 3D one works by adding a transparent black gradient to the radial part. The colourful one works by making a fixed backdrop and a transparent-grey gradient on top that reveals the underlying one.

3d red orange green

  background:
    conic-gradient(from -135deg,
      transparent 270deg,
      white 270deg),
    radial-gradient(
      var(--background) 0 var(--cutout),
      #0002 calc(var(--cutout)),
      #0000 calc(var(--cutout) + 15px),
      #0002 calc(var(--cutout) + 30px),
      #0000 calc(var(--cutout) + 30px) 100%),
    conic-gradient(from -135deg,
      var(--color) calc(270deg*var(--value)),
      grey calc(270deg*var(--value)) 270deg,
      transparent 270deg);

  background:
    radial-gradient(
      var(--background) 0 var(--cutout),
      transparent var(--cutout) 100%),
    conic-gradient(from -135deg,
      transparent calc(270deg*var(--value)),
      grey calc(270deg*var(--value)) 270deg,
      transparent 270deg),
    conic-gradient(from -135deg,
      red 0,
      orange 135deg,
      lime 270deg,
      transparent 270deg);
Pepijn de Vos