Shoot for the Moon

This article, part of the writing collection, was published on .

Don't be half-minded when dealing with fractions and pixels. How can we ensure all browsers interpret fractions in our CSS equally?

As CSS changes and morphs over time, we must not forget that legacy browsers remain unchanged; one gotcha that a legacy browser might snipe you with, if left unattended, is decimals.

A confusing subject for the uninitiated (myself included), decimals in CSS behave in a way that might not be straightforward to some, especially when you consider the variety of browsers and their individual behaviours.

A Bit of Prerequisite Information Permalink

It’s probably worth refreshing yourself on the various rounding methods used in CSS in various browsers. Alex Kilgour wrote an excellent article on the subject, Browser Rounding and Fractional Pixels, that’s more than worth reading through and bookmarking, if only for his concise table of rounding methods used by browsers and when they’re used. I’ve summarised these different rounding methods below, but I still recommend checking out Alex’s article.

truncate to x decimals
Strips all but the first x characters after the decimal.
Let x = 2
12.3456%12.34%
round to x decimals
Rounds the figure to x decimals.
Let x = 2
12.3456%12.35%
nearest integer
Same as round to x decimals but rounds to the nearest integer (whole number).
12.3456%12%
12.5000%13%
down
Same as nearest integer but always rounds down to the nearest integer.
12.3456%12%
12.9999%12%
sub-pixel rendering
This is the most complicated of the different methods of dealing with decimals in CSS. I will freely admit I know very little about what’s going on with sub-pixel rendering, but have drawn up a quick demo to show a little bit about how it works.

Sorry, this code snippet failed to load, but you can still check it out over on CodePen!

While the width of each box in the above demo is technically 133.3333px, sub-pixel rendering comes into play, and its behaviour might be surprising. You might expect that the width of each box would be rounded individually, creating three 133px-wide boxes, leaving one extra pixel of the full 400px-wide .parent unaccounted for.

However, what is happening, as far as I can tell, is that the browser creates a tally of the leftover 0.3333px from each of the three boxes and adds that one extra pixel of width to one of the three boxes. The exact mechanics of how this happens are a bit of a mystery to me (why does the middle box receive the extra pixel?), but the outcome makes some rhyme and reason.

But let’s not concern ourselves with the mechanics of sub-pixel rendering for now, and focus on legacy browsers that employ the less accurate methods of CSS rounding, such as down or nearest integer.

An Example Permalink

Let’s look at an example where we’re setting a percentage-based value that includes decimals. Please ignore the glaringly obvious magic number in this example!

.parent {
	width: 1337px;
}
.child {
	width: 60.029%;
}
  • .parent width is set to 1337px
  • .child width is set to 60.029%, as our target width is 803px
  • 1337px ÷ 100 × 60.029 = 802.58773px

Modern browsers will utilise sub-pixel rendering to render a pixel value containing decimals; however, older browsers, like IE8, will truncate the percentage-based value to only two decimal places! This spells trouble in our particular case:

1337px ÷ 100 × 60.02 = 802.4674px

Even the above value is rounded to the wrong target value by a modern browser.

Due to discrepancies between browsers, we can’t be sure whether a value will receive sub-pixel rendering, be truncated, be rounded to the nearest integer, or even be rounded down (floored) to the nearest integer!

As a result, more often than not, I recommend overshooting your target value with your fraction, whether it be a percentage, em, or rem fraction. The reason for overshooting is such that any browser’s method of rounding decimals will achieve your target value.

Brass Tacks Permalink

So let’s use the running example, and modify it to match these conditions and ensure that, no matter the rounding method used by the browser, the end-result pixel value is consistent.

.parent {
	width: 1337px;
}
.child {
	width: 60.06%;
}
  • .parent width is set to 1337px
  • .child width is set to 60.06%
  • 1337px ÷ 100 × 60.06 = 803.0022px

Because the worst truncation that will occur is to 2 decimal places, our value of 60.06% will satisfy each rounding method, and our target value of 803px will be achieved cross-browser.

It’s also worth noting that a percentage-based value of 60.059%, ever-so-slightly less than 60.06%, will result in a computed value of 802.9888px. This satisfies almost every method of rounding, but it still fails when rounding down. By making sure our computed value overshoots the target value, that is to say that the decimal value is slightly greater than the integer value, we satisfy the conditions to round down to our target value.

The Takeaway Permalink

When creating fractions resulting in decimals in CSS, make sure that your computed value overshoots your target value if you have to support legacy browsers.

You can also send an anonymous reply (using Quill and Comment Parade).