Grow your CSS skills. Land your dream job.

Value Bubbles for Range Inputs

Published by Chris Coyier

Range inputs in HTML5 are like this:

<input type="range" name="quantity" min="1" max="10">

In browsers that support them, they look like this:

Now that's great and all. You could use it for anything where you want to collect a number from a user that has an enforced minimum and maximum value.

But notice anything weird? All by itself, that range input doesn't communicate to the user what number they will actually be submitting. Now if your input is something like "How are you feeling? Left for sad, right for happy." - then fine, you probably don't need to show the user a number. But I would wager it's more common that you'll need to show the number than not show it.

To be fair, the spec says:

The input element represents a control for setting the element's value to a string representing a number, but with the caveat that the exact value is not important, letting UAs provide a simpler interface than they do for the Number state.

But c'mon, just because we want a cool slider doesn't automatically mean we should prevent the user from knowing the submitted value. I wouldn't necessarily say browsers should alter their UI control to show that number. I am saying we should build that ourselves!

This is the perfect use case for the <output> tag, which is specifically for values calculated by form elements. Here is a super simple implementation of how you might use it:

<input type="range" name="foo">
<output for="foo" onforminput="value = foo.valueAsNumber;"></output>

But let's ixnay that grody inline JavaScript, pull in our friend jQuery, get our CSS on and do this thing right. This goal is below. Any range input, any time, any min/max/step - we put a bubble above it showing the current value.

Let's style the output element first. We'll absolutely position it above the input. That gives us the ability to adjust the left value as well, once we figure out with JavaScript what it should be. We'll fancy it up with gradients and border radius, and even add a little pointer triangle with a pseudo element.

output { 
  position: absolute;
  background-image: linear-gradient(top, #444444, #999999);
  width: 40px; 
  height: 30px; 
  text-align: center; 
  color: white; 
  border-radius: 10px; 
  display: inline-block; 
  font: bold 15px/30px Georgia;
  bottom: 175%;
  left: 0;
  margin-left: -1%;
output:after { 
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  border-top: 10px solid #999999;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  margin-top: -1px;

Now what we need to do is watch all range inputs for a change in their value. Our goal is to shift the left position of the bubble in pace with the slider. That's not the simplest thing in the world, being that sliders can be of any width and any minimum or maximum value. We're going to have to do a little math. Here's all the jQuery JavaScript, commented up:

// DOM Ready
$(function() {
 var el, newPoint, newPlace, offset;
 // Select all range inputs, watch for change
 $("input[type='range']").change(function() {
   // Cache this for efficiency
   el = $(this);
   // Measure width of range input
   width = el.width();
   // Figure out placement percentage between left and right of input
   newPoint = (el.val() - el.attr("min")) / (el.attr("max") - el.attr("min"));
   // Janky value to get pointer to line up better
   offset = -1.3;
   // Prevent bubble from going beyond left or right (unsupported browsers)
   if (newPoint < 0) { newPlace = 0; }
   else if (newPoint > 1) { newPlace = width; }
   else { newPlace = width * newPoint + offset; offset -= newPoint; }
   // Move bubble
       left: newPlace,
       marginLeft: offset + "%"
 // Fake a change to position bubble at page load

The one gross part in there is that 1.3 value. I was trying to line up the tip of the bubble's triangle with the center of the slider. It's not easy, because the slider's center is never 100% left or right. That value isn't perfect, nor perfectly implemented, but it's better that not having it.

As a bonus, browsers that don't support the range input still get the bubble action.

NOTE: The above code depends on the range inputs have specified min and max values. If they don't it kinda breaks. I think it would be weird to use a range inputs without specifying these things, although if you don't it seems they default to 0 and 100. To bullet proof this, you'd grab each attribute, test it, and if it didn't look right fix it. Something like:

var minValue, maxValue;
if (!el.attr("min")) { minValue = 0; } else { minValue = el.attr("min"); }

...then use the minValue variable in the math. And do similar for max. Anyway, here's the live demo:

View Demo   Download Files

Improvements welcome.


Here's the above demo on CodePen for your playing pleasure. Another update: I know it's a little janky. It's been a while and some browser stuff must have happened. I'm going to link up some more demos below so hopefully you can find one you like.

See the Pen Range Input Value Bubbles by Chris Coyier (@chriscoyier) on CodePen

Dave Olsen ported the idea to not have a dependency on jQuery. Here's that version:

See the Pen Range Input Value Bubbles (jQuery Free) by Chris Coyier (@chriscoyier) on CodePen

And more!

See the Pen CSS Range Slider by Sean Stopnik (@thelifemgmt) on CodePen.

See the Pen Number Slider by simurai (@simurai) on CodePen.

See the Pen 3D html5 Range price slider by One div (@onediv) on CodePen.

See the Pen Tooltips for Range Inputs by Jeffrey Shaver (@jeffshaver) on CodePen.

And don't forget range input can have datalists which put little notches on them which is kinda cool.


  1. This is pretty sweet! Thanks Chris!

    How about bubbles that only appear as you slide, then fade out when you release? :P

  2. Michał Czernow
    Permalink to comment#

    This site: seems to be totally blank at least in new Opera 11.10. Looks like problem with z-index. It’s a bit irony, because Opera is the only browser listed on the site, that fully supports range input :).

    • Uh oh, that’s weird. I can’t reproduce it though. Running 11.10 here and it loads fine.

    • Michał Czernow
      Permalink to comment#

      Oh! you’re right, works perfectly. Though I checked it on Windows 7. I noticed the bug on Windows XP. Hm… I’ll check it again and if something still will be wrong, I’ll let You know.

    • Michał Czernow
      Permalink to comment#

      Ok, bug still exists, but apparently on Windows XP (SP 3). I haven’t checked it on Vista.

      I had to only turn off those declarations:

      left: -3500px;
      opacity: 0;

      for “#content, h1 section” selector and content becomes accessible.

  3. Permalink to comment#


    Great post! You can do some really cool effects with this! I’ll definitely try it myself.


  4. joe
    Permalink to comment#

    Why is it a a bonus that browsers that don’t support the range input still get the bubble action?

    you get the exact same value twice… as long as one doesn’t replace that fallback-inputfield with a slider via javascript the bubble is unnecessary

  5. Brilliant stuff. For one I love how you style the bubble. Secondly it’s relatively simple way to add a lot better usability to the slider element.

  6. Permalink to comment#

    Brilliantly written article. 3 Cheers to Chris.

  7. ion
    Permalink to comment#

    how about IE

  8. Branden
    Permalink to comment#

    On the “Note”, because jQuery’s $().attr() function returns undefined if the attribute does not exist you may do something like this to simplify and shorten the code for that.

    var minVal, maxVal;
    minVal = el.attr("min") || 0;
    maxVal = el.attr("max") || 0;

  9. Permalink to comment#

    I wish I could do this stuff natively, then I wouldn’t have to use 5000 lines of code/libraries just to do these simple sliders:

  10. huang47
    Permalink to comment#

    it’s awesome

    I was trying to create several variations by range input, however, the behavior is a bit different on iOS webkit which can not “tap and set value” like the other webkit can do

    Any insights?

  11. Nyemcsok Janos
    Permalink to comment#

    The solution is straight. Replace the range thumb with output :before/after

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".