How to create dashed circles with uniform spacing?

Viewed 4569

I wanted to make a dotted circle with CSS and created it with the following process.

Although the dashed circle could be displayed by this process, The gap between the end and the beginning of the dashed line became narrow, and the gap was not uniform.

.c {
  width: 500px;
  height: 500px;
  border-width: 15px;
  border-style: dashed;
  border-radius: 600px;
}
<div class="c"></div>

Is there a way to make the gap uniform? can we also control the gap between dashes?

If that's not possible with CSS alone, we're considering using JavaScript or something similar.

2 Answers

Here is an optimized version of the conic-gradient() solution where you can easily control the number of dashes and the space between

.box {
  --d:4deg; /* distance between dashes */
  --n:30;   /* number of dashes */
  --c:#000; /* color of dashes */
  width: 180px;
  display:inline-block;
  border-radius:50%;
  border:2px solid transparent; /* control the thickness of border*/
  background: 
    linear-gradient(#fff,#fff) padding-box padding-box,
    repeating-conic-gradient(
        var(--c)    0 calc(360deg/var(--n) - var(--d)), 
        transparent 0 calc(360deg/var(--n)) 
    ) border-box;
}

/* keep the element square */
.box:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div class="box"></div>

<div class="box" style="--n:20;border-width:5px;width:150px"></div>

<div class="box" style="--n:8;--d:20deg;border-width:5px;width:150px"></div>

<div class="box" style="--n:10;--d:15deg;border-width:3px;width:100px"></div>

<div class="box" style="--n:10;--d:20deg;border-width:3px;width:100px"></div>

To have full transparency we consider mask

.box {
  --d:4deg; /* distance between dashes */
  --n:30;   /* number of dashes */
  --c:#000; /* color of dashes */
  --b:2px;   /* control the thickness of border*/
  
  width: 180px;
  display:inline-block;
  border-radius:50%;
  background: 
    repeating-conic-gradient(
        var(--c)    0 calc(360deg/var(--n) - var(--d)), 
        transparent 0 calc(360deg/var(--n)));
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - var(--b)),#fff calc(100% - var(--b) + 1px));
          mask:radial-gradient(farthest-side,transparent calc(100% - var(--b)),#fff calc(100% - var(--b) + 1px));
}

/* keep the element square */
.box:before {
  content:"";
  display:block;
  padding-top:100%;
}


body {
  background:linear-gradient(to right,yellow,pink);
}
<div class="box"></div>

<div class="box" style="--n:20;--b:5px;width:150px;--c:blue"></div>

<div class="box" style="--n:8;--d:20deg;--b:10px;width:130px;--c:red"></div>

<div class="box" style="--n:18;--d:12deg;--b:8px;width:100px;--c:green"></div>

<div class="box" style="--n:10;--d:20deg;--b:3px;width:100px;--c:purple"></div>

CSS dashed circle with uniform space

To make things funny we can even consider a more complex coloration to the dashes:

.box {
  --d:4deg; /* distance between dashes */
  --n:30;   /* number of dashes */
  --b:2px;   /* control the thickness of border*/
  
  width: 180px;
  display:inline-block;
  border-radius:50%;
  background:linear-gradient(red,blue);
  -webkit-mask:
      radial-gradient(farthest-side,transparent calc(100% - var(--b)),#fff calc(100% - var(--b) + 1px)),
      repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--d)),transparent 0 calc(360deg/var(--n)));
  -webkit-mask-composite: source-in;
          mask:
      radial-gradient(farthest-side,transparent calc(100% - var(--b)),#fff calc(100% - var(--b) + 1px)),
      repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--d)),transparent 0 calc(360deg/var(--n)));
          mask-composite: intersect;
}

/* keep the element square */
.box:before {
  content:"";
  display:block;
  padding-top:100%;
}


body {
  background:linear-gradient(to right,yellow,pink);
}
<div class="box"></div>

<div class="box" style="--n:20;--b:5px;width:150px;background:conic-gradient(green,orange,black)"></div>

<div class="box" style="--n:8;--d:20deg;--b:10px;width:130px;background:conic-gradient(black,white,black)"></div>

<div class="box" style="--n:18;--d:12deg;--b:8px;width:100px;background:linear-gradient(60deg,red 50%,green 0)"></div>

<div class="box" style="--n:10;--d:20deg;--b:3px;width:100px;background:#fff"></div>

Transparent CSS dashes with conic-gradient and mask

You may for sure want some content inside so better apply the mask/background on a pseudo element to avoid masking the content:

.box {
  --d:4deg; /* distance between dashes */
  --n:30;   /* number of dashes */
  --b:2px;   /* control the thickness of border*/
  
  width: 180px;
  display:inline-flex;
  justify-content:center;
  align-items:center;
  font-size:35px;
  border-radius:50%;
  position:relative;
}
.box::after {
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  z-index:-1;
  border-radius:inherit;
  background:var(--c,linear-gradient(red,blue));
  -webkit-mask:
      radial-gradient(farthest-side,transparent calc(100% - var(--b)),#fff calc(100% - var(--b) + 1px)),
      repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--d)),transparent 0 calc(360deg/var(--n)));
  -webkit-mask-composite: source-in;
          mask:
      radial-gradient(farthest-side,transparent calc(100% - var(--b)),#fff calc(100% - var(--b) + 1px)),
      repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--d)),transparent 0 calc(360deg/var(--n)));
          mask-composite: intersect;
  
}

/* keep the element square */
.box:before {
  content:"";
  padding-top:100%;
}


body {
  background:linear-gradient(to right,yellow,pink);
}
<div class="box">19</div>

<div class="box" style="--n:20;--b:5px;width:150px;--c:conic-gradient(green,orange,black)">17</div>

<div class="box" style="--n:8;--d:20deg;--b:10px;width:130px;--c:conic-gradient(black,white,black)">5</div>

<div class="box" style="--n:18;--d:12deg;--b:8px;width:100px;--c:linear-gradient(60deg,red 50%,green 0)">9</div>

<div class="box" style="--n:10;--d:20deg;--b:3px;width:100px;--c:#fff">13</div>


Related question to get more CSS ideas to achieve a similar result: CSS Only Pie Chart - How to add spacing/padding between slices?. You will find more supported ways than conic-gradient() (actually it doesn't work on Firefox) but you are required to use a lot of code unlike the above solution where only one element is needed.


Using SVG you will also need some calculation to make sure you have a uniform spacing:

svg {
  width:200px;
}
<svg viewBox="-3 -3 106 106">
  <!-- 
    The circumference of the circle is 2*PI*R ~ 314.16
    if we want N dashed we use d=314.16/N
    For N = 20 we have d=15.71
    For a gap of 5 we will have "10.71,5" (d - gap,gap)
  -->
  <circle cx="50" cy="50" r="50" 
    stroke-dasharray="10.71, 5" 
    fill="transparent" 
    stroke="black" 
    stroke-width="5" />
</svg>

With CSS variables we can make it easier but it's not supported in all the browser (actually it doesn't work in Firefox)

svg {
  --n:20; /* number of dashes*/
  --d:5;  /* distance */
  width:200px;
}

svg circle {
   stroke-dasharray:calc((2*3.14*50)/var(--n) - var(--d)), var(--d); 
}
<svg viewBox="-3 -3 106 106">
  <circle cx="50" cy="50" r="50" fill="transparent" stroke="black" stroke-width="5" />
</svg>

<svg viewBox="-3 -3 106 106" style="width:150px;--n:20;--d:10">
  <circle cx="50" cy="50" r="50" fill="transparent" stroke="red"   stroke-width="5" />
</svg>

<svg viewBox="-3 -3 106 106" style="width:100px;--n:8;--d:15">
  <circle cx="50" cy="50" r="50" fill="transparent" stroke="green" stroke-width="5" />
</svg>

SVG uniform space dashes

We can also easily use the SVG as background to make things more flexible:

.box {
  display:inline-block;
  width:200px;
  background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-3 -3 106 106"><circle cx="50" cy="50" r="50" fill="transparent" stroke="black" stroke-width="5" style="stroke-dasharray:29.25, 10" /></svg>') center/contain;
}

.box:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div class="box">

</div>

<div class="box" style="width:150px;">

</div>

<div class="box" style="width:100px;">

</div>

When used as background, you need to manually set the value so you will need a different background each time. We can only make the color easy to change by using the SVG as mask;

.box {
  display:inline-block;
  width:200px;
  position:relative;
}

.box:before {
  content:"";
  display:block;
  padding-top:100%;
}

.box::after {
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  z-index:-1;
  background:var(--c,red);
  -webkit-mask:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-3 -3 106 106"><circle cx="50" cy="50" r="50" fill="transparent" stroke="black" stroke-width="5" style="stroke-dasharray:29.25, 10" /></svg>') center/contain;
          mask:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-3 -3 106 106"><circle cx="50" cy="50" r="50" fill="transparent" stroke="black" stroke-width="5" style="stroke-dasharray:29.25, 10" /></svg>') center/contain;
}
<div class="box">

</div>

<div class="box" style="width:150px;--c:linear-gradient(60deg,green 50%,yellow 0);">

</div>

<div class="box" style="width:100px;--c:linear-gradient(red,blue)">

</div>

SVG dashed border with uniform space

Use stroke-dasharray with SVG.

svg {
  width: 20vmax;
  height: 20vmax;
}
<svg viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="48" stroke-dasharray="10, 4" fill="transparent" stroke="purple" stroke-width="5" />
</svg>

Or you can use radial-gradient(), repeating-conic-gradient() functions without Firefox.

.c {
  width: 20vmax;
  height: 20vmax;
  background-image: radial-gradient(#fff 68%, transparent 68% 70%, #fff 70%),
    repeating-conic-gradient(#000 0% 3%, transparent 3% 5%);
}
<div class="c"></div>

Related