Styling Cross-Browser Compatible Range Inputs with CSS

Avatar of Daniel Stern
Daniel Stern on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

Styling for range inputs has improved dramatically since the release of IE 10. It is now possible to generate cross-browser compatible range inputs (sliders) using only CSS. In this tutorial, we will take a basic range input (<input type="range">):

Screenshot of a range input, Mac Chrome 38

And turn it into this:

Range input with completely custom styles.

In order to simplify the process of generating cross-compatible styles, LESS style sheets have been included. CSS is also available.

Applying base CSS styles

Several styles need to be applied to range inputs in all browsers to override their basic appearance.

input[type=range] {
  -webkit-appearance: none; /* Hides the slider so that custom slider can be made */
  width: 100%; /* Specific width is required for Firefox. */
  background: transparent; /* Otherwise white in Chrome */
}

input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
}

input[type=range]:focus {
  outline: none; /* Removes the blue border. You should probably do some kind of focus styling for accessibility reasons though. */
}

input[type=range]::-ms-track {
  width: 100%;
  cursor: pointer;

  /* Hides the slider so custom styles can be added */
  background: transparent; 
  border-color: transparent;
  color: transparent;
}

This gives us invisible or unstyled range inputs in all browsers. Now we can apply our custom styles.

Styling the Thumb

The widget that you click or drag along the track is called the thumb. It can be styled just like a regular HTML element.

/* Special styling for WebKit/Blink */
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  border: 1px solid #000000;
  height: 36px;
  width: 16px;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
  margin-top: -14px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d; /* Add cool effects to your sliders! */
}

/* All the same stuff for Firefox */
input[type=range]::-moz-range-thumb {
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  border: 1px solid #000000;
  height: 36px;
  width: 16px;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
}

/* All the same stuff for IE */
input[type=range]::-ms-thumb {
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  border: 1px solid #000000;
  height: 36px;
  width: 16px;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
}

Note that while we’re repeating code here, that’s necessary as you can’t comma-separate these type of selectors. Browsers will drop the entire selector if it doesn’t understand a part of it.

This gives us the following:

A styled input on an invisible track (WebKit/Blink) or an unstyled track (Firefox and IE)

Styling the Track

The line that the thumb moves across is called the track. It is also styled just like a regular HTML element.

A note on IE: Internet Explorer 10+ has a slightly different approach to range inputs. In IE, you are allowed to specify completely different styles for the upper (right of the thumb) and lower (left of the thumb) areas of the track.

Another thing to note is that you can apply focus effects to the track which change the styling when the user is interacting with the range.

input[type=range]::-webkit-slider-runnable-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  background: #3071a9;
  border-radius: 1.3px;
  border: 0.2px solid #010101;
}

input[type=range]:focus::-webkit-slider-runnable-track {
  background: #367ebd;
}

input[type=range]::-moz-range-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  background: #3071a9;
  border-radius: 1.3px;
  border: 0.2px solid #010101;
}

input[type=range]::-ms-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  background: transparent;
  border-color: transparent;
  border-width: 16px 0;
  color: transparent;
}
input[type=range]::-ms-fill-lower {
  background: #2a6495;
  border: 0.2px solid #010101;
  border-radius: 2.6px;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
input[type=range]:focus::-ms-fill-lower {
  background: #3071a9;
}
input[type=range]::-ms-fill-upper {
  background: #3071a9;
  border: 0.2px solid #010101;
  border-radius: 2.6px;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
input[type=range]:focus::-ms-fill-upper {
  background: #367ebd;
}

The code above gives us:

a track with no thumb (Chrome) or an unstyled thumb (Firefox and IE)

Building a Complete Range Input

Now that we’ve built a thumb and a track, we can combine the CSS together and make the completed range input.

Full CSS to Style Range Inputs in All Browsers

The full CSS for the styled range inputs in all browsers is below.

input[type=range] {
  -webkit-appearance: none;
  margin: 18px 0;
  width: 100%;
}
input[type=range]:focus {
  outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  background: #3071a9;
  border-radius: 1.3px;
  border: 0.2px solid #010101;
}
input[type=range]::-webkit-slider-thumb {
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  border: 1px solid #000000;
  height: 36px;
  width: 16px;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
  -webkit-appearance: none;
  margin-top: -14px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
  background: #367ebd;
}
input[type=range]::-moz-range-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  background: #3071a9;
  border-radius: 1.3px;
  border: 0.2px solid #010101;
}
input[type=range]::-moz-range-thumb {
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  border: 1px solid #000000;
  height: 36px;
  width: 16px;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
}
input[type=range]::-ms-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  background: transparent;
  border-color: transparent;
  border-width: 16px 0;
  color: transparent;
}
input[type=range]::-ms-fill-lower {
  background: #2a6495;
  border: 0.2px solid #010101;
  border-radius: 2.6px;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
input[type=range]::-ms-fill-upper {
  background: #3071a9;
  border: 0.2px solid #010101;
  border-radius: 2.6px;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
input[type=range]::-ms-thumb {
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
  border: 1px solid #000000;
  height: 36px;
  width: 16px;
  border-radius: 3px;
  background: #ffffff;
  cursor: pointer;
}
input[type=range]:focus::-ms-fill-lower {
  background: #3071a9;
}
input[type=range]:focus::-ms-fill-upper {
  background: #367ebd;
}

Completed Range Input

These styles make the following input:

Range input with completely custom styles.

Bonus: Full Less to Style Range Inputs in All Browsers

There is a lot of CSS required to make identical range inputs for each browser. Using a preprocessor, you can achieve much more efficient results. Included below is the .less file used to generate the CSS above.

@track-color: #424242;
@thumb-color: #555bc8;

@thumb-radius: 8px;
@thumb-height: 30px;
@thumb-width: 30px;
@thumb-shadow-size: 1px;
@thumb-shadow-blur: 1px;
@thumb-shadow-color: #111;
@thumb-border-width: 1px;
@thumb-border-color: white;

@track-width: 100%;
@track-height: 10px;
@track-shadow-size: 2px;
@track-shadow-blur: 2px;
@track-shadow-color: #222;
@track-border-width: 1px;
@track-border-color: black;

@track-radius: 5px;
@contrast: 5%;

.shadow(@shadow-size,@shadow-blur,@shadow-color) {
  box-shadow: @shadow-size @shadow-size @shadow-blur @shadow-color, 0px 0px @shadow-size lighten(@shadow-color,5%);
}

.track() {
  width: @track-width;
  height: @track-height;
  cursor: pointer;
}

.thumb() {
  .shadow(@thumb-shadow-size,@thumb-shadow-blur,@thumb-shadow-color);
  border: @thumb-border-width solid @thumb-border-color;
  height: @thumb-height;
  width: @thumb-width;
  border-radius: @thumb-radius;
  background: @thumb-color;
  cursor: pointer;
}

input[type=range] {
  -webkit-appearance: none;
  margin: @thumb-height/2 0;
  width: @track-width;

  &:focus {
    outline: none;
  }

  &::-webkit-slider-runnable-track {
    .track();
    .shadow(@track-shadow-size,@track-shadow-blur,@track-shadow-color);
    background: @track-color;
    border-radius: @track-radius;
    border: @track-border-width solid @track-border-color;
  }
  
  &::-webkit-slider-thumb {
    .thumb();
    -webkit-appearance: none;
    margin-top: ((-@track-border-width * 2 + @track-height) / 2) - (@thumb-height / 2);
  }

  &:focus::-webkit-slider-runnable-track {
    background: lighten(@track-color, @contrast);
  }

  &::-moz-range-track {
    .track();
    .shadow(@track-shadow-size,@track-shadow-blur,@track-shadow-color);
    background: @track-color;
    border-radius: @track-radius;
     border: @track-border-width solid @track-border-color;
  }
  &::-moz-range-thumb {
     .thumb();
  }

  &::-ms-track {
    .track(); 
    background: transparent;
    border-color: transparent;
    border-width: @thumb-width 0;
    color: transparent;
  }

  &::-ms-fill-lower {
    background: darken(@track-color, @contrast);
    border: @track-border-width solid @track-border-color;
    border-radius: @track-radius*2;
    .shadow(@track-shadow-size,@track-shadow-blur,@track-shadow-color);
  }
  &::-ms-fill-upper {
    background: @track-color;
    border: @track-border-width solid @track-border-color;
    border-radius: @track-radius*2;
    .shadow(@track-shadow-size,@track-shadow-blur,@track-shadow-color);
  }
  &::-ms-thumb {
    .thumb();
  }
  &:focus::-ms-fill-lower {
    background: @track-color;
  }
  &:focus::-ms-fill-upper {
    background: lighten(@track-color, @contrast);
  }
}

Bonus 2: Sass Version!

Darlan Rod created a Sass version as well.

Browser Support

The range input itself has browser support about like: Firefox 23+, Safari 4+, iOS 5+, Chrome 6+, Opera 11+, IE 10+, Android 4.2+. So pretty good. This custom styling stuff should largely match that if you follow the code in this article.

Here’s a screenshot from a demo in lots of current versions of browsers:

If a browser doesn’t support range inputs, you’ll just get a text input, which is still functional and valid.

Useful Tool

As cross-browser range input styles only became viable in 2014, there are not many tools yet to generate modern styles. range.css is a useful tool for generating CSS styles for range inputs that I created.

More Examples

You should definitely check out Ana Tudor’s collection of highly stylized range inputs.