<label class="heat-slider heat-slider--v">
  <span class="heat-slider--label">How Spicy?</span>
  <span class="heat-slider--input">
    <input type="range" value="32" min="0" max="143" oninput="doThings(event)" />
  </span>
</label>

<output>change the slider</output>
@mixin slider-track() {
  background: transparent;
  padding: calc(var(--slider-handle-size) / 1.6) 0;
  margin: 0 calc(var(--slider-handle-size) / 2 * -1);
  cursor: pointer;
}

@mixin slider-thumb($cursor: grab) {
  position: relative;
  appearance: none;
  box-shadow: 0 0 0 .2em #444; // inset, 0 0 0 .2em #fff;
  height: calc(var(--slider-handle-size) * 2);
  width: var(--slider-handle-size);
  margin: calc(var(--slider-handle-size) * -1) 0;
  border-radius: var(--slider-radius, 1em);
  background: transparent;
  cursor: $cursor;
}

// LAYOUT
// - side-by-side
.heat-slider--h {
  display: flex;
  align-items: center;

  .heat-slider--label + * {
    margin-left: 1em;
    flex: 1;
  }
}
  
// - stacked
.heat-slider--v {
  display: flex;
  flex-direction: column;
  justify-content: center;
  
  .heat-slider--label + * {
    margin-top: 1em;
    flex: 1;
  }
}

// MECHANICAL
.heat-slider {
  --slider-base: #d8d8d8;
  --slider-red: #fc3770;
  --slider-orange: #ff7f36;
  --slider-yellow: #F6D866;
  --slider-green: #74C35A;
  --slider-blue: #2C95DD;
  --slider-radius: 1em;
  --slider-handle-size: 1em;
  --p: 22.9%; // magic, set by javascript funtion when input value updates
  
  width: 30em; // cheap opinion
  
  &--input {
    position: relative;
    line-height: 0;
    border-radius: var(--slider-radius);
    background-image: linear-gradient(to right, 
      var(--slider-blue), 
      var(--slider-green), 
      var(--slider-yellow), 
      var(--slider-orange), 
      var(--slider-red)
    );

    // the magic mask
    &::before {
      content: '';
      position: absolute;
      pointer-events: none;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      margin-right: -1px; // hack for border-radius anti-aliasing, ugh
      border-radius: var(--slider-radius);
      background: linear-gradient(to right,
        transparent 0,
        transparent calc(var(--p) + var(--slider-handle-size) / 2),
        var(--slider-base) var(--p),
        var(--slider-base) 100%
      );
    }

    input {
      position: realtive;
      z-index: 1;
      width: 100%;
      appearance: none;
      font: inherit;
      background: transparent;
      border: 0;
      margin: 0;
      padding: 0;

      // slider track
      &::-webkit-slider-runnable-track { @include slider-track() }
      &::-moz-range-track { @include slider-track() }

      // slider thumb
      &::-webkit-slider-thumb { @include slider-thumb() }
      &::-moz-range-thumb { @include slider-thumb() }
      &:focus { outline: none }
      &:active {
        // slider thumb
        &::-webkit-slider-thumb { @include slider-thumb($cursor: grabbing); }
        &::-moz-range-thumb { @include slider-thumb($cursor: grabbing); }
      }
    }
  }
}










// boring
body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: system-ui, sans-serif;
  // font-size: 2em; // uncomment to see full responsive sizing :)
}

*, *::before, *::after { box-sizing: border-box }

output {
  margin: 1em;
  opacity: .4;
}
View Compiled
const output = document.querySelector('output')

const doThings = (event) => {
  const { value, min, max, step, parentElement: parent } = event.target
  const decimals = step && step.includes('.') ? step.split('.')[1] : 1
  const percent = `${((value - min)/(max - min) * 100).toFixed(decimals)}%`
  parent.style.setProperty('--p', percent)

  output.value = `value: ${value} (${percent})`
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.