Styling Input Range Sliders
Tags: html, css, react, tutorial
Published: 2023-07-06 02:26:09


![Input range slider meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6vt2ekl800iitbwdbqw.jpg)

Styling [input range sliders](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range) might seem easy at first but there are a lot of caveats and it's more complicated than it seems. in this article, I will go through what is needed to have working, styled horizontal and vertical input range sliders.

## 1. The Shadow DOM
When you add the `<input type="range" />` line to your HTML, under the hood, browsers add multiple additional DOM elements to actually render it on the page. this implementation differs for each browser and is something you need to keep in mind when styling different parts of the slider.
These DOM elements are hidden by default and are called [shadow DOM elements](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) for that reason. the first step is to actually see what the structure of these elements looks like.

## 2. Revealing the Shadow DOM
To see the shadow DOM elements, you need to change some settings in your browser's DevTools.
- Chrome

>1. Open the DevTools (F12)
2. Click the `gear` icon at the top right corner
3. In the `Preferences` tab, under the `Elements` section, enable the `Show user agent shadow DOM` option

- Firefox

>1. In the address bar type `about:config`, hit Enter, and click `Accept the Risk and Continue`
2. Search for `devtools.inspector.showAllAnonymousContent` and set it to `true` 

After doing the steps above you can check your `<input type="range" />` element in the DevTools and see what is actually rendered:
- Chrome

![Input range slider shadow DOM in Chrome](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7zvp1jl1jrm4n8rrag4.png)

- Firefox

![Input range slider shadow DOM in Firefox](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwmsgfr4cza4ns5xtw2h.png)

## 3. Pseudo-Elements
Each browser allows us to target these shadow elements using different pseudo-elements/selectors:

- Chrome

>- Track: [`::-webkit-slider-runnable-track`](https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-slider-runnable-track)
- Thumb: [`::-webkit-slider-thumb`](https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-slider-thumb)

- Firefox

>- Track: [`::-moz-range-track`](https://developer.mozilla.org/en-US/docs/Web/CSS/::-moz-range-track)
- Thumb: [`::-moz-range-thumb`](https://developer.mozilla.org/en-US/docs/Web/CSS/::-moz-range-thumb)
- Progress: [`::-moz-range-progress`](https://developer.mozilla.org/en-US/docs/Web/CSS/::-moz-range-progress)

## 4. Styling

Now that we know the structure of the shadow elements and how to target them using pseudo-elements/selectors, we can start styling them. but before doing that, we need to remove the default style (appearance) applied by the browser. we can do it using the `appearance` CSS property:

```css
appearance: none;
```

We can also set the background to transparent to get rid of the default background color:

```css
background-color: transparent;
```

Now we can apply our custom styles using the pseudo-elements/selectors.
A simple style (using flashy colors for demonstration purposes) would look like this:

{% codesandbox xenodochial-khayyam-6mt6nz %}

As you can see both from the shadow DOM elements and the pseudo-elements/selectors, Chrome does not provide a `progress` element for us to style. but we can achieve a similar result by doing one of the following:

1. CSS

    We can cast a shadow from the `thumb` to the start of the `track` and clip it to give the impression of it being the same height as the track. ([example](https://codepen.io/ShadowShahriar/pen/zYPPYrQ))

2. JavaScript

    We can get the position of the `thumb` and set the background of the `track` to be a `linear-gradient` that's one color up to that position and another to the end. ([example](https://codepen.io/duplich/pen/qjYQEZ))

>ℹ️ We can also use `:hover`, `:active`, and `:disabled` selectors to further customize the appearance in different states.

## 5. Vertical Slider

>Edit: After playing around with it a bit more, I've figured out the problem. I was using `rotate : -90deg;` to rotate the horizontal slider and make a vertical one. using the [`rotate`](https://developer.mozilla.org/en-US/docs/Web/CSS/rotate) property seems to glitch the slider movement somehow. if we use [`transform: rotate(-90deg);`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate) instead, everything works fine and it won't get stuck. so just do that and ignore the next two sections ^^;

![Vertical slider meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f2jebecwgn7l4j97s5a.jpg)

If we rotate our horizontal slider to make a vertical one, it will look and work fine on desktop but on mobile/touch it won't work properly and will be stuck. to address this issue, each browser provides an extra property we can use to tell it to render a vertical slider instead of a horizontal one:

- Chrome

```css
-webkit-appearance: slider-vertical;
```

- Firefox

```html
<input type="range" orient="vertical" />
```

But there's a problem. before, we set the `appearance` to `none` to get rid of the default styles but setting these properties will override our styles and revert the slider to how it looked like before.

To work around this, what I came up with is rotating our styled slider to make a vertical but non-functional slider. then we put a working vertical slider made using the properties above, on top of it, hide it, and when its value changes, we update our original slider. so what users interact with will be the default vertical slider but what they see is our styled slider. (we only need to do this for mobile/touch)

{% codesandbox input-range-slider-forked-h4hrzg %}

>⚠️ Still on Firefox sometimes the vertical slider gets stuck on mobile/touch.

>ℹ️ We also need to add `writing-mode: bt-lr;` for older versions of Edge.

## 6. orient="vertical" in React/TypeScript
After adding `orient="vertical"` to the `input` tag in React/TypeScript, we will get this error:

`Property 'orient' does not exist on type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'`

According to React, the `orient` property does not exist on the `input` tag, so we have to add the property ourselves. we can use `module declare` to do this. we can either add the following lines to the top of the current file or place it in a `index.d.ts` file:

```typescript
declare module 'react' {
  export interface InputHTMLAttributes<T> {
    orient?: string;
  }
}
```

>ℹ️ You can use this method to add any custom property to any HTML tag you want.

---

🎉 Congratulations! Now, you have probably given up on customizing the input range slider and have decided to either use the default browser style or a component library!

Whether you're brave enough to do it or decided against it, hopefully, you've learned something from reading this article that could help you in the future. :)

---

P.S.
>- Styling for Safari, Edge, Opera, Brave, etc. would be similar to Chrome since they either use [Webkit](https://webkit.org/) or are compatible with it. 
- I've ignored Internet Explorer throughout this article but it has its own set of pseudo-elements you need to style separately.