192

enter image description here

I want the left side to be green and the right side to be gray. As pictured above would be PERFECT. Preferably a pure CSS solution (only need to worry about WebKit).

Is such a thing possible?

Shamoon
  • 41,293
  • 91
  • 306
  • 570
  • 4
    **Here you go:** https://codepen.io/vsync/pen/mdEJMLv?editors=1100 – vsync Oct 26 '20 at 16:51
  • 1
    @vsync I feel like a book could be written just explaining in full detail what every last bit of (S)CSS does. It's rather daunting to try to figure out how to change just one thing (for instance, to make the height of the color taller) – Michael Jul 07 '21 at 22:56
  • 1
    @Michael - No need to read the CSS in order to make adjustments - In the *Codepen* you have all the possible CSS variables outlined for you **in the top-right button**. Just click it and see. You can also see a list of possible variables in the [component's repository page](https://github.com/yairEO/ui-range#css-variables) – vsync Jul 08 '21 at 07:48
  • 1
    https://www.cssportal.com/style-input-range/ – Akin Zeman Feb 13 '23 at 23:40

14 Answers14

203

Pure CSS solution:

  • Chrome: Hide the overflow from input[range], and fill all the space left to thumb with shadow color.
  • IE: no need to reinvent the wheel: ::-ms-fill-lower
  • Firefox no need to reinvent the wheel: ::-moz-range-progress

/*Chrome*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
    input[type='range'] {
      overflow: hidden;
      width: 80px;
      -webkit-appearance: none;
      background-color: #9a905d;
    }
    
    input[type='range']::-webkit-slider-runnable-track {
      height: 10px;
      -webkit-appearance: none;
      color: #13bba4;
      margin-top: -1px;
    }
    
    input[type='range']::-webkit-slider-thumb {
      width: 10px;
      -webkit-appearance: none;
      height: 10px;
      cursor: ew-resize;
      background: #434343;
      box-shadow: -80px 0 0 80px #43e5f7;
    }

}
/** FF*/
input[type="range"]::-moz-range-progress {
  background-color: #43e5f7; 
}
input[type="range"]::-moz-range-track {  
  background-color: #9a905d;
}
/* IE*/
input[type="range"]::-ms-fill-lower {
  background-color: #43e5f7; 
}
input[type="range"]::-ms-fill-upper {  
  background-color: #9a905d;
}
<input type="range"/>
deathangel908
  • 8,601
  • 8
  • 47
  • 81
  • 1
    Hi deathangel908, nice solution. I would just improve the box shadow part: box-shadow: -80px 0 0px 80px #43e5f7; – Filip Witkowski Aug 29 '16 at 17:55
  • 55
    The issue with this in chrome is if you want the thumb to be larger than the track, then the box shadow overlaps the track with the height of the thumb. Is there a way to confine the box-shadow inside the track? – mharris7190 Jan 10 '17 at 17:02
  • 1
    This does't work for inputs with percentage based widths. Anyone has a workaround for that? – Remi Sture Jun 13 '17 at 14:24
  • 2
    A working CSS/JS version can be found here: https://jsfiddle.net/remisture/esyvws3d/ – Remi Sture Jun 13 '17 at 15:04
  • 2
    @mharris7190 Any updates on how to get the thumb bigger than the track in Chrome? – CodeF0x Jun 26 '19 at 08:07
  • How can I use this for circular thumb? I tried to do it, but didn't work. https://www.codepile.net/pile/j5RpEdyA – kiner_shah Sep 14 '19 at 08:11
  • 2
    @CodeF0x I figured out how to do it. In a nutshell, lay two inputs on top of each other. Top one is the thumb only (hide the track). bottom input should be overflow hidden. make the bottom thumb the same height as its track, and give it a negative box shadow colored to your liking. tie both sliders together with js, so the bottom thumb moves with & behind the top one. or use firefox. – tenwest Feb 10 '20 at 18:54
  • 12
    @tenwest Wow, that's horrible. Thanks for the update! – CodeF0x Feb 12 '20 at 08:33
  • @tenwest unfortunately this is not a pure CSS solution. – Yamil Díaz Aguirre Aug 29 '21 at 17:06
  • 1
    Why does this slider suddenly look all blocky and like it was rendered on a computer from the 80's? This doesn't match OP's image requirements. – HoldOffHunger Oct 29 '21 at 14:23
153

While the accepted answer is good in theory, it ignores the fact that the thumb then cannot be bigger than size of the track without being chopped off by the overflow: hidden. See this example of how to handle this with just a tiny bit of JS.

// .chrome styling Vanilla JS

document.getElementById("myinput").oninput = function() {
  var value = (this.value-this.min)/(this.max-this.min)*100
  this.style.background = 'linear-gradient(to right, #82CFD0 0%, #82CFD0 ' + value + '%, #fff ' + value + '%, white 100%)'
};
#myinput {
  background: linear-gradient(to right, #82CFD0 0%, #82CFD0 50%, #fff 50%, #fff 100%);
  border: solid 1px #82CFD0;
  border-radius: 8px;
  height: 7px;
  width: 356px;
  outline: none;
  transition: background 450ms ease-in;
  -webkit-appearance: none;
}
<div class="chrome">
  <input id="myinput" min="0" max="60" type="range" value="30" />
</div>
dargue3
  • 2,802
  • 2
  • 14
  • 23
  • can you suggest me when I added max=5 in HTML then color not fill properly – Husna Feb 27 '20 at 10:33
  • @Husna I've extended this answer to also handle min max attributes, hope it helps. – htmn Mar 06 '20 at 09:18
  • 1
    @Husna You have to calculate the % as a difference between your max and min value. For example, if you have min:0 and max: 10, you should use, in the JS:, `+ this.value / (10-0)*100 +'%, #fff` – Rosario Russo Apr 15 '20 at 15:27
  • 7
    @Rosario Russo You just need to repace this.value in the JS with ```(this.value-this.min)/(this.max-this.min)*100``` – Silvan Jun 02 '20 at 09:24
  • 1
    @dargue3 you should update your answer with silvans input – Toskan Dec 18 '20 at 03:59
  • Updated it to show how you'd handle `min` and `max` – dargue3 Dec 18 '20 at 22:51
  • thanks a lot! For react we can use it with combination with `useRef` for `getDocumentId` :) – Yuqiu G. Apr 08 '21 at 20:39
  • @YuqiuG. better approach would be to use useMemo in combination with the style attribute on the input element I think – ciphrd Oct 16 '21 at 22:48
  • Note that if you don't specify the max on the input, it would return Infinity and the calculus would fail. Something like `const max = (Number(element.max) || 100) !== Infinity ? (Number(element.max) || 100) : 100;` would fix this. – ssougnez Jan 04 '22 at 13:33
  • Could you possibly answer this one https://stackoverflow.com/questions/71751563/making-input-thumb-bigger-than-rest also(same task but with React) my js is weak so cant convert – walee Apr 06 '22 at 12:53
  • It's 2022 and this is the only answer I could find which works in both Firefox and Chrome with problems. The accepted answer (pure CSS) does not work - browsers must have changed. – mseifert Aug 29 '22 at 02:42
97

Use this simple css property to change color of checkbox, radio button and range

accent-color: #F55050;

Current browser support

Kostas Minaidis
  • 4,681
  • 3
  • 17
  • 25
27

Yes, it is possible. Though I wouldn't recommend it because input range is not really supported properly by all browsers because is an new element added in HTML5 and HTML5 is only a draft (and will be for long) so going as far as to styling it is perhaps not the best choice.

Also, you'll need a bit of JavaScript too. I took the liberty of using jQuery library for this, for simplicity purposes.

Here you go: http://jsfiddle.net/JnrvG/1/.

federico-t
  • 12,014
  • 19
  • 67
  • 111
23

If you use the first answer, there is a problem with the thumb. In chrome if you want the thumb to be larger than the track, then the box shadow overlaps the track with the height of the thumb.

Just to sum up all the answers I wrote a working slider with a larger slider thumb: jsfiddle

const slider = document.getElementById("myinput")
const min = slider.min
const max = slider.max
const value = slider.value

slider.style.background = `linear-gradient(to right, red 0%, red ${(value-min)/(max-min)*100}%, #DEE2E6 ${(value-min)/(max-min)*100}%, #DEE2E6 100%)`

slider.oninput = function() {
  this.style.background = `linear-gradient(to right, red 0%, red ${(this.value-this.min)/(this.max-this.min)*100}%, #DEE2E6 ${(this.value-this.min)/(this.max-this.min)*100}%, #DEE2E6 100%)`
};
#myinput {
  border-radius: 8px;
  height: 4px;
  width: 150px;
  outline: none;
  -webkit-appearance: none;
}

input[type='range']::-webkit-slider-thumb {
  width: 6px;
  -webkit-appearance: none;
  height: 12px;
  background: black;
  border-radius: 2px;
}
<div class="chrome">
  <input id="myinput" type="range" min="0" value="25" max="200" />
</div>
Ole
  • 41,793
  • 59
  • 191
  • 359
abdigali
  • 247
  • 2
  • 5
16

Building on top of @dargue3's answer, if you want the thumb to be larger than the track, you want to fully take advantage of the <input type="range" /> element and go cross browser, you need a little extra lines of JS & CSS.

On Chrome/Mozilla you can use the linear-gradient technique, but you need to adjust the ratio based on the min, max, value attributes as mentioned here by @Attila O.. You need to make sure you are not applying this on Edge, otherwise the thumb is not displayed. @Geoffrey Lalloué explains this in more detail here.

Another thing worth mentioning, is that you need to adjust the rangeEl.style.height = "20px"; on IE/Older. Simply put this is because in this case "the height is not applied to the track but rather the whole input including the thumb". fiddle

/**
 * Sniffs for Older Edge or IE,
 * more info here:
 * https://stackoverflow.com/q/31721250/3528132
 */
function isOlderEdgeOrIE() {
  return (
    window.navigator.userAgent.indexOf("MSIE ") > -1 ||
    !!navigator.userAgent.match(/Trident.*rv\:11\./) ||
    window.navigator.userAgent.indexOf("Edge") > -1
  );
}

function valueTotalRatio(value, min, max) {
  return ((value - min) / (max - min)).toFixed(2);
}

function getLinearGradientCSS(ratio, leftColor, rightColor) {
  return [
    '-webkit-gradient(',
    'linear, ',
    'left top, ',
    'right top, ',
    'color-stop(' + ratio + ', ' + leftColor + '), ',
    'color-stop(' + ratio + ', ' + rightColor + ')',
    ')'
  ].join('');
}

function updateRangeEl(rangeEl) {
  var ratio = valueTotalRatio(rangeEl.value, rangeEl.min, rangeEl.max);

  rangeEl.style.backgroundImage = getLinearGradientCSS(ratio, '#919e4b', '#c5c5c5');
}

function initRangeEl() {
  var rangeEl = document.querySelector('input[type=range]');
  var textEl = document.querySelector('input[type=text]');

  /**
   * IE/Older Edge FIX
   * On IE/Older Edge the height of the <input type="range" />
   * is the whole element as oposed to Chrome/Moz
   * where the height is applied to the track.
   *
   */
  if (isOlderEdgeOrIE()) {
    rangeEl.style.height = "20px";
    // IE 11/10 fires change instead of input
    // https://stackoverflow.com/a/50887531/3528132
    rangeEl.addEventListener("change", function(e) {
      textEl.value = e.target.value;
    });
    rangeEl.addEventListener("input", function(e) {
      textEl.value = e.target.value;
    });
  } else {
    updateRangeEl(rangeEl);
    rangeEl.addEventListener("input", function(e) {
      updateRangeEl(e.target);
      textEl.value = e.target.value;
    });
  }
}

initRangeEl();
input[type="range"] {
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 300px;
  height: 5px;
  padding: 0;
  border-radius: 2px;
  outline: none;
  cursor: pointer;
}


/*Chrome thumb*/

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  -moz-appearance: none;
  -webkit-border-radius: 5px;
  /*16x16px adjusted to be same as 14x14px on moz*/
  height: 16px;
  width: 16px;
  border-radius: 5px;
  background: #e7e7e7;
  border: 1px solid #c5c5c5;
}


/*Mozilla thumb*/

input[type="range"]::-moz-range-thumb {
  -webkit-appearance: none;
  -moz-appearance: none;
  -moz-border-radius: 5px;
  height: 14px;
  width: 14px;
  border-radius: 5px;
  background: #e7e7e7;
  border: 1px solid #c5c5c5;
}


/*IE & Edge input*/

input[type=range]::-ms-track {
  width: 300px;
  height: 6px;
  /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
  background: transparent;
  /*leave room for the larger thumb to overflow with a transparent border */
  border-color: transparent;
  border-width: 2px 0;
  /*remove default tick marks*/
  color: transparent;
}


/*IE & Edge thumb*/

input[type=range]::-ms-thumb {
  height: 14px;
  width: 14px;
  border-radius: 5px;
  background: #e7e7e7;
  border: 1px solid #c5c5c5;
}


/*IE & Edge left side*/

input[type=range]::-ms-fill-lower {
  background: #919e4b;
  border-radius: 2px;
}


/*IE & Edge right side*/

input[type=range]::-ms-fill-upper {
  background: #c5c5c5;
  border-radius: 2px;
}


/*IE disable tooltip*/

input[type=range]::-ms-tooltip {
  display: none;
}

input[type="text"] {
  border: none;
}
<input type="range" value="80" min="10" max="100" step="1" />
<input type="text" value="80" size="3" />
htmn
  • 1,505
  • 2
  • 15
  • 26
  • Could you possibly answer this one also(same task but with React) my js is weak so cant convert – walee Apr 05 '22 at 21:13
13

You can simply use the accent color (in Chrome 99)

 <input style="accent-color: #2ecc71" type="range"/>
Florian Reisinger
  • 2,638
  • 4
  • 23
  • 34
12

A small update to this one:

if you use the following it will update on the fly rather than on mouse release.

"change mousemove", function"

<script>
$('input[type="range"]').on("change mousemove", function () {
    var val = ($(this).val() - $(this).attr('min')) / ($(this).attr('max') - $(this).attr('min'));

    $(this).css('background-image',
                '-webkit-gradient(linear, left top, right top, '
                + 'color-stop(' + val + ', #2f466b), '
                + 'color-stop(' + val + ', #d3d3db)'
                + ')'
                );
});</script>
Tunaki
  • 132,869
  • 46
  • 340
  • 423
James Parker
  • 129
  • 1
  • 2
6

The previous accepted solution is not working any longer.

I ended up coding a simple function which wraps the range into a styled container adding the bar that is needed before the cursor. I wrote this example where easy to see the two colors 'blue' and 'orange' set in the css, so they can be quickly modified.

fedeghe
  • 1,243
  • 13
  • 22
6

-webkit-appearance: none; removes tick marks when using datalist. If the general appearance of the slider is fine, but the default blue color (in Chrome) needs to fit a theme color, apply a filter: hue-rotate(); to the input[type="range"] element. Other filters can be used. Some even change the background color of the slider.

input[type="range"] {
  filter: hue-rotate(180deg); //rotate degrees to get desired color
}
<input type="range" min="0" max="5" step="1" list="data" value="1" />
<datalist id="data">
<option>0</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</datalist>
OXiGEN
  • 2,041
  • 25
  • 19
1

Here is another approach if you don't mind using JS. This @steveholgado Codepen overlays 3 divs for the track, progress, and thumb over the top of an input[type=range] with an opacity of zero (transparent). An oninput listener updates the styles for the divs to create pretty much any appearance you want.

The nice thing is that it is fairly browser agnostic, and deals with the inflexibility of styling sliders on Chrome. It offers a lot more styling flexibility in general.

If you want to use something other than 0 to 100 for the slider range, you'll have to scale appropriately in the listener. For example, value = value * 100 / parseInt(range.getAttribute("max")); (assuming min=0)

https://codepen.io/steveholgado/pen/OEpGXq

HTML:

<div class="wrap">
  <input type="range" class="range" min="0" max="100" step="0.1" value="0">
  <div class="track">
    <div class="track-inner"></div>
  </div>
  <div class="thumb"></div>
</div>

CSS:

.wrap {
  width: 300px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.range {
  width: 100%;
  cursor: pointer;
  opacity: 0;
}
.range::-ms-tooltip {
  display: none;
}

.track {
  width: 100%;
  height: 4px;
  background: #DDDDDD;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
}
.track-inner {
  width: 0;
  height: 100%;
  background: #E24F4F;
}

.thumb {
  width: 16px;
  height: 16px;
  background: #AAAAAA;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 0;
  transform: translate(0%, -50%);
  pointer-events: none;
}

JS:

const range = document.querySelector('.range')
const thumb = document.querySelector('.thumb')
const track = document.querySelector('.track-inner')

const updateSlider = (value) => {
  thumb.style.left = `${value}%`
  thumb.style.transform = `translate(-${value}%, -50%)`
  track.style.width = `${value}%`
}

range.oninput = (e) =>
  updateSlider(e.target.value)

updateSlider(50) // Init value
KevinHJ
  • 1,014
  • 11
  • 24
0

It's now supported with pseudo elements in each of WebKit, Firefox and IE. But, of course, it's different in each one. : (

See this question's answers and/or search for a CodePen titled prettify <input type=range> #101 for some solutions.

Community
  • 1
  • 1
Wilson F
  • 1,250
  • 1
  • 20
  • 37
0

In 2023, you can do it 3 ways


  1. use DOM .style.accentColor property
DOM_range_element.style.accentColor = 'blue' 

  1. use jquery .css()
$('#DOM_range_element').css({'accent-color':'blue'})

or

$('#DOM_range_element').css('accent-color', 'blue')

Here is my result

enter image description here

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
hoogw
  • 4,982
  • 1
  • 37
  • 33
-7
input type="range" min="0" max="50" value="0"  style="margin-left: 6%;width: 88%;background-color: whitesmoke;"

above code changes range input style.....

milo526
  • 5,012
  • 5
  • 41
  • 60
Krish
  • 1
  • Works in FireFox 57 but not Opera 49 nor Safari 11 — worse, nothing supports foreground-color. – Devon Dec 13 '17 at 23:11