How to make border radius on small div appear the same as a larger div

Viewed 116

I am creating a progress bar and my issue is: When the % progress is low (width: 0-5%), the border radius does not look the same as when it is larger (width: 5-100%).

In example 1 below, you can see the progress bar as it is intended to look.

In example 2 & 3, you can see how it looks when the width is low, causing the border-radius of the progress bar to not function the same as example 1.

I am considering I need a different way to "cut off" the progress, rather than just decreasing the width, but I wasn't able to come up with anything simple to do so. Anyone have a good idea on how to improve this code?

Note: I also have a secondary "ghost" progress bar which experiences the same issue and has complicated my attempts to keep it simple.

I have looked up some progress bar examples, but they experience the same issue. Some of the ones I have seen avoid the problem entirely by skipping straight from 0 to 5% but I do not want to do that.

.progress-bar {
  position: absolute;
  width: 0px;
  height: 17px;
  border: 0px solid white;
  /* border-radius issue */
  border-radius: 8px;
}

.progress-bar-container {
  position: relative;
  width: 200px;
}

/* some helper classes */

.ghost {
  opacity: 0.15;
}

.progress-percent {
  margin-bottom: 4px;
}

.bg-blue {
  background: #0096FF;
}

.bg-grey {
  background: #E8E8E8;
}

.outer-container {
  margin-top: 16px
}
<!-- example 1 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="width: 75%;">
    </div>
    <!-- variable width is above ~5% -->
    <div class="progress-bar bg-blue" style="width: 45%">
    </div>
  </div>
</div>

<!-- example 2 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="width: 45%">
    </div>
    <!-- variable width is below ~5% -->
    <div class="progress-bar bg-blue" style="width: 1%">
    </div>
  </div>
</div>

<!-- example 3 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="width: 45%">
    </div>
    <!-- variable width is below ~5% -->
    <div class="progress-bar bg-blue" style="width: 3%">
    </div>
  </div>
</div>

Here is the JSFiddle with some extra hover text which is un-related to issue.

4 Answers

Try to add overflow: hidden; to .progress-bar

.progress-bar {
  position: absolute;
  width: 0px;
  height: 17px;
  border: 0px solid white;
  /* border-radius issue */
  border-radius: 8px;
  overflow: hidden;
}

.progress-bar-container {
  position: relative;
  width: 200px;
}

/* some helper classes */

.ghost {
  opacity: 0.15;
}

.progress-percent {
  margin-bottom: 4px;
}

.bg-blue {
  background: #0096FF;
}

.bg-grey {
  background: #E8E8E8;
}

.outer-container {
  margin-top: 16px
}
<!-- example 1 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="width: 75%;">
    </div>
    <!-- variable width is above ~5% -->
    <div class="progress-bar bg-blue" style="width: 45%">
    </div>
  </div>
</div>

<!-- example 2 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="width: 45%">
    </div>
    <!-- variable width is below ~5% -->
    <div class="progress-bar bg-blue" style="width: 1%">
    </div>
  </div>
</div>

<!-- example 3 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="width: 45%">
    </div>
    <!-- variable width is below ~5% -->
    <div class="progress-bar bg-blue" style="width: 3%">
    </div>
  </div>
</div>

Consider this question: What does a 4px wide pill shape look like with 8px rounded corners?

It's a trick question because a 4px wide pill shape can not have rounded corners bigger than 2px. CSS understands this so when you define rounded corners bigger than 1/2 of a shape's width, it automatically scales them down.

My preferred way to overcome things like this is to mask your loading bar. This is where you put a shape over your loading bar that is the same color as the background to hide bits of it.

enter image description here

The nice thing about masking is that you can make your loading bar look however you want, not just rounded/square:

enter image description here

The border radius needs 'enough' pixels to play with.

This snippet sets a CSS variable --w instead of the actual width in the elements and then in CSS increases the width by 16px and moves them back 16px so there is a rounded look to even the smallest width.

.progress-bar {
  position: absolute;
  width: 0px;
  height: 17px;
  border: 0px solid white;
  /* border-radius issue */
  border-radius: 8px;
}

.progress-bar.bg-blue {
  width: calc(var(--w) + 16px);
  left: -16px;
}

.progress-bar-container {
  position: relative;
  width: 200px;
}


/* some helper classes */

.ghost {
  opacity: 0.15;
}

.progress-percent {
  margin-bottom: 4px;
}

.bg-blue {
  background: #0096FF;
}

.bg-grey {
  background: #E8E8E8;
}

.outer-container {
  margin-top: 16px
}
<!-- example 1 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="--w: 75%;">
    </div>
    <!-- variable width is above ~5% -->
    <div class="progress-bar bg-blue" style="--w: 45%">
    </div>
  </div>
</div>

<!-- example 2 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="--w: 45%">
    </div>
    <!-- variable width is below ~5% -->
    <div class="progress-bar bg-blue" style="--w: 1%">
    </div>
  </div>
</div>

<!-- example 3 -->
<div class="outer-container">
  <div class="progress-bar progress-bar-container bg-grey">
    <div class="progress-bar ghost bg-blue" style="--w: 45%">
    </div>
    <!-- variable width is below ~5% -->
    <div class="progress-bar bg-blue" style="--w: 3%">
    </div>
  </div>
</div>

I would just use one div and two pseudo-elements. Have the pseudo-elements take up 100% of the width and then translate them by calculating their distance to the right, and finally hiding them by setting overflow: hidden on the div.

Use CSS variables to control the percentage of the pseudo-elements.

.progress-bar {
  position: relative;
  width: 200px;
  background-color: lightgrey;
  overflow: hidden;
  margin-bottom: 1rem;
}

.progress-bar,
.progress-bar::before,
.progress-bar::after {
  --progress-bar-height: 1rem;
  height: var(--progress-bar-height);
  border-radius: var(--progress-bar-height);  
}

.progress-bar::before {
  background-color: lightgreen;
   --x-displacement: calc(100% - var(--progress-1));
}

.progress-bar::after {
   background-color: blue;
   --x-displacement: calc(100% - var(--progress-2));
}

.progress-bar::before,
.progress-bar::after {
  content: '';
  position: absolute;
  right: var(--x-displacement);
  width: 100%;
}
<div class="progress-bar" style="--progress-1: 80%; --progress-2: 0%"></div>

<div class="progress-bar" style="--progress-1: 50%; --progress-2: 10%"></div>

<div class="progress-bar" style="--progress-1: 10%; --progress-2: 70%"></div>

Related