The HTML <meter> element and its (undefined) segment boundaries

I recently found a use for the <meter> element: an element to represent a scalar measurement within a known range. (That’s the unappealing language the HTML Standard uses to describe a horizontal gauge bar.) However, I was dismayed to discover that different web browsers have vastly different interpretations of a vaguely defined aspect of the <meter> element. Here’s what I found and the workaround to get the same behavior in all browsers.

HTML Web Applications (“HTML5”) was all the rage in 2010, and browser vendors innovated left and right. One of those innovations was the HTML <meter> element. It may not have been as flashy as the new input fields or <video> element, but it has its uses.

Browsers render the <meter> element as a partially filled horizontal gauge bar. Optionally, it can have colored-coded bars representing a low, medium, and high segment. Unfortunately, the different web browsers don’t agree on where the boundaries between the segments lie.

The <meter> element has three primary attributes. The value attribute set its value, and min and max set the range (0.0 to 1.0, by default). The low and high attributes sets the boundaries between the low, medium, and high segments.

The optimum attribute specifies which end of the range is the “good” values (lower or higher values). Browsers will render the good segment in green, less good in orange, and even less good in red.

Here’s a quick example (without the required value attribute):


Browser rendering (value="3"): Your web browser doesn’t support the <meter> element. No demo, sorry!

Browser rendering (value="7"): Your web browser doesn’t support the <meter> element. No demo, sorry!

The first browser rendering (value="3") is shown as a short orange bar in Chrome 99, Safari 15, and Firefox 98 (current versions). The second rendering (value="7") is shown as a long green bar in Chrome and Safari, but Firefox shows a long orange bar.

So, why isn’t the first bar colored red and the second green in all browsers? Alternatively, why aren’t both bars orange everywhere? Both values are set on the exact segment boundary but with different results. The underlying question is where, exactly, are the boundaries for the three color segments?

The HTML Standard says about the <meter> element that The [low, high, and optimum] attributes can be used to segment the gauge’s range into ‘low’, ‘medium’, and ‘high’ parts. The definition for each attribute says that the low attribute set the upper boundary of the low part and the high sets the lower boundary of the high part.

The HTML Standard is surprisingly ambiguous on this point considering the excruciating level of details it goes to when defining the other possible attributes. It seems like the authors either forgot to specify it or assumed the correct interpretation was glaringly obvious.

That can be interpreted in at least three different ways. (At least two given the differing implementations.) Here’s a table of the current implementors’ interpretations plus what I believe to be the correct interpretation:

Low Medium High
Firefox low > value low value high high < value
Chrome/Safari low value > high high value
Attribute Inclusive low value low < value > high

Firefox’s implementation is consistent: the values of the attributes are excluded from the segments with the same name (exclusive). Like Firefox, Chrome and Safari exclude the low attribute value from the low segment. Unlike Firefox, Chrome and Safari include the high attribute value in the high segment (inclusive).

There’s an unresolved bug against the HTML Standard to clarify what’s the correct interpretation (HTML bug #3520). (No browser bugs yet as those would be pending the outcome of the standards discussion.)

I believe that the inclusive attribute interpretation is the correct one; it makes more logical sense than not that the value you specify in an attribute named after a segment should be included in that segment.

This interoperability problem probably won’t be resolved for years. In the meantime, you can use a workaround to get every web browser to behave attribute-inclusive to a high level of precision.

All the attributes are floats, so you can move the boundaries by a tiny fraction. Add 0.0000001 to the value in your low attribute, and subtract 0.0000001 from the high attribute. (A seven-digit fraction is within the precision of a 32-bit float. Use any fraction that makes sense with the data you’re visualizing.)

Web browser interoperability is at an all-time high, but that doesn’t mean web browsers are fully interoperable. Compatibility discrepancies like this one can cost hours to understand, workaround, and document. Please support my work by buying me a coffee with the link below.