Create a nice-looking input range with only CSS!!
Tags: webdev, beginners, frontend, css
Published: 2020-04-29 17:52:59

---
title: Create a nice-looking input range with only CSS!!
published: true
description: Tutorial on how to style your input range type
tags: webdev, beginners, frontend, css
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/d32far0d07np22rtso85.png
---

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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#<input>_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](https://dev-to-uploads.s3.amazonaws.com/i/skvpljjodgpvhcrgqto7.png)

* **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](https://dev-to-uploads.s3.amazonaws.com/i/ca9jhisayhscohi3ybv3.png)

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

![checkbox input type](https://dev-to-uploads.s3.amazonaws.com/i/50k2xl5h7lsm7452u0pj.png)

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

![color input type](https://dev-to-uploads.s3.amazonaws.com/i/ecejrvgjtbffl9jxnq2f.png)

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

![email input type](https://dev-to-uploads.s3.amazonaws.com/i/wlgtvcng2qdsb5hra3bt.png)
 
* **password**: A single-line text field whose value is obscured. Will alert user if site is not secure.

![pass input type](https://dev-to-uploads.s3.amazonaws.com/i/mfcq7pb2ng8ejzuwcyty.png)

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

![number input type](https://dev-to-uploads.s3.amazonaws.com/i/9ddq14t0hmq0h979vt6v.png)

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

<center> *No image for obvious reason xD* </center>

* **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](https://dev-to-uploads.s3.amazonaws.com/i/6qiduilk2tm2775e6q97.png)

As we can see, there are a lot of types that we can use, you can check them in deep [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)

------

# `<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:
```html
<input type="range" />
```

This will render the same as the saw above:

![range input type](https://dev-to-uploads.s3.amazonaws.com/i/6qiduilk2tm2775e6q97.png)

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:

```js
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:

```html
<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>
  </datalist>
  <input type="range" min="1" max="10" step="1" list="custom-list"/>
</div>
```

The markup above will render this:

![input with datalist](https://dev-to-uploads.s3.amazonaws.com/i/vbkf6d80amavy0irqb8j.png)


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](https://dev-to-uploads.s3.amazonaws.com/i/cr46bblgkifzj9cils1u.png)

They're called **[Shadow DOM elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM)** 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:

![settings](https://dev-to-uploads.s3.amazonaws.com/i/1f29154tup7c173x3vus.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/5bdahmnv9uk8pxs3aisx.png)

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

![regular input range](https://dev-to-uploads.s3.amazonaws.com/i/lsqaac1me3ful00vxq4a.png)

We'll see the markup a bit different:

![input range shadow](https://dev-to-uploads.s3.amazonaws.com/i/6t5i64rd3dfzccpr915l.png)

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.

### Styling

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

```css
.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:

```css
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:

![tracker box](https://dev-to-uploads.s3.amazonaws.com/i/ii6ilft72nh0uqojyjjq.png)

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

```css
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.

![Thumb styled](https://dev-to-uploads.s3.amazonaws.com/i/6bnfqjckrauh8mg2krcn.gif)

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

```css
#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:

![datalist styled](https://dev-to-uploads.s3.amazonaws.com/i/jjenzzg219x56onxtrvz.png)

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


```css
#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`

```css
#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:

```css
.container {
  ...
  overflow: hidden;
}
```

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

```css
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.

*Before*
![Initial input range](https://dev-to-uploads.s3.amazonaws.com/i/rmh6hz5gtk0q5u6qqvz9.gif)

*After*
![Finished input range](https://dev-to-uploads.s3.amazonaws.com/i/mk13h8zobzz7xfmacg0g.gif)


You can check the full code [here.](https://codepen.io/christo-pr/pen/NWGgKNq)

Happy coding!! 💻🤓