Search code examples
htmlcssgauge

How to create semi-circle/ellipse with HTML CSS? Like a gauge/speedometer


I'm trying to do something like this:

enter image description here

It's not a full or semi circle, and the edges have a little radius. Also need to contain some point to show the current position in percentage. Can someone give me a light on how to put together something close to the 3 ideas below?

.circle {
   display: flex;
   width: 200px;
   height: 100px;
   border-top-left-radius: 200px;
   border-top-right-radius: 200px;
   border: 10px solid gray;
   border-bottom: 0;
}

.circle>.text {
   font-size: 50px;
   margin: auto;
}

.circle.gradient {
   margin-top: 30px;
   border-image-slice: 1;
   border-width: 10px;
   border-bottom: 0;
   border-image-source: linear-gradient(to left, #6ECE34, #E6A749, #E5A749, #F71C1C);
}

.line {
   position: relative;
   margin-top: 30px;
   width: 220px;
   height: 10px;
   border-radius: 5px;
   background: linear-gradient(to left, #6ECE34, #E6A749, #E5A749, #F71C1C);
}

.line>.point {
   position: absolute;
   top: -3px;
   left: 21%;
   width: 8px;
   height: 16px;
   border-radius: 5px;
   border: 1px solid black;
   background: #FFF;
}
<!-- Just semi circle -->
<div class="circle">
   <div class="text">
      21
   </div>
</div>

<!-- With gradient border -->
<div class="circle gradient"></div>

<!-- Maybe strech in Y -->
<div class="line">
   <!-- Relative point -->
   <div class="point"></div>
</div>


Solution

  • You can cobble together that look and functionality using CSS for the look and updating CSS variables for the pointer position/slope in whatever language you are using to update the speeds.

    Here's a snippet for the look with --pointerleft, --pointerright and --pointerdeg set just for the demo. In the operational code you'd need to do some trigonometry to calculate these given the particular speed at the time and then set the CSS variables (e.g. in JS).

    Whether using radial and conic gradients and then having to position the little round bits at the ends can be said to be a recommendation I don't know. SO doesn't like recommendations but I'll risk it and suggest this works but is a bit hacky and an SVG might be better.

    Note, the snippet just has a simple 3 color conic gradient, you can add more/different colors at different degrees as required.

    .ring {
      position: relative;
      --pointerleft: 11%;
      --pointertop: 11%;
      --pointerdeg: -45deg;
      width: 50vmin;
      height: 50vmin;
      background-image: radial-gradient(red 0, red 50%, transparent 50%, transparent 100%), radial-gradient(green 0, green 50%, transparent 50%, transparent 100%), radial-gradient(white 0, white 60%, transparent 60%), conic-gradient(orange 0, green 130deg, white 130deg, white 230deg, red 230deg, orange 360deg);
      background-size: 11% 11%, 11% 11%, 100% 100%, 100% 100%;
      background-repeat: no-repeat;
      background-position: 9.2% 82.3%, 90.8% 82.3%, center center, center center;
      border-radius: 50%;
      border-style: none;
    }
    
    .ring::after {
      position: absolute;
      content: '';
      width: 5%;
      height: 15%;
      left: var(--pointerleft);
      top: var(--pointertop);
      transform: rotate(var(--pointerdeg));
      border-style: solid;
      border-width: 0.5vmin;
      border-radius: 2vmin;
      background-color: white;
    }
    
    .speed {
      display: inline-block;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translateX(-50%) translateY(-50%);
      text-align: center;
      color: gray;
    }
    
    .speed .number {
      font-size: 6vw;
    }
    
    .speed .units {
      font-size: 3vw;
    }
    <div class="ring">
      <div class="speed">
        <div class="number">21</div>
        <div class="units" style=";">km/h</div>
      </div>
    </div>