Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In CSS Grid why is 1FR+9FR not behaving the same as 10FR in small screen sizes?

Tags:

So I am defining a layout in CSS Grid which needs this particular row to have identically sized inputs. I can do the math and just specify percentages and it works fine (see commented out line) but when I specify it via the fractional unit (fr) then the spanned column "squishes" more than the non-spanned columns.

.A {
  grid-area: a;
}

.B {
  grid-area: b;
}

.C {
  grid-area: c;
}

.Exchange_Row {
  display: grid;
  grid-template-columns: 1fr 9fr 10fr 10fr 1fr;
  /*
   grid-template-columns:  3% 27% 30% 30% 3%;
  */
  grid-template-areas: "a a b c ."
}

input[type=text] {
  border: solid;
}
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>

When I resize this to a narrow view (narrower than 598) then A continues to get smaller (down to 127px) but B and C stay the same at 169px.

If I comment out the grid-template-columns line using fr units and instead use the percentages below then it works fine.

Here are screenshots showing the different sizes when the columns are defined using fr units (as in the code above):

A with size 127px

B with size 169px

C with size 169px

Can anybody help me understand why this is happening? I expected A to stop at 169px or else B and C to continue to shrink down to 127px since they are all defined almost identically.

like image 762
Peter Bowers Avatar asked Jan 10 '19 12:01

Peter Bowers


1 Answers

Add min-width:0 to input and you should fix the percentage value so they are exacltly the same as the fr. You have 31fr so 1fr is about 100/31 = 3.225%

.A {
  grid-area: a;
}

.B {
  grid-area: b;
}

.C {
  grid-area: c;
}

.Exchange_Row {
  display: grid;
  grid-template-columns: 1fr 9fr 10fr 10fr 1fr;
  grid-template-areas: "a a b c ."
}
.Exchange_Row.percentage {
 grid-template-columns:  3.225% 29.03% 32.25% 32.25% 3.225%;
}

input[type=text] {
  border: solid;
  min-width:0;
}
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row percentage">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>

This is related to Automatic Minimum Size of Grid Items and how grid items are sized. An input element has a default width set by browser considered as a minimum size (it's also the same with flexbox). This size is used to define the column size of the 10fr.

If we refer to the above links:

Once the grid items have been placed, the sizes of the grid tracks (rows and columns) are calculated, accounting for the sizes of their contents and/or available space as specified in the grid definition.

Both columns (defined by 10fr) will be sized considering their content (the input element) but the first two columns (1fr and 9fr) cannot be sized using the input because this one is spaning both of them and not only one of them. To say this differently: the 1fr and 9fr columns don't have any explicit content thus they will be sized according to the available space then the input will match this space.

In other words, the first input is sized based on the 1fr and 9fr but the other input is sizing the 10fr.

By adding min-width:0 we remove the Automatic Minimum size constraint thus there is no more content size to account for and all the grid columns will get sized using the available space then the inputs will match that size.

Adding width:100% will also fix the issue but differently. In this case, we tell the input to have its width based on its containing block (the grid item) so we need to first define the size of the grid item (considering the available space) then resolve the percentage value to define the input size.

This will happen with any configuration even if you change the fr values:

.A {
  grid-area: a;
}

.B {
  grid-area: b;
}

.C {
  grid-area: c;
}

.Exchange_Row {
  display: grid;
  grid-template-columns: 5fr 5fr 1fr 1fr 1fr;
  grid-template-areas: "a a b c ."
}
.Exchange_Row.another {
 grid-template-columns:  50fr 50fr 3fr 1fr 1fr;
}

input[type=text] {
  border: solid;
}
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row another">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>

Even if we define bigger value for the first input, the other will always win because they will define the size of their own grid column and the first one will simply take the remaining.

UPDATE

Another way to explain this (as comment by @Michael_B) is that 1fr unit is equivalent to minmax(auto,1fr) which means that for the 10fr column we have the auto size of the content as a lower bound which isn't the case for the two first columns since they don't have the input as their content.

We can use minmax(0,1fr) to overcome this instead of using min-width:0

.A {
  grid-area: a;
}

.B {
  grid-area: b;
}

.C {
  grid-area: c;
}

.Exchange_Row {
  display: grid;
  grid-template-columns: minmax(0,1fr) minmax(0,9fr) minmax(0,10fr) minmax(0,10fr) minmax(0,1fr);
  grid-template-areas: "a a b c ."
}
.Exchange_Row.percentage {
 grid-template-columns:  3.225% 29.03% 32.25% 32.25% 3.225%;
}

input[type=text] {
  border: solid;
}
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>
<div style="width: 90%; border: solid; border-radius: 5px; padding: 5px;">
  <div id="currencyRow" class="Exchange_Row percenage">
    <input type="text" class="A" value="A" />
    <input type="text" class="B" value="B" />
    <input type="text" class="C" value="C" />
  </div>
</div>
like image 124
Temani Afif Avatar answered Oct 11 '22 14:10

Temani Afif