How to design a pixel-perfect progress ring that works for all browser zoom levels?

35 views Asked by At

My issue is not with the progress ring itself, you can see the attached snippet as an example. It works as expected, and exactly as I need it, but not for all browser zoom levels. At some levels, the size of the inner circle and/or the spacing between the ring and the inner circle is not pixel-perfect. You can clearly see that at certain browser zoom levels, there's less spacing between the ring and the inner circle.

Here's a demonstration of the problem as a GIF:

enter image description here

Is there anything that can be done to fix this across all browsers?

:root {
  --ring-size: 26px;
  --border-width: 2px;
  --progress: 75%;
  --gap-size: 1px;
}

html {
  font-size: 16px;
}

.progressRing {
  width: var(--ring-size);
  height: var(--ring-size);
  background: conic-gradient(red var(--progress), lightgray 0);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.progressRing::before {
  content: "";
  width: calc(100% - var(--border-width) * 2);
  height: calc(100% - var(--border-width) * 2);
  margin: auto;
  background: white;
  border-radius: 50%;
  position: absolute;
}

.innerCircle {
  width: calc(100% - var(--border-width) * 2 - var(--gap-size) * 2);
  height: calc(100% - var(--border-width) * 2 - var(--gap-size) * 2);
  background-color: gray;
  border-radius: 50%;
  z-index: 1;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Progress Ring</title>
  <link rel="stylesheet" href="styles.css" />
</head>

<body>
  <div class="progressRing">
    <div class="innerCircle"></div>
  </div>
</body>

</html>

1

There are 1 answers

0
prkos On

I got a pretty good result using the radial gradient instead of an extra child element and the pseudoelement.

Using the calc technique where you "move" the points that are of the same color half a pixel or full pixel away is what is making the color transitions smooth and also fully centered.

:root {
  --ring-size: 26px;
  --border-width: 2px;
  --progress: 75%;
  --gap-size: 2px;
}

html {
  font-size: 16px;
}

.progressRing {
  width: var(--ring-size);
  height: var(--ring-size);
  background-image: radial-gradient(circle at center, silver 0, silver calc(50% - (var(--border-width) + var(--gap-size)) - 1px), white calc(50% - (var(--border-width) + var(--gap-size))), white calc(50% - var(--border-width) - 1px), transparent calc(50% - var(--border-width))), conic-gradient(red var(--progress), lightgray 0);
  border-radius: 50%;
  display: block;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Progress Ring</title>
  <link rel="stylesheet" href="styles.css" />
</head>

<body>
  <div class="progressRing">
  </div>
</body>

</html>