14

.small-container { width: 200px; }
.small-container > div { margin: 16px; }
.border { box-shadow: 0 0 2px 2px black; }

.break-long-text {
    width: 100%;
    word-wrap: break-word;
    overflow-wrap: break-word;
}

.grid-fr {
    display: grid;
    grid-template-columns: 1fr;
}

.grid-minmax {
    display: grid;
    grid-template-columns: minmax(120px, 1fr);
}
<div class="small-container">
  <div class="grid-fr">
    <div class="break-long-text border">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
  <div class="grid-minmax">
    <div class="break-long-text border">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
</div>

The two grids are completely the same, the only difference is the grid-template-columns value. However, one grid respects the width of its parent (200px) and the other ignores the width.

  1. grid: grid-template-columns: 1fr; => ignores parent-width and continues until the content is filled

  2. grid: grid-template-columns: minmax(120px, 1fr); => Only goes up to parent-width (200px) and then becomes as high as necessary. However, it is still larger than the defined min-width (120px), which is of course correct since 1fr is larger than 120px in this example.

Since in the end both should have the same width of 1fr, I wonder about different results. Is this a bug or expected behavior? And why does the second grid work the way I expect it to and not the first?

So should I always use minmax if I don't want one cell to be larger than its parent?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Putzi San
  • 5,566
  • 3
  • 19
  • 35
  • 1
    For more good answers to the problem described here, you should also check [How come minmax(0, 1fr) works for long elements while 1fr doesn't?](https://stackoverflow.com/q/52861086/5627010) and [Prevent content from expanding grid items](https://stackoverflow.com/q/43311943/5627010) – Putzi San Nov 22 '19 at 12:03

3 Answers3

10

Here's the solution to the second grid.

.grid-minmax {
   display: grid;
   grid-template-columns: minmax(120px, 1fr);
 }

This is how the browsers calculate the track size:

(200px container) - (120px nonflexible track size) + (1fr leftover space) = 200px

Using the guidance provided in the specification (see below), the behavior above is relatively simple and straightforward.

But why does the content in the first container overflow?

That's a tricky scenario and, at this point, I can see two potential paths to the answer.

  1. There is an issue / problem / bug in the workings between:

    .break-long-text {
        word-wrap: break-word;
        overflow-wrap: break-word;
     }
    

    and

    grid-template-columns: 1fr;
    

    I say this because when the string is broken up, the problem in the question doesn't exist. In other words, both grid containers behave the same (demo).

OR

  1. I'm not finding the answer in the specification. I've reviewed the following sections.


Here's how the fr unit works, in detail:

From the spec:

§ 7.2.3. Flexible Lengths: the fr unit

A flexible length or <flex> is a dimension with the fr unit, which represents a fraction of the leftover space in the grid container.

Tracks sized with fr units are called flexible tracks as they flex in response to leftover space similar to how flex items fill space in a flex container.

Okay. So far, so good. The fr unit in grid layout functions similarly to flex-grow in flex layout.

Moving on with the spec...

The distribution of leftover space occurs after all non-flexible track sizing functions have reached their maximum.

The total size of such rows or columns is subtracted from the available space, yielding the leftover space, which is then divided among the flex-sized rows and columns in proportion to their flex factor.

(emphasis mine)

In other words all non-flexible track sizes (such as those based on pixels or ems) are first subtracted from the available space (200px, in this case). Whatever space remains is considered leftover space, which then gets targeted by the fr unit.

Also, what exactly is a "track sizing function"?

Each track sizing function can be specified as a length, a percentage of the grid container’s size, a measurement of the contents occupying the column or row, or a fraction of the free space in the grid.

It can also be specified as a range using the minmax() notation, which can combine any of the previously mentioned mechanisms to specify separate min and max track sizing functions for the column or row.

https://www.w3.org/TR/css3-grid-layout/#grid-template-rows-track-sizing-function

This explains the workings for the second grid container (with minmax()) but not the first (with 1fr).

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    I was also convinced it may be a bug .. was waiting for you to confirm it's not a logical behavior :p – Temani Afif Jul 09 '18 at 21:09
  • I'm not convinced it's a bug because the behavior is consistent across all major browsers that support Grid. But because the problem is resolved when the string is broken up, it leaves that possibility open. @TemaniAfif – Michael Benjamin Jul 09 '18 at 21:11
  • Hi, thanks for your answer! But I don't think it has anything to do with `overflow-wrap: break-word;` when I remove the corresponding class the behavior remains the same ([updated fiddle](https://jsfiddle.net/94jdok7m/) - you can see it on the `box-shadow`) – Putzi San Jul 09 '18 at 21:50
  • Yeah, as I mentioned in my comment above, the bug option is unlikely, but still within the realm of possibility, in my view. I would give more weight to my second option. – Michael Benjamin Jul 09 '18 at 21:55
  • Another reason to think that this goes to the grid sizing algorithm is that both `1fr` and `auto` render the same layout. Again, I either didn't see or didn't understand the spec language that pertains to this behavior. – Michael Benjamin Jul 10 '18 at 00:03
  • I think it has to do with [this question and answer](https://stackoverflow.com/questions/43311943/prevent-content-from-expanding-grid-items): if I add `min-width 0` to one item then it stops growing like with `minmax`. It seems like `minmax([MIN], [MAX])` overwrites the default `min-width: auto` to `min-width: [MIN]` => [updated fiddle](https://jsfiddle.net/mgtf9w4s/) – Putzi San Jul 11 '18 at 09:47
5

Solution:

Setting min-width:0 of .break-long-text container will do the trick.

.small-container { width: 200px; }
.small-container > div { margin: 16px; }
.border { box-shadow: 0 0 2px 2px black; }

.min-width-0{
    min-width: 0;
}
.break-long-text {
    width: 100%;
    word-wrap: break-word;
    overflow-wrap: break-word;
}

.grid-fr {
    display: grid;
    grid-template-columns: 1fr;
}

.grid-minmax {
    display: grid;
    grid-template-columns: minmax(120px, 1fr);
}
<div class="small-container">
  <div class="grid-fr">
    <div class="break-long-text border min-width-0">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
  <div class="grid-fr">
    <div class="break-long-text border">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
  <div class="grid-minmax">
    <div class="break-long-text border">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
</div>

Explanation:

From css-tricks:

The minimum width of a grid column is auto. (The same is true for flex items, by the way.)

And since auto is entirely based on content, we can say it is “indefinitely” sized, its dimensions flex. If we were to put an explicit width on the column, like 50% or 400px, then we would say it is “definitely” sized.

To apply our fix, we need to make sure that there is the column has a definite minimum width instead of auto.

ZuzEL
  • 12,768
  • 8
  • 47
  • 68
2

So should I always use minmax if I don't want one cell to be larger than its parent?

It seems pretty clear to me that the grid fr units are not playing well with the word-wrap , overflow-wrap and hyphens properties.

That being the case, I would recommend using the minmax() function as a workaround when you have reason to believe that the inner text will have long words.

Note: Since we are attempting to simulate the results of fr units - it would make sense to use 0 as the minimum length in the minmax() function. eg: grid-template-columns: minmax(0, 1fr);

The demo below compares fr units and minmax() with long text - the results seem to look the same!

.medium-container { 
  width: 50vw;
  border: 1px solid green;
  padding: 20px; 
}
.border { box-shadow: 0 0 2px 2px black; }

.break-long-text {
    width: 100%;
    word-wrap: break-word;
    overflow-wrap: break-word;
}


.grid-fr {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-gap: 20px;
}

.grid-minmax {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-gap: 20px;
}

.break-long {
    width: 100%;
    word-wrap: break-word;
    overflow-wrap: break-word;
}
<h2>Using 'fr' units:</h2>
<div class="medium-container">
  <div class="grid-fr">
    <div class="break-long-text border"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled
      it to make a type specimen book. 
    </div>
    <div class="break-long-text border"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,  when an unknown printer took a galley of type and scrambled
      it to make a type specimen book. 
    </div>
    <div class="break-long-text border"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled
      it to make a type specimen book. 
    </div>
      </div>
</div>
<hr>
<h2>Using 'minmax' workaround (contains long text):</h2>
<div class="medium-container">
  <div class="grid-minmax">
    <div class="break-long-text border"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG when an unknown printer took a galley of type and scrambled
      it to make a type specimen book. 
    </div>
    <div class="break-long-text border"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG when an unknown printer took a galley of type and scrambled
      it to make a type specimen book. 
    </div>
    <div class="break-long-text border"> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG when an unknown printer took a galley of type and scrambled
      it to make a type specimen book. 
    </div>
      </div>
</div>

Codepen demo (Resize to see effect)


Another workaround: word-break: break-word;

'fr' units do play well with the word-break property.

.break-long-text {
    word-break: break-word; /* word-break workaround */
}

In particular, blink-based browsers are ok with the break-word value, while firefox requires the break-all value.

.small-container { width: 200px; }
.small-container > div { margin: 16px; }
.border { box-shadow: 0 0 2px 2px black; }

.break-long-text {
    word-break: break-word;
}
/* firefox only CSS */
@-moz-document url-prefix() {
  .break-long-text {
    word-break: break-all;
  }
}

.grid-fr {
    display: grid;
    grid-template-columns: 1fr;
}

.grid-minmax {
    display: grid;
    grid-template-columns: minmax(120px, 1fr);
}
<div class="small-container">
  <div class="grid-fr">
    <div class="break-long-text border">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
  <div class="grid-minmax">
    <div class="break-long-text border">
      LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG
    </div>
  </div>
</div>

The problem with this workaround is that browsers which require the break-all value - such as firefox - break works in the middle - usually not a desired result.

.small-container { width: 200px; }
.small-container > div { margin: 16px; }
.border { box-shadow: 0 0 2px 2px black; }

.break-long-text {
    word-break: break-all;
}

.grid-fr {
    display: grid;
    grid-template-columns: 1fr;
}

.grid-minmax {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
}
<div class="small-container">
  <div class="grid-fr">
    <div class="break-long-text border">
      Lorem Ipsum is simply dummy text of the printing and typesetting industry. LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG when an unknown printer took a galley of type and scrambled
    </div>
  </div>
  <div class="grid-minmax">
    <div class="break-long-text border">
      Lorem Ipsum is simply dummy text of the printing and typesetting industry. LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG when an unknown printer took a galley of type and scrambled
    </div>
  </div>
</div>
Danield
  • 121,619
  • 37
  • 226
  • 255