<h1>(remix colors with sliders down below)</h1>

<script src="https://gist.github.com/lonekorean/8a6a3c508b7c71deb4070d3314900b1f.js"></script>

<div id="app">
  <textarea class="filter-css" v-model="cssFilterDisplay"></textarea>
  <div class="filters">
    <filter-control
      label="Hue Rotate" filter-key="hueRotate" min="0" max="360"
      :initial-value="hueRotate" @update="updateValue">
    </filter-control>

    <filter-control
      label="Brightness" filter-key="brightness" min="0" max="200"
      :initial-value="brightness" @update="updateValue">
    </filter-control>

    <filter-control
      label="Contrast" filter-key="contrast" min="0" max="200"
      :initial-value="contrast" @update="updateValue">
    </filter-control>

    <filter-control
      label="Saturate" filter-key="saturate" min="0" max="200"
      :initial-value="saturate" @update="updateValue">
    </filter-control>
  </div>
</div>

<script type="text/x-template" id="filter-control-template">
  <div class="filter-control">
    <label :for="filterKey">{{label}}</label>
    <div class="filter-inputs">
      <input type="range" class="filter-range" :min="min" :max="max" v-model="number">
      <input type="number" class="filter-number" :id="filterKey" :min="min" :max="max" v-model="number">
      <div class="filter-unit">{{unit}}</div>
    </div>
  </div>
</script>
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Roboto:400,700');

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

body {
  margin: 20px;
  font: 18px 'Roboto', sans-serif;
}

.gist, h1, #app {
  max-width: 800px;
  margin: 0 auto;
}

h1 {
  margin-bottom: 20px;
  color: #666;
  text-align: center;
  font-size: 32px;
}

.filter-css {
  display: block;
  width: 100%;
  height: 110px;
  margin: 30px 0 0;
  padding: 10px 15px;
  border: 1px dashed #ccc;
  font: 15px/1.5 'Roboto Mono', monospace;
  resize: none;
}

.filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.filter-control {
  width: 100%;
  margin-top: 20px;

  label {
    display: inline-block;
    font-weight: bold;
    margin-bottom: 5px;
  }
}

.filter-inputs {
  display: flex;
  align-items: center;
}

.filter-range {
  flex: 1;
}

.filter-number {
  width: 60px;
  margin-left: 10px;
  text-align: right;
  font: 15px/1.5 'Roboto Mono', monospace;
}

.filter-unit {
  margin-left: 5px;
  width: 30px;
}

@media only screen and (min-width: 600px) {
  .filter-css {
    height: 90px;
  }
  
  .filter-control {
    width: calc(50% - 20px);
  }
}
View Compiled
Vue.component('filter-control', {
  template: '#filter-control-template',
  props: ['label', 'filterKey', 'initialValue', 'min', 'max'],
  
  data() {
    return {
      number: 0,
      unit: ''
    }
  },
  computed: {
    value() {
      return (this.number || 0) + this.unit;
    }
  },
  watch: {
    value() {
      this.$emit('update', this.filterKey, this.value);
    }
  },
  created() {
    // parse an inital value like "50%" to get 50 and "%"
    let parsedInitialValue = this.initialValue.toString().match(/(\d+)(\D*)/);
    if (parsedInitialValue && parsedInitialValue.length === 3) {
      this.number = +parsedInitialValue[1];
      this.unit = parsedInitialValue[2]
    }
  }
});

new Vue({
  el: '#app',
  data: {
    hueRotate: '0deg',
    brightness: '70%',
    contrast: '100%',
    saturate: '150%',
    codeLineEls: document.querySelectorAll('.blob-code')
  },
  computed: {
    cssFilterValue() {
      let filters = [];
      this.appendFilterPart(filters, 'hue-rotate', this.hueRotate, '0deg');
      this.appendFilterPart(filters, 'brightness', this.brightness, '100%');
      this.appendFilterPart(filters, 'contrast', this.contrast, '100%');
      this.appendFilterPart(filters, 'saturate', this.saturate, '100%');

      return filters.join(' ') || 'none';
    },
    cssFilterDisplay() {
      return 'body .gist .blob-code {\n' +
        '    filter: ' + this.cssFilterValue + ';\n' +
        '}';
    }
  },
  watch: {
    cssFilterValue() {
      this.setCodeLineStyles();
    }
  },
  created() {
    this.setCodeLineStyles();
  },
  methods: {
    updateValue: function(valueKey, value) {
      this[valueKey] = value;
    },
    appendFilterPart(filters, filterName, value, ignoreValue) {
      if (value !== ignoreValue) {
        filters.push(filterName + '(' + value + ')');
      }
    },
    setCodeLineStyles() {
      this.codeLineEls.forEach((el) => {
        el.style.filter = this.cssFilterValue;
      });      
    }
  }
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js