Home>

I made a custom progress bar with three separate parts (uniquely customizable center, left and right), but I have a hard time aligning the center block correctly at all stages.

I will first show the desired end state with three graphical layouts, then describe the current problem, and finally provide my current workaround, which is buggy and needs some kind of fix.

Three desired states:
Desired initial state result with 1% left alignment:

Desired halfway result with center block 50% perfectly in the middle:

Desired end state with center block fully right aligned:

body {margin: 100px; background: #CCC}
.fullbar {
    background-color: blue;
    width: 100%;
}
.progress {
    background: green;
  margin: 10px 0 0 0;
    text-align: right;
    padding: 0 0px 1px 0px;
    line-height: 5px;
}
.number {
  background: inherit;
  color: #FFF;
    padding: 4px;
    padding: 0 2px 1px 3px;
} 
<
div class= "fullbar" >
<
div class= "progress" style= "width: 50%" >
    <
div class= "number" >
50% <
/div >
<
/div >
<
/div >

Problem

The center block should be horizontally aligned perfectly in the middle when the state is 50%. However, it is not. The end of the line is centered instead of the div containing the actual number "50%".

PS. For some unknown reason, the body of the central block does not display correctly in the code view. Perhaps due to my extensive css dumps, my progress bar looked different than it did here in bare code. But we are talking about a div with a class name number that needs to be centered correctly, which is not currently there.

Free translation of the question Sophisticated proportional triple element progress bar design using only CSSfrom the participant @Sam.

  • Answer # 1

    You can do as shown below. I use different colors to see the result better

    body {
      margin: 100px;
      background: #CCC
    }
    .fullbar {
      background-color: blue;
    }
    .progress {
      background: lightgreen;
      margin: 10px 0 0 0;
      height: 5px;
      position: relative; /* relative here * /
      width: var (-p);
    }
    .number {
      position: absolute; /* absolute here * /
      background: rgba (255,0,0,0.5);
      left: 100%; /* push to the right side * /
      transform: translateX (calc (-1 * var (-p))); /* offset to the left based on --p * /
      top: -10px;
      bottom: -10px;
      color: #FFF;
      padding: 0 2px 1px 3px;
    } 
    <
    div class= "fullbar" >
      <
    div class= "progress" style= "-p: 0%" >
        <
    div class= "number" >
    0% <
    /div >
      <
    /div >
    <
    /div >
    <
    div class= "fullbar" >
      <
    div class= "progress" style= "-p: 20%" >
        <
    div class= "number" >
    20% <
    /div >
      <
    /div >
    <
    /div >
    <
    div class= "fullbar" >
      <
    div class= "progress" style= "-p: 50%" >
        <
    div class= "number" >
    50% <
    /div >
      <
    /div >
    <
    /div >
    <
    div class= "fullbar" >
      <
    div class= "progress" style= "-p: 80%" >
        <
    div class= "number" >
    80% <
    /div >
      <
    /div >
    <
    /div >
    <
    div class= "fullbar" >
      <
    div class= "progress" style= "-p: 100%" >
        <
    div class= "number" >
    100% <
    /div >
      <
    /div >
    <
    /div >
    

    Another idea using just one div:

    body {
      margin: 100px;
      background: #CCC
    }
    .progress {
      margin: 20px 0;
      height: 10px;
      position: relative;
      background: linear-gradient (lightgreen 0 0) 0 /var (-p) 100% no-repeat blue;
    }
    .progress :: before {
      content: attr (style);
      font-family: monospace;
      font-size: 20px;
      white-space: nowrap;
      text-indent: -4ch;
      overflow: hidden;
      position: absolute;
      background: rgba (255, 0, 0, 0.8);
      border: 5px solid transparent;
      top: 50%;
      left: var (-p);
      transform: translate (calc (-1 * var (-p)), -50%);
      color: #FFF;
    } 
    <
    div class= "progress" style= "-p: 0%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 20%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 50%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 80%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 100%" >
    <
    /div >
    

    Update

    Example with animation

    body {
      margin: 100px;
      background: #CCC
    }
    .progress {
      margin: 20px 0;
      height: 10px;
      position: relative;
      background: linear-gradient (lightgreen 0 0) 0 /var (-p) 100% no-repeat blue;
      animation: p1 1s 1s both;
    }
    .progress :: before {
      content: attr (style);
      font-family: monospace;
      font-size: 20px;
      white-space: nowrap;
      text-indent: -4ch;
      overflow: hidden;
      position: absolute;
      background: rgba (255, 0, 0, 0.8);
      border: 5px solid transparent;
      top: 50%;
      left: var (-p);
      transform: translate (calc (-1 * var (-p)), -50%);
      color: #FFF;
      animation: p2 1s 1s both;
    }
    @keyframes p1 {from {background-size: 0 100%}}
    @keyframes p2 {from {left: 0; transform: translate (0%, -50%)}} 
    <
    div class= "progress" style= "-p: 0%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 20%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 50%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 80%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 100%" >
    <
    /div >
    

    For numeric animation, I would use@propertybut so far it is only available on chrome an edge:

    body {
      margin: 100px;
      background: #CCC
    }
    @property --p {
      syntax: '<
    number >
    ';
      inherits: true;
      initial-value: 0;
    }
    @property --s {
      syntax: '<
    integer >
    ';
      inherits: true;initial-value: 0;}
    .progress {
      margin: 20px 0;
      height: 10px;
      position: relative;
      background: linear-gradient (lightgreen 0 0) 0 /calc (var (-p, 0) * 1%) 100% no-repeat blue;
      animation: p1 1s 1s both;
      --s: var (-p);
      counter-set: num var (-s);
    }
    .progress :: before {
      content: counter (num) "%";
      font-family: monospace;
      font-size: 20px;
      white-space: nowrap;
      overflow: hidden;
      position: absolute;
      background: rgba (255, 0, 0, 0.8);
      border: 5px solid transparent;
      top: 50%;
      left: calc (var (-p) * 1%);
      transform: translate (calc (-1% * var (-p)), -50%);
      color: #FFF;
    }
    @keyframes p1 {from {--p: 0; -s: 0}} 
    <
    div class= "progress" style= "-p: 0" >
    <
    /div >
    <
    div class= "progress" style= "-p: 20" >
    <
    /div >
    <
    div class= "progress" style= "-p: 50" >
    <
    /div >
    <
    div class= "progress" style= "-p: 80" >
    <
    /div >
    <
    div class= "progress" style= "-p: 100" >
    <
    /div >
    

    Until there is more support, you can fake this like below:

    body {
      margin: 100px;
      background: #CCC
    }
    .progress {
      margin: 20px 0;
      height: 10px;
      position: relative;
      background: linear-gradient (lightgreen 0 0) 0 /var (-p) 100% no-repeat blue;
      animation: p1 1s 1s both;
    }
    .progress :: before {
      content: attr (style);
      font-family: monospace;
      font-size: 20px;
      white-space: nowrap;
      text-indent: -4ch;
      overflow: hidden;
      position: absolute;
      background: rgba (255, 0, 0, 0.8);
      border: 5px solid transparent;
      top: 50%;
      left: var (-p);
      transform: translate (calc (-1 * var (-p)), -50%);
      color: #FFF;
      animation: p2 1s 1s both, p3 0.8s 1s both;
    }
    @keyframes p1 {from {background-size: 0% 100%}}
    @keyframes p2 {from {left: 0%; transform: translate (0%, -50%)}}
    @keyframes p3 {/* put some randome number to fake the animation * /
      0% {content: "-p: 0%"}
      15% {content: "-p: 5%"}
      30% {content: "-p: 9%"}
      45% {content: "-p: 10%"}
      60% {content: "-p: 11%"}
      75% {content: "-p: 40%"}
      90% {content: "-p: 20%"}
    } 
    <
    div class= "progress" style= "-p: 0%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 20%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 50%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 80%" >
    <
    /div >
    <
    div class= "progress" style= "-p: 100%" >
    <
    /div >
    

    Or some crazy idea like below:

    body {
      margin: 100px;
      background: #CCC
    }
    .progress {
      margin: 20px 0;
      height: 10px;
      position: relative;
      background: linear-gradient (lightgreen 0 0) 0 /calc (var (-p) * 1%) 100% no-repeat blue;
      animation: p1 1s 1s both;
    }
    .progress :: before {
      content: "0% \ A 1% \ A 2% \ A 3% \ A 4% \ A 5% \ A 6% \ A 7% \ A 8% \ A 9% \ A 10% \ A 11% \ A 12% \ A 13% \ A 14% \ A 15% \ A 16% \ A 17% \ A 18% \ A 19% \ A 20% \ A 21% \ A 22% \ A 23% \ A 24 % \ A 25% \ A 26% \ A 27% \ A 28% \ A 29% \ A 30% \ A 31% \ A 32% \ A 33% \ A 34% \ A 35% \ A 36% \ A 37% \ A 38% \ A 39% \ A 40% \ A 41% \ A 42% \ A 43% \ A 44% \ A 45% \ A 46% \ A 47% \ A 48% \ A 49 % \ A 50% \ A 51% \ A 52% \ A 53% \ A 54% \ A 55% \ A 56% \ A 57% \ A 58% \ A 59% \ A 60% \ A 61% \ A 62% \ A 63% \ A 64% \ A 65% \ A 66% \ A 67% \ A 68% \ A 69% \ A 70% \ A 71% \ A 72% \ A 73% \ A 74 % \ A 75% \ A 76% \ A 77% \ A 78% \ A 79% \ A 80% \ A 81% \ A 82% \ A 83% \ A 84% \ A 85% \ A 86% \ A 87% \ A 88% \ A 89% \ A 90% \ A 91% \ A 92% \ A 93% \ A 94% \ A 95% \ A 96% \ A 97% \ A 98% \ A 99 % \ A 100% ";
      font-family: monospace;
      font-size: 20px;
      width: 4ch;
      line-height: 1em;
      height: 1em;
      text-align: center;
      overflow: hidden;
      position: absolute;
      background: rgba (255, 0, 0, 0.8);
      border: 5px solid transparent;
      top: 50%;
      left: calc (var (-p) * 1%);
      transform: translate (calc (-1% * var (-p)), -50%);
      color: # 0000;
      text-shadow: 0 calc (var (-p) * -1em) 0 #fff;
      animation: p2 1s 1s both, p3 1s 1s steps (var (-p)) both;
    }
    @keyframes p1 {from {background-size: 0% 100%}}
    @keyframes p2 {from {left: 0%; transform: translate (0%, -50%)}}
    @keyframes p3 {from {text-shadow: 0 0 0 #fff}} 
    <div class= "progress" style= "-p: 0" > <
    /div >
    <
    div class= "progress" style= "-p: 20" >
    <
    /div >
    <
    div class= "progress" style= "-p: 50" >
    <
    /div >
    <
    div class= "progress" style= "-p: 80" >
    <
    /div >
    <
    div class= "progress" style= "-p: 100" >
    <
    /div >
    

    Free translation the answerfrom participant @Temani Afif.

    Thanks for the translation, interesting implementation.

    Greg--2021-09-30 15:46:30

    @ –Greg always please. And someone does not like it, minus. In such cases, I always think that the minus person needs to try it himself, find something interesting, useful among the thousands of answers, translate, arrange, and then decide whether to minus the other for the time and work spent.

    Alexandr_TT2021-09-30 16:00:32

    There is a suspicion that they did not even delve into the question. The question itself is framed in sufficient detail and it is clear that it is very rare

    Greg--2021-09-30 16:06:51

    I can't understand how the last response content works: "0% \ A 1% ...." as I understand it, the content in the column moves in the mask with a step of 0.0

    Greg--2021-09-30 16:16:34

    @Sevastopol 'Thank you Pavel! I thought that summer would end and you would free yourself from construction business. I also had in the summer season only the evenings and nights for SO. I hope that when you get back we will work for our pleasure.

    Alexandr_TT2021-09-30 16:43:44