We learn as we go, we write as we learn.

About michelada.io

By joining forces with your team, we can help with your Ruby on Rails and Javascript work. Or, if it works better for you, we can even become your team! From e-Commerce to Fintech, for years we have helped turning our clients’ great ideas into successful business.

Go to our website for more info.


Create a nice-looking input range with only CSS!!

21st October 2020

We all as a developers know the importance of the <input /> tag element, without it, we wouldn't have a way to receive any input from the user.

There are a lot of types that we can use on an input element, and each one, change the behavior of the element, here's a list of the most common types of input's:

text: The default value. A single-line text field.

button: A push button with no default behavior displaying the value of the value attribute, empty by default. If it's inside a <form> tag, it will submit the form when pressed.

checkbox: A check box allowing single values to be selected/deselected.

color: A control for specifying a color; opening a color picker when active in supporting browsers.

color picker view 

email: A field for editing an email address. Looks like a text input, but with validation parameters on supported devices.

password: A single-line text field whose value is obscured. Will alert user if site is not secure.

number: A control for entering a number. Displays a spinner and adds default validation when supported.

hidden: A control that is not displayed but whose value is submitted to the server.

No image for obvious reason

range: A control for entering a number whose exact value is not important. Displays as a range widget defaulting to the middle value.

As we can see, there are a lot of types that we can use, you can check them in deep here

<input type="range" />

In this post I'm going to show you how to modify the default style for the range input type so it looks better.

NOTE: All the code below works only on Chrome and Opera, TBH I didn't even try to fix any visual bug on any other browser.

The very basic implementation of an input range looks like this:

<input type="range" />

This will render the same as the saw above:

  • range: A control for entering a number whose exact value is not important. Displays as a range widget defaulting to the middle value.

We have available some extra properties when using this element:

  • value: String representation of a number, although it can't be an empty string (""). The default value is always halfway between min and max properties. This is how is calculated:
defaultValue = (rangeElem.max < rangeElem.min) ? rangeElem.min
               : rangeElem.min + (rangeElem.max - rangeElem.min)/2;

max & min: The maximum and minimum value permitted.

  • step: The stepping interval.
  • list: The id of the <datalist> element that contains optional pre-defined options.

Ok! enough with HTML Input 101, let's put a style on that input!.

First we need the basic html structure, something like this:

<div class="container">
  <datalist id="custom-list">
    <option value="1"></option>
    <option value="2"></option>
    <option value="3"></option>
    <option value="4"></option>
    <option value="5"></option>
    <option value="6"></option>
    <option value="7"></option>
    <option value="8"></option>
    <option value="9"></option>
    <option value="10"></option>
    <option value="11"></option>
  <input type="range" min="1" max="10" step="1" list="custom-list"/>

The markup above will render this:

It renders a hash mark for each <option> of the <datalist> element.

Thumb and tracker

On any slider control, we have 2 element that we can interact to, the thumb and the tracker, the thumb is just the little widget that let you drag and drop along the track:

They're called Shadow DOM elements and we can actually style them with CSS, so let's start by that.

Enable user agent shadow DOM

I recommend you to enable the Shadow DOM on your browser, since we can't inspect this elements by default, and sometimes it's easier to just manipulate the element directly from the developer tools instead of the common process: change, save & reload.

I'll show you how to activate it using Opera, but this works the same on Chrome, and I'm pretty sure that on FF it's just as easy as it is on Opera (is just a checkbox, shouldn't be a big issue on any browser).

This will open the settings section. Next, we only need to check the option "Show user agent shadow DOM" under the Elements list:

Now, if we inspect the input, instead of looking like a regular input element:

We'll see the markup a bit different:

The input has elements inside, which represent the tracker and the thumb, now will be easier to inspect and modify the styles for this elements.


Let's start by styling our container element and removing the default look & feel of the input

.container {
  position: relative;
  max-width: 450px;
  overflow: hidden;

input[type="range"] {
  -webkit-appearance: none;
  width: 100%;

To style the thumb and the tracker we have the pseudo-elements:

  • ::-webkit-slider-runnable-track: Selector for the tracker.
  • ::-webkit-slider-thumb: Selector for the thumb.

IE and FF also have their pseudo-elements: :-moz-range-track and ::-moz-range-thumb for FF, ::-ms-track and ::-ms-thumb for IE

Using this, we'll apply our custom styles, let's start with the tracker:

input[type="range"]::-webkit-slider-runnable-track {
  width: 100%;
  height: 1.2em;
  cursor: pointer;
  border: 1px solid #29334f;
  overflow: hidden;

We set a border around the tracker and hide all the overflow content, now should look something like this:

I know, I know! It looks really ugly, so lets fix that by applying some style to the thumb:

input[type="range"]::-webkit-slider-thumb {
   height: 12px;
   width: 45px;
   cursor: pointer;
   -webkit-appearance: none;
   border-bottom: 1px solid #29334f;
   box-shadow: 0 0 0 red, -40px 0 0 red, -85px 0 0 red, -130px 0 0 red,
      -175px 0 0 red, -220px 0 0 red, -265px 0 0 red, -310px 0 0 red,
      -350px 0 0 red, -390px 0 0 red, -409px 0 0 red;
   background: black;

First remove the default look & feel for the thumb, then we create a box shadow (10 actually) for the thumb and position them one behind the other, so now the input looks better:

Remember, we have the shadowDOM active, if something doesn't look the same, just inspect the element and change the style as you need.

Now, all we need to do is put some style in the <datalist> element:

#custom-list {
  display: flex;
  justify-content: space-between;

#custom-list option {
  position: relative;
  background: white;
  top: 18px;
  border-left: 1px solid black;
  border-right: 1px solid black;

Our datalist have a display flex and its items are align with a space between them, next, we just position all the datalist element upfront our input range (it was at the top). Finally we just set left and right borders on each option element. This is how it looks like:

Even though our input range is start taking shape, still looks a little weird, so let's fix it.

The first problem, is the first option element with 2 borders, we don't need that, so let's hide it

#custom-list option:first-of-type {
  visibility: hidden;

But that's not enough, we also need to fix the height of option so the top border of the option element will overflow a little bit the top border of the tracker.

You can base on the height we define for the thumb, in this case: 12px

#custom-list option {
  min-height: 12.5px; # just a little bit more

If we look at the end of our input range, we still se a weird border, something like "]" (is actually our input track), to fix that, let's set overflow: hidden to the .container element:

.container {
  overflow: hidden;

And last but not least, let's remove the ugly focus effect that the input have by default:

input[type="range"]:focus {
  outline: none;

And there you go!! Now you have a great looking input range, using only a few lines of CSS.


Initial input range


Finished input range

You can check the full code here.

Happy coding!! 💻🤓

View Comments