Shoot for the Moon https://repc.co/uanoe
Don’t be halfminded 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 #prerequisiteinformation
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%
 subpixel 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 subpixel rendering, but have drawn up a quick demo to show a little bit about how it works.
Check out this Pen!
While the width
of each box in the above demo is technically 133.3333px
, subpixel 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 subpixel rendering for now, and just focus on legacy browsers that employ the less accurate methods of CSS rounding, such as down
or nearest integer.
An Example #anexample
Let’s look at an example where we’re setting a percentagebased value that includes decimals. Please ignore the glaringly obvious magic number in this example!
.parent
width
is set to1337px
.child
width
is set to60.029%
, as our targetwidth
is803px
1337px ÷ 100 × 60.029 = 802.58773px
Modern browsers will utilise subpixel rendering to render a pixel value containing decimals; however, older browsers, like IE8, will truncate the percentagebased 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 subpixel 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 #brasstacks
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 endresult pixel value is consistent.
.parent
width
is set to1337px
.child
width
is set to60.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 crossbrowser.
It’s also worth noting that a percentagebased value of 60.059%
, eversoslightly 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 #tldr
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.