This is a blog post about a change of behavior
on CSS Grid Layout
related to percentage row tracks and gutters
in grid containers with indefinite height.
Igalia has just implemented the change
which can affect some websites out there.
So here I am going to explain several things about
how percentages work in CSS and all the issues around it,
of course I will also explain the change we are doing in Grid Layout
and how to keep your previous behavior in the new version
with very simple changes.
Sorry for the length but I have been dealing with these issues since 2015
(probably earlier but that is the date of the first commit
I found about this topic),
and I went too deep explaining the concepts.
Probably the post has some mistakes, this topic is not simple at all,
but it represents a kind of brain dump of my knowledge about it.
Percentages and definite sizes
This is the easy part, if you have an element with fixed width and height
resolving percentages on children dimensions is really simple,
they are just computed against the width or height of the containing block.
A simple example:
<div style="width: 500px; height: 200px; border: dashed thick black;">
<div style="width: 50%; height: 75%; background: magenta;"></div>
Example of percentage dimensions in a containing block with definite sizes
Things are a bit trickier for percentage margins and paddings.
In inline direction (width in horizontal writing mode)
they work as expected and are resolved against the inline size.
However in block direction (height) they are not resolved against
the block size (as one can initially expect)
but against the inline size (width) of the containing block.
Again a very simple example:
<div style="width: 500px; height: 200px; border: dashed thick black;">
<div style="margin-left: 10%; margin-top: 10%;
height: 150px; background: magenta;"></div>
Example of percentage margins in a containing block with definite sizes
Note that there is something more here,
in both Flexbox and Grid Layout specifications it was stated in the past
that percentage margins and paddings resolve against
their corresponding dimension, for example inline margins
against inline axis and block margins against block axis.
This was implemented like that in Firefox and Edge,
but Chromium and WebKit kept the usual behavior
of resolving always against inline size.
So for a while the spec had the possibility to resolve them in either way.
This was a source of interoperability issues between the different browsers
but finally the CSS Working Group (CSSWG) resolved to
keep the behavior for regular blocks also for flex and grid items.
And both Firefox and Edge modified their behavior
and all browsers have the same output nowadays.
Percentages and indefinite sizes
First question is, what is an indefinite size?
The simple answer is that a definite size is a size
that you can calculate without taking into account the contents of the element.
An indefinite size is the opposite, in order to compute it
you need to check the contents first.
But then, what happens when the containing block dimensions are indefinite?
For example, a floated element has indefinite width
(unless otherwise manually specified),
a regular block has indefinite height by default (
For heights this is very simple, percentages are directly ignored
so they have no effect on the element, they are treated as
For widths it starts to get funny.
Web rendering engines have two phases to compute the width of an element.
A first one to compute the minimum and maximum intrinsic width
(basically the minimum and maximum width of its contents),
and a second one to compute the final width for that box.
So let’s use an example to explain this properly.
Before getting into that, let me tell you that I am going to use Ahem font
in some examples, as it makes very easy to know the size of the text
and resolve the percentages accordingly,
so if we use
font: 50px/1 Ahem; we know that the size
X character is a square of 50x50 pixels.
<div style="float: left; font: 50px/1 Ahem;
border: solid thick black; background: magenta; color: cyan;">
Example of intrisic width without constraints
The browser first calculates the intrinsic width,
as minimum it computes 250px
(the size of the smallest word,
XXXXX in this case),
as maximum size it would be 400px
(the size of the whole text without line breaking
So after this phase the browser knows that the element
should have a width between 250px and 400px.
Then during layout phase the browser will decide the final size,
if there are no constraints imposed by the containing block
it will use the maximum intrinsic width (400px in this case).
But if you have a wrapper with a 300px width,
the element will have to use 300px as width.
If you have a wrapper smaller than the minimium intrinsic width,
for example 100px,
the element will still use the minimum 250px as its size.
This is a quick and dirty explanation,
but I hope it is useful to get the general idea.
Example of intrisic width with different constraints
In order to resolve percentage widths (in the indefinite width situations)
the browser does a different thing depending on the phase.
During intrinsic size computations the percentage width is ignored
auto like for the heights).
But in the layout phase the width is resolved against
the intrinsic size computed earlier.
Trying to summarize the above paragraphs,
we can say that somehow the width is only indefinite
while the browser is computing the intrinsic width of the element,
afterwards during the actual layout
the width is considered definite
and percentages are resolved against it.
So now let’s see an example of indefinite dimensions and percentages:
<div style="float: left;
border: solid thick black; background: magenta;">
<div style="width: 50%; height: 50%; background: cyan;">Hello world!</div>
Example of percentage dimensions in a containing block with indefinite sizes
First the size of the magenta box is calculated based on its contents,
as it has not any constraint it uses the maximum intrinsic width
(the length of
Then as you can see the width of the cyan box is 50% of the text length,
but the height is the same than if we use
(the default value), so the 50% height is ignored.
For margins and paddings things work more or less the same,
remember that all of them are resolved against the inline direction
(so they are ignored during intrinsic size computation
and resolved later during layout).
But there is something special about this too.
Nowadays all the browsers have the same behavior
but that was not always the case, not so long time ago
(before Firefox 61 which was released past June)
things worked different in Firefox than the rest of browsers
Again let’s go to an example:
<div style="float: left; font: 50px/1 Ahem;
border: solid thick black; background: magenta;">
<div style="margin-left: 50%; height: 100px;
background: cyan; color: blue;">XXXXX</div>
Example of percentage margins in a containing block with indefinite sizes
In this example the size of the magenta box (the floated
is the width of the text, 250px in this case.
Then the margin is 50% of that size (125px),
making that the size of the cyan box gets reduced to 125px too,
which causes overflow.
But for these cases (percentage width margins and paddings
and indefinite width container)
Firefox did something extra that was called back-compute percentages.
For that it something similar to the following formula:
Intrinsic width / (1 - Sum of percentages)
Which for this case would be
250px / (1 - 0.50) = 500px.
So it takes as intrinsic size of the magenta box 500px,
and then it resolves the 50% margin against it (250px).
Thanks to this there is no overflow,
and the margin is 50% of the containing block size.
Example of old Firefox behavior back-computing percentage margins
This Firefox behavior seems really smart and avoid overflows,
but the CSSWG discussed about it
and decided to use the other behavior.
The main reason is what happens when you are around 100% percentages,
or if you go over that value.
The size of the box starts to be quite big
(with 90% margin it would be 2500px),
and when you go to 100% or over it you cannot use that formula
so it considers the size as infinity
(basically the viewport size in this example)
and there is discontinuity in how percentages are resolved.
So after that resolution Firefox changed their implementation
and removed the back-computing percentages logic,
thus we have now interoperability in
how percentage margins and paddings are resolved.
CSS Grid Layout and percentages
And now we arrive to CSS Grid Layout and how to resolve percentages
in two places: grid tracks and grid gutters.
Of course when the grid container has definite dimensions
there are no problems in resolving percentages against them,
that is pretty simple.
As usual the problem starts with indefinite sizes.
Originally this was not a controversial topic,
percentages for tracks were behaving similar
to percentage for dimensions in regular blocks.
A percentage column was treated as
auto for intrinsic size computation
and later resolved against that size during layout.
For percentage rows they were treated as
It does not mean that this is very easy to understand
(actually it took me a while),
but once you get it, it is fine and not hard to implement.
But when percentage support was added to grid gutters
the big party started.
Firefox was the first browser implementing them
and they decided to use the back-compute technique
explained in the previous point.
Then when we add support in Chromium and WebKit
we did something different than Firefox,
we basically mimic the behavior of percentage tracks.
As browsers started to diverge different discussions appear.
One of the first agreements on the topic was that both
percentage tracks and gutters should behave the same.
That invalidated the back-computing approach,
as it was not going to work fine for percentage tracks as they have contents.
In addition it was finally discarded even for regular blocks,
as commented earlier,
so this was out of the discussion.
However the debate moved to how percentage row tracks and gutters
should be resolved, if similar to what we do for regular blocks
or if similar to what we do for columns.
The CSSWG decided they would like to keep CSS Grid Layout
as symmetric as possible, so making row percentages
resolve against the intrinsic height would achieve that goal
So finally the CSSWG resolved to modify how percentage row tracks and gutters
are resolved for grid containers with indefinite height.
The two GitHub issues with the last discussions are:
Let’s finish this point with a pair of examples to understand
the change better comparing the previous and new behavior.
<div style="display: inline-grid; border: solid thick;
grid-template-columns: 75%; grid-template-rows: 50%;">
<div style="background: magenta;">Testing</div>
Example of percentage tracks in a grid container with indefinite sizes
Here the intrinsic size of the grid container
is the width and height of the text
and then the percentages tracks are resolved against that size
for both columns and rows (before that was only done for columns).
<div style="display: inline-grid; grid-gap: 10%; border: solid thick;
grid-template-columns: 200px 200px; grid-template-rows: 100px 100px;">
<div style="background: magenta;"></div>
<div style="background: cyan;"></div>
<div style="background: yellow;"></div>
<div style="background: lime;"></div>
Example of percentage gutters in a grid container with indefinite sizes
In this example we can see the same thing, with the new behavior
both the percentage column and row gaps are resolved
against the intrinsic size.
Change behavior for indefinite height grid containers
For a while all browsers were behaving the same
(after Firefox dropped the back-computing approach)
so changing this behavior would imply some kind of risks,
as some websites might be affected by that and get broken.
For that reason we added a use counter
to track how many websites where hitting this situation,
using percentage row tracks in a indefinite height grid container.
The number is not very high, but there is an increasing trend as
Grid Layout is being adopted
(almost 1% of websites are using it today).
And then Firefox changed the behavior for percentage row gutters
to follow the new text on the spec,
so they are resolved against the intrinsic height
(this happened in version 62).
However it did not change the behavior for percentage row tracks yet.
This was a trigger to retake the topic and go deeper on it,
after analyzing it carefully and crafting a prototype implementation
we sent an intent to implement and ship to
blink-dev mailing list.
The intent was approved, but we were requested to analyze the sites
that were hitting the use counter.
After checking 178 websites only 8 got broken due to this change,
we contacted them to try to get them fixed
explaining how to keep the previous behavior (more about this in next point).
You can find more details about this research in this mail.
Apart from that we added a deprecation message in Chromium 69,
so if you have a website that is affected by this
(it does not mean that it has to get broken but
that it uses percentage row tracks in a grid container with indefinite height)
[Deprecation] Percentages row tracks and gutters
for indefinite height grid containers
will be resolved against the intrinsic height
instead of being treated as auto and zero respectively.
This change will happen in M70, around October 2018.
for more details.
Finally this week the patch has been accepted and merged in master,
so since Chromium 70.0.3516 (current Canary) you will have the new behavior.
Apart from that we also make the fix in WebKit
that will be hopefully part of the next Safari releases.
In addition Firefox and Edge developers have been notified
and we have shared the tests in WPT
so hopefully those implementations will get updated soon too.
Update your website
Yes this change might affect your website or not,
even if you get the deprecation warning it can be the case
that your website is still working perfectly fine,
but in some cases it can break quite badly.
The good news is that the solution is really straightforward.
If you find issues in your website and you want to keep the old behavior
you just need to do the following for grid containers with indefinite height:
- Change percentages in
- Modify percentages in
With those changes your website will keep behaving like before.
In most cases you will realize that the percentages were unneeded
and were not doing anything useful for you,
even you would be able to drop the declaration completely.
One of these cases would be websites that have grid containers
with just one single row of 100% height (
many of the sites hitting the use counter are like this.
All these are not affected by this change,
unless the have extra implicit rows,
but the 100% is not really useful at all there,
they can simply remove the declaration.
Another sites that have issues are the ones that have for example two rows
that sum up 100% in total (
grid-template-rows: 25% 75%).
These percentages were ignored before,
so the contents always fit in each of the rows.
Now the contents might not fit in each row and the results
might not be the desired ones.
<div style="display: grid; grid-template-rows: 25% 75%; border: solid thick;">
<div style="background: magenta;">First<br>two lines</div>
<div style="background: cyan;">Second</div>
Example of overlapping rows in the new behavior
The sites that were more broken usually have several rows
and used percentages only for a few of them or for all.
And now the rows overflow the height of the grid container
and they overlap other content on the website.
There were cases like this example:
<div style="display: grid; grid-template-rows: 50%; border: solid thick;">
<div style="background: magenta;">First</div>
<div style="background: cyan; height: 200px;">Second</div>
<div style="background: yellow; height: 100px;">Third</div>
Example of overflowing rows in the new behavior
This topic has been a kind of neverending story for the CSSWG,
but finally it seems we are reaching to an end.
Let’s hope this does not get any further
and things get settle down after all this time.
We hope that this change is the best solution for web authors
and everyone will be happy with the final outcome.
As usual I could not forget to highlight that all this work
has been done by Igalia
thanks to Bloomberg sponsorship
as part of our ongoing collaboration.
Igalia and Bloomberg
working together to build a better web
Thanks for reading that long, this ended up being
much more verbose and covering more topics than originally planned.
But I hope it can be useful to understand the whole thing.
You can find all the examples from this blog post in this pen
feel free to play with them.
And to finish this blog post I could only do it by quoting fantasai:
this is why I hate percentages in CSS
I cannot agree more with her. 😇