Why doesn't this radial-gradient complete the circle?
Asked Answered
N

1

6

I'm trying to use a radial gradient to create a border within circle elements that are radio buttons. The basic CSS is shown below. I cannot figure out why the red gradient does not circle the entire circumference of the circles.

As the white color-stop approaches 100%, gaps appear in the red at the top, right, left, and bottom.

Why does this happen, and how would I fix it while still using a radial gradient?

.container {
  background: #ddd;
  width: 400px;
  height: 200px;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 2px solid transparent;
  width: 20px;
  height: 20px;
  margin-right: 20px;
}

.radio1 { background: radial-gradient(circle closest-side, white 75%, red 100%); }
.radio2 { background: radial-gradient(circle closest-side, white 90%, red 100%); }
.radio3 { background: radial-gradient(circle closest-side, white 95%, red 100%); }
.radio4 { background: radial-gradient(circle closest-side, white 99%, red 100%); }
<div class="container">
  <div class="radio"></div>
  <div class="radio radio1"></div>
  <div class="radio radio2"></div>
  <div class="radio radio3"></div>
  <div class="radio radio4"></div>
</div>

Also on JSFiddle: https://jsfiddle.net/zvgcs80f/

Nardi answered 31/5, 2019 at 17:13 Comment(4)
If you didn't feel like messing with the gradients, a simpler alternative might be box-shadow. Something like box-shadow: inset 0 0 3px 3px red;. Of course, you asked why and how with a radial gradient, so this isn't really an answer - just an option :)Datary
background: radial-gradient(circle closest-side, white 99%, red 100%);will not cover a single pixel in a 20px square boxe, so it's even impossible mission to draw a gradient, at 75% to 100% the gradient has about 2.5 pixels to be drawn ... It does not work because your boxes are too small :( the second box has 2pixel to faint from red to white, third and fourth have no chance to draw your gradientTasker
@TylerRoper If I do that, the white background goes away. Is there a way to keep both?Nardi
turn your box to 200px and to 300px and see that it woks fine, .. because there will the minimum and enough room to draw the gradients :(Tasker
I
8

Your percentage will be converted to pixel relatively to the width/height of your element. In your case, your element is small thus 99% and 100% will most likely be converted to the same value or very close values and you will have subpixel rendring issue.

Instead you can rely on calc() where you can easily define the thickness as a pixel value independently of the element size.

You need to also adjust background-origin and make it border-box so that you draw the gradient considering the border area and you will have a perfect circle.

.container {
  background: #ddd;
  width: 400px;
  height: 200px;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 2px solid transparent;
  width: 20px;
  height: 20px;
  margin-right: 20px;
  background-origin:border-box;
}

.radio1 { background-image: radial-gradient(circle closest-side, white calc(100% - 1px), red 100%); }
.radio2 { background-image: radial-gradient(circle closest-side, white calc(100% - 2px), red 100%); }
.radio3 { background-image: radial-gradient(circle closest-side, white calc(100% - 3px), red 100%); }
.radio4 { background-image: radial-gradient(circle closest-side, white calc(100% - 4px), red 100%); }
<div class="container">
  <div class="radio"></div>
  <div class="radio radio1"></div>
  <div class="radio radio2"></div>
  <div class="radio radio3"></div>
  <div class="radio radio4"></div>
</div>

Here is an example with big values of border to better illustrate the issue related to background-origin

.container {
  background: #ddd;
  width: 400px;
  height: 200px;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 20px solid rgba(0,0,0,0.2);
  width: 50px;
  height: 50px;
  margin-right: 20px;
 background-image: radial-gradient(circle closest-side, white calc(100% - 4px), red 100%); }
<div class="container">
  <div class="radio radio4"></div>
</div>

The background is drawn on the padding box then it's getting repeated over all the area (border box).

If you disable the repeat you will have this:

.container {
  background: #ddd;
  width: 400px;
  height: 200px;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 20px solid rgba(0,0,0,0.2);
  width: 50px;
  height: 50px;
  margin-right: 20px;
 background-image: radial-gradient(circle closest-side, white calc(100% - 4px), red 100%); 
  background-repeat:no-repeat;
 }
<div class="container">
  <div class="radio"></div>
</div>

Here is if we repeat only on the X axis

.container {
  background: #ddd;
  width: 400px;
  height: 200px;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 20px solid rgba(0,0,0,0.2);
  width: 50px;
  height: 50px;
  margin-right: 20px;
 background-image: radial-gradient(circle closest-side, white calc(100% - 4px), red 100%); 
  background-repeat:repeat-x;
 }
<div class="container">
  <div class="radio"></div>
</div>

And here is what is happening when using 100% for both colors which is similar to your situation and you will better understand why you have coloration only on the corners.

.container {
  background: #ddd;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 20px solid rgba(0, 0, 0, 0.2);
  width: 50px;
  height: 50px;
  margin-right: 20px;
  background-image: radial-gradient(circle closest-side, white 100%, red 100%);
}

.one {
  background-repeat: no-repeat;
}

.two {
  background-repeat: repeat;
}

.three {
  border-width: 5px;
}
.four {
  width:20px;
  height:20px;
  border-width: 2px;
}
<div class="container">
  <div class="radio one"></div>
  <div class="radio two"></div>
  <div class="radio three"></div>
  <div class="radio four"></div>
</div>

And if we change the origin it's fine:

.container {
  background: #ddd;
  width: 400px;
  height: 200px;
  padding: 20px;
}

.radio {
  display: inline-block;
  background: white;
  border-radius: 50%;
  border: 20px solid rgba(0,0,0,0.2);
  width: 50px;
  height: 50px;
  margin-right: 20px;
 background-image: radial-gradient(circle closest-side, white calc(100% - 4px), red 100%); 
  background-origin:border-box;
 }
<div class="container">
  <div class="radio"></div>
</div>
Interloper answered 31/5, 2019 at 19:31 Comment(1)
This is a very nice answer that went above what I expected. Thank you!Nardi

© 2022 - 2024 — McMap. All rights reserved.