JSFiddle - React, Tailwind, and code Playground

by nukeboy212

HTML

<input type="range" multiple value="10,40, 90" />

CSS

@supports (--css: variables) {
  input[type="range"].multirange {
    -webkit-appearance: none;
    padding: 0;
    margin: 0;
    display: inline-block;
    vertical-align: top;
    width: 250px;
    margin-top: 50px;
    margin-left: 50px;
    background: lightblue;
  }
  input[type="range"].multirange.original {
    position: absolute;
  }
  input[type="range"].multirange.original::-webkit-slider-thumb {
    position: relative;
    z-index: 2;
  }
  input[type="range"].multirange.original::-moz-range-thumb {
    transform: scale(1);
    /*FF doesn't apply position it seems*/
    G z-index: 1;
  }
  input[type="range"].multirange::-moz-range-track {
    border-color: transparent;
    /* needed to switch FF to "styleable" control */
  }
  input[type="range"].multirange.ghost {
    position: relative;
    background: var(--track-background);
    --track-background: linear-gradient(to right, transparent var(--low), var(--range-color) 0, var(--range-color) var(--high), transparent 0) no-repeat 0 45% / 100% 40%;
    --range-color: hsl(190, 80%, 40%);
  }
  input[type="range"].multirange.ghost::-webkit-slider-runnable-track {
    background: var(--track-background);
  }
  input[type="range"].multirange.ghost::-moz-range-track {
    background: var(--track-background);
  }
}

JavaScript

(function() {
  "use strict";

  var supportsMultiple = self.HTMLInputElement && "valueLow" in HTMLInputElement.prototype;

  var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");

  self.multirange = function(input) {
    if (supportsMultiple || input.classList.contains("multirange")) {
      return;
    }

    var values = input.getAttribute("value").split(",");
    var max = +input.max || 100;
    var ghost = input.cloneNode();

    input.classList.add("multirange", "original");
    ghost.classList.add("multirange", "ghost");

    input.value = values[0] || max / 2;
    ghost.value = values[1] || max / 2;

    input.parentNode.insertBefore(ghost, input.nextSibling);

    Object.defineProperty(input, "originalValue", descriptor.get ? descriptor : {
      // Dang you Safari >:(
      get: function() {
        return this.value;
      },
      set: function(v) {
        this.value = v;
      }
    });

    Object.defineProperties(input, {
      valueLow: {
        get: function() {
          return Math.min(this.originalValue, ghost.value);
        },
        set: function(v) {
          this.originalValue = v;
        },
        enumerable: true
      },
      valueHigh: {
        get: function() {
          return Math.max(this.originalValue, ghost.value);
        },
        set: function(v) {
          ghost.value = v;
        },
        enumerable: true
      }
    });

    if (descriptor.get) {
      // Again, fuck you Safari
      Object.defineProperty(input, "value", {
        get: function() {
          return this.valueLow + "," + this.valueHigh;
        },
        set: function(v) {
          var values = v.split(",");
          this.valueLow = values[0];
          this.valueHigh = values[1];
        },
        enumerable: true
      });
    }

    function update() {
      ghost.style.setProperty("--low", input.valueLow * 100 / max + 1 + "%");
      ghost.style.setProperty("--high", input.valueHigh * 100 / max - 1 +...