DEV Community

Márk Munkácsi
Márk Munkácsi

Posted on • Edited on

Styled Range Input - A way out of Range Input nightmare

Did you ever faced with range inputs? They are really simple, right? You can pass min and max, perhaps step as you can see below.

<input type="range" min="100" max="200" step="10">
Enter fullscreen mode Exit fullscreen mode

But what if you need to create custom styled range input? Here comes the pain.

⬇️ tldr; if you just want the code, scroll down.

So, the range input have three parts. And if you want to implement it for yourself, probably you will use the same three parts as <div>s with a plenty of JavaScript magic, mouse event handling and calculating the value out of relative sizes and positions.

Trackbar, Progressbar, Thumb.

There are a lot of articles in the wild about styling range inputs. Maybe the most comprehensive articles about this topic are from 2017.

So we have the three parts, let's start with Thumb. It's a simple button-like draggable thingie. Unfortunately we need to use different prefixes like in old times for browser compatibility.

input[type="range"]::-webkit-slider-thumb {
  /* Styles for Chrome */
}
input[type="range"]::-moz-range-thumb {
  /* Styles for Firefox */
}
input[type="range"]::-ms-thumb {
  /* Styles for IE */
}
Enter fullscreen mode Exit fullscreen mode

The following can be the Trackbar which is the range what Thumb can slide on. Here we have another three pseudo elements for browsers.

input[type="range"]::-webkit-slider-runnable-track {
  /* Styles for Chrome */
}
input[type="range"]::-moz-range-track {
  /* Styles for Firefox */
}
input[type="range"]::-ms-track {
  /* Styles for IE */
}
Enter fullscreen mode Exit fullscreen mode

Great, but can we create a Progressbar for it? Sure, let's see following:

input[type="range"]::-moz-range-progress {
  /* Styles for Firefox */
}
input[type="range"]::-ms-fill-lower {
  /* Styles for IE */
}
/* Styles for Chrome... ¯\_(ツ)_/¯ */
Enter fullscreen mode Exit fullscreen mode

Yep, that's all folks, Chrome don't have styling for Progressbar. Although you can implement a moderately ugly workaround using CSS calc() function, which is well supported in modern browsers. Besides CSS you will need some JS magic. The sad news are, pseudo elements can't be reached from JavaScript, but you can set CSS variables with it. Let's see the magic. You need to change only WebKit related styles of Trackbar.

input[type="range"] {
  --webkitProgressPercent: 0%;
}
input[type="range"]::-webkit-slider-runnable-track {
  background-image: linear-gradient(
    90deg,
    #f2f2f2 var(--webkitProgressPercent),
    #262626 var(--webkitProgressPercent)
  );
}
Enter fullscreen mode Exit fullscreen mode

Now you only need to attach the --webkitProgressPercent variable to Thumb's position. Here you will need to listen to some mouse events to achieve the native functionality. Rather I will attach here a working example which includes JS functionality as well.

PS, I didn't tested it in IE, only in Chrome and Firefox. I created a React component for this issue as well.

Now you are out of Range Input nightmare!😁

I hope this article was helpful for you, if you have a question or suggestion, let's discuss it in comments. And don't forget to like it. 🙏

Top comments (7)

Collapse
 
taliastorymaker profile image
Talia • Edited

Thanks for the post. When I recently had to style a range input, its default appearance on Chrome and Edge was similar to the design I was going for. I wanted to keep things simple, so I decided to not implement the whole JavaScript gradient thing and instead used some non-psuedo-element-based styles to style it on Chrome and Edge (such as accent-color). For Firefox, I did use the -moz-prefixed psuedo-elements. Unfortunately, Safari's default style was far from what I wanted, so I ended up targeting Safari specifically using a technique I found on a BrowserStack post and wrote a few styles using the -webkit-prefixed psuedo-elements to get it closer to what I wanted (but I decided to be OK with it not looking exactly like the desired design). I definitely found your article and CodePen helpful with deciding what to do.

Collapse
 
jasontipton profile image
Jason Tipton • Edited

@munkacsimark awesome post!
Is there a way to modify it to support multiple range inputs on the same webpage? It looks like adding additional range sliders does not pick up the special Chrome JS/styles
dev-to-uploads.s3.amazonaws.com/up...

Collapse
 
munkacsimark profile image
Márk Munkácsi

Hi @jasontipton , thanks! 😁
The example was just for one input, there was only a document.querySelector() which returns one item. For handling multiple inputs you just need to select all of them with document.querySelectorAll() and apply handler on each of them.
I modified both examples to handle multiple items. 😊

Collapse
 
jasontipton profile image
Jason Tipton

@munkacsimark awesome solution! that makes sense, thank you!

Thread Thread
 
jasontipton profile image
Jason Tipton

@munkacsimark
found that the highlighted (--webkitProgressPercent) was not working in mobile (at least not in Chrome, I did not test the other mobile browsers). I was able to get it working though by adding to the JS.

Adding here in case anyone else needs it :)

        // Desktop
        inputElement.addEventListener("mousemove", handleMove);
        inputElement.addEventListener("mousedown", handleDown);
        inputElement.addEventListener("mouseup", handleUpAndLeave);
        inputElement.addEventListener("mouseleave", handleUpAndLeave);
        inputElement.addEventListener("click", setCSSProperty);

        // Mobile
        inputElement.addEventListener("touchmove", handleMove);
        inputElement.addEventListener("touchstart", handleDown);
        inputElement.addEventListener("touchend", handleUpAndLeave);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
adah profile image
ada-h

I literally just created an account here to say thank you for this, I had to make a slight modification on RangeInput.jsx to get the different color on the left track to change but other than that, this worked like a charm

Collapse
 
munkacsimark profile image
Márk Munkácsi

Thanks, I'm happy it helped you! 😁