Search code examples
csslinear-gradients

Is it possible to create a css based gradient on a fixed height scale?


I have a div that gets resized programmatically and sits inside a wrapper div. I would like the background color of the (inner) div to be a gradient, but with the gradient based on the height of the wrapper div.

For example, the gradient could be something like background: linear-gradient(0deg, green 0%, green 50%, yellow 80%, orange 90%, red 100%)

When the inner div is less than 50% of the height of the wrapper div, I want it to be completely green, but if it gets to 70%, it would start to turn yellow at the top, at 80% it starts going orange at the top and if it hit 100% it would have the full gradient. See the codepen below for an example of what I mean in terms of the colors.

The inner div will be resized every 10th of a second or so and will look similar to an audio peak meter.

Is this possible to achieve with pure CSS, or do I need to use javascript? If I use javascript, would it be more performant to have a bunch of css classes and switch them depending on percentage, or color things directly?

I could achieve this with multiple blocks, something like https://codepen.io/octod/pen/vPQLVo (not mine), but ideally I want it to be one continuous block of color with a smooth gradient.

I could also achieve it by keeping the inner div full height and resizing an overlay div, but unfortunately I have a transparent background, so this is not ideal.

EDIT: As requested, this is what I currently have that does not work (it shows the full gradient when the inner div is not the full height). I have simplified it and put css inline for this post.

real snippet: (Edited by Mister Jojos - this is not representative of my code, but the accepted answer is based on this version, so I have left it like this)

.outer {
  display: inline-block;
  height: 400px;
  width: 40px;
  background-color: dimgrey;
  padding: 5px;
  margin: 1em;
  }
.outer > div {
  width: 100%;
  }
  #inner50 {
  height: 50%;
  background: linear-gradient(0deg, green 0%, green 50%, yellow 80%, orange 90%, red 100%);
  }
#inner70 {        /* please set the gradient values */
  height: 70%;
  background: linear-gradient(0deg, green 0%, green 50%, yellow 80%, orange 90%, red 100%);
  }
#inner100 {       /* please set the gradient values */
  height: 100%;
  background: linear-gradient(0deg, green 0%, green 50%, yellow 80%, orange 90%, red 100%);
  }
 
 <!-- result in case 50% --> 
 <div class="outer">  <div id="inner50"></div> </div>

 <!-- result in case 70% --> 
<div class="outer">  <div id="inner70"></div> </div>

 <!-- result in case 100% --> 
<div class="outer">  <div id="inner100"></div> </div>

Javascript snippet:

// this function is called every 1/10th of a second
function updateMeter(newHeight) {
    getElementById("inner").style.height = newHeight+'px';
}

Solution

  • Simply fix the size of the gradient to the size of the outer div:

    .outer {
      display: inline-block;
      height: 400px;
      width: 40px;
      background-color: dimgrey;
      padding: 5px;
      margin: 1em;
    }
    
    .outer>div {
      width: 100%;
      background: 
        linear-gradient(0deg, green 0%, green 50%, yellow 80%, orange 90%, red 100%) 
          top/ /* place it on the top */
          100% 400px; /* width=100% height=400px */
    }
    
    #inner50 {
      height: 50%;
    }
    
    #inner70 {
      height: 70%;
    }
    
    #inner100 {
      height: 100%;
    }
    <!-- result in case 50% -->
    <div class="outer">
      <div id="inner50"></div>
    </div>
    
    <!-- result in case 70% -->
    <div class="outer">
      <div id="inner70"></div>
    </div>
    
    <!-- result in case 100% -->
    <div class="outer">
      <div id="inner100"></div>
    </div>

    Also like below:

    .outer {
      display: inline-flex;
      height: 400px;
      width: 40px;
      background-color: dimgrey;
      padding: 5px;
      margin: 1em;
    }
    
    .outer>div {
      width: 100%;
      margin-top:auto;
      background: 
        linear-gradient(0deg, green 0%, green 50%, yellow 80%, orange 90%, red 100%) 
          bottom/ /* place it on the top */
          100% 400px; /* width=100% height=400px */
    }
    
    #inner50 {
      height: 50%;
    }
    
    #inner70 {
      height: 70%;
    }
    
    #inner100 {
      height: 100%;
    }
    <!-- result in case 50% -->
    <div class="outer">
      <div id="inner50"></div>
    </div>
    
    <!-- result in case 70% -->
    <div class="outer">
      <div id="inner70"></div>
    </div>
    
    <!-- result in case 100% -->
    <div class="outer">
      <div id="inner100"></div>
    </div>