DEV Community

Cover image for Create a nice-looking input range with only CSS!!

Posted on • Updated on

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

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.

text input type

  • 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.

button input type

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

checkbox input type

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

color input type

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

email input type

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

pass input type

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

number input type

  • hidden: A control that is not displayed but whose value is submitted to the server.
No image for obvious reason xD
  • range: A control for entering a number whose exact value is not important. Displays as a range widget defaulting to the middle value.

range input type

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" />
Enter fullscreen mode Exit fullscreen mode

This will render the same as the saw above:

range input type

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;
Enter fullscreen mode Exit fullscreen mode
  • 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"/>
Enter fullscreen mode Exit fullscreen mode

The markup above will render this:

input with datalist

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:

thumb & 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).

So, first open the Developer Tools (F12) in your browser, once its open, either press F1 or use the kebab menu (A.K.A 3 dots menu) and click on the "settings" option:


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

activate shadow dom

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

regular input range

We'll see the markup a bit different:

input range shadow

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%;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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

tracker box

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;
Enter fullscreen mode Exit fullscreen mode

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.

Thumb styled

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;
Enter fullscreen mode Exit fullscreen mode

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:

datalist styled

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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

input[type="range"]:focus {
  outline: none;
Enter fullscreen mode Exit fullscreen mode

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!! 💻🤓

Top comments (1)

yaireo profile image
Yair Even Or

Checkout my input-range implementation: