[csswg-drafts] [css-grid-2] Grid Track Explicit Sizing Controls (aka Flex Sizing in Grid) (#4147)

mtom55 has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-grid-2] Grid Track Explicit Sizing Controls (aka Flex Sizing in Grid) ==
### Problem
CSS Grid is an excellent, innovative and well written solution for describing 2 dimensional layouts, however there are situations where nested flexbox layouts provide more control and power to the developer, and there are situations that are impossible without having to resort to a combination of flexbox and javascript.

**Grid Track Sizing**
Currently grid track sizing provides us options to define how space is distributed, but does 
not let us control how the space is allocated in the same way flexbox allows us to do it.

Currently we have:
* min-content - minimum space need for the content (squeezing it down ot the longest inline element)
* max-content - squeeze it down to the smallest to fit the content without wrapping
* 1em / px / % - Essentially calculated width values
* {x}fr - the available remaining space divided by the sum of x over all the "fr"s
* minmax() - uses combos of the above
* auto (minimum of min-width or max-content (kinda))

Flexbox however provides:
flex: 1 1 200px;
flex: (grow) (shrink) (basis);

Where:
1. if there is "additional" space we can use the grow factor to specify how the additional space is distributed
2. if there is not enough space and we need to shrink elements we can specify how the elements are shrunk
3. We can set a basis that defines when we consider the element is shrinking or enlarged... and also that flex-basis can be set to min-content, max-content etc

### Real World Problem
If you are building highly responsive layouts, it is important to control how particular items collapse and the **order** in which they collapse.  For example when you are controlling white space between elements, some white space is more important than others.  Having non-wrapped text is more important than having an extra 20px of white space between two elements.

Take the following example:
![image](https://user-images.githubusercontent.com/11502425/61765256-e0743c80-ae0e-11e9-9ecf-697ad92ba778.png)

We have a CSS Grid with two products, with a spacer between the title and between the two buttons.

If you open the example here: https://codepen.io/anon/pen/GVoZBg 

Change the codepen setting to display the code panels on the left:
![image](https://user-images.githubusercontent.com/11502425/61765776-00a4fb00-ae11-11e9-860b-c3992ffd9b06.png)

Then gradually collapse the viewable area by using panel handle you will notice that out of the elements that are not at their min-content size, it will collapse the largest first, or where there are two or more elements of equal size it will collapse them evenly (in px).

Our desired behaviour for this particular page would be:
1. Collapse the blue spacer first down to a minimum of 5px
then
2. Collapse the red spacer down to a minimum of 3px
then
3. Collapse the buttons from 120px to max-content
then
4. Collapse the title from 110px to min-content
then
5. Overflow

**Note:**  In this example we have shrunk the available space and the product title has not yet collapsed, the blue spacer has started to shrink but the red spacer is it's original size:
 
![image](https://user-images.githubusercontent.com/11502425/61765351-4d87d200-ae0f-11e9-822e-4dc9055a5f1c.png)

(flexbox solution)


### Possible Solution 

Essentially a priority order would solve this issue.  

However we think a more powerful solution and useful and intuitive solution would be to copy
the flex spec and introduce flex-shrink, flex-grow and flex-basis properties as an option for the
column track sizing.

For each grid track we would need 5 properties:
* min-width (or height)
* max-width (or height)
* flex-grow
* flex-shrink
* flex-basis

flex(min-width, max-width, flex-grow, flex-shrink, flex-basis)

where you can set them to auto if you don't need that particular value.

```css
grid-template-columns: 
    flex(min-content, max-content, 100000, 0, 0px) /* Product Title */
    flex(5px, 100px, 1, 0, 0px)                    /* Spacer between Product Title and Buttons */
    flex(max-content, 120px, 10000, 0, 0px)        /* Button 1 */
    flex(3px, 20px, 100, 0, 0px)                   /* Spacer between buttons */
    flex(max-content, 120px, 10000, 0, 0px)        /* Button 2 */
;
```

This is the simplest format that we could come up with that would solve all of many use-cases we could imagine.

This is particularly good as then grid would be strictly better than flex for every 2d layout use-case.

### Working Flex Example
See: https://codepen.io/anon/pen/GVoZBg Flexbox Example (single row)

Please see this implemented in flexbox under the heading Flexbox Example (single row).  

This example works perfectly but only for a single row or for examples where you do not rely on content sizing.

### Multiple Row Issue
See:  https://codepen.io/anon/pen/GVoZBg Flexbox Issue (Multiple Rows)

![image](https://user-images.githubusercontent.com/11502425/61765411-9475c780-ae0f-11e9-9242-282b1b395c98.png)

This illustrates the issue of using flexbox, is that we can not base the width of the first column on the longest max-content... we could do min-width: 30px; max-width: 200px; but then you would need to manually set this based on the content

### Code for Example
```html
<!doctype html>
<html>
  <head>
    <title>[css-grid-2] Grid Track Explicit Sizing Controls (aka Flex Sizing in Grid)</title>
    <style>
      /* Grid Example */
      .grid {
          display: inline-grid;
          border: 3px solid orange;
          padding: 10px;
          grid-template-columns: 
              minmax(min-content, max-content)  /* Product Title */
              minmax(5px, 100px)                /* Spacer between Product Title and Buttons */
              minmax(max-content, 120px)        /* Button 1 */
              minmax(3px, 20px)                 /* spacer between buttons */
              minmax(max-content, 120px)        /* Button 2 */
          ;
        
          grid-template-rows: max-content max-content;
          grid-row-gap: 10px;
      }

      .product-title:not(.output) { border: 1px solid pink}
      .separator-blue:not(.output) {  border: 1px solid blue;}
      .separator-red:not(.output)  {  border: 1px solid red;}

      .button:not(.output) {
        padding-left: 3px;
        padding-right: 3px;
        align-self: center;
        border: 1px solid hsla(217, 21%, 12%, 0.3);
        color: #2c3e50;
        border-radius: 12px;
        text-align: center;
        font-size: 15px;
        line-height: 50px;
        font-weight: bold;
      }

      /* Widths box */

      .widths {
        margin-top: 20px;
        border: 1px solid black;
        padding: 10px;
      }

      .widths > div > span:nth-of-type(1) { width: 120px; display: inline-block;  font-weight: bold}
      .widths .separator { height: 20px;}

      /* Flexbox Example */

      .flexbox {
        padding: 10px;
        border: 1px solid orange;
      }
      .flexbox .flexrow {
         display: flex;
      }

      .flexbox .flexrow .product-title {
        min-width: min-content;
        max-width: max-content;
        flex: 100000 0 0px;
      }
      .flexbox .flexrow .separator-blue {
        min-width: 5px;
        max-width: 100px;
        flex: 1 0 0px;
      }
      .flexbox .flexrow .button {
        min-width: max-content;
        max-width: 120px;
        flex: 10000 0 0px;
      }
      .flexbox .flexrow .separator-red {
        min-width: 3px;  
        max-width: 20px;
        flex: 100 0 0px;
      }
    </style>
  </head>
  <body>
    <h3>Grid Example</h3>
    <div class="grid">
      <!-- PRODUCT ONE (ROW 1) -->
      <div class="product-title one">Product One</div>
      <div class="separator-blue one"></div>
      <div class="button one a">BUTTON 1A</div>
      <div class="separator-red one"></div>
      <div class="button one b">BUTTON 1B</div>
      
      <!-- PRODUCT TWO (ROW 2) -->
      <div class="product-title two">Product Two Longer Title</div>
      <div class="separator-blue two"></div>
      <div class="button a two">BUTTON 2A</div>
      <div class="separator-red two"></div>
      <div class="button b two ">BUTTON 2B</div>
    </div>

    <div class="widths">
      <div><span>Product One:</span>   <span class="product-title  one output"></span></div>
      <div><span>Separator Blue:</span><span class="separator-blue one output"></span></div>
      <div><span>Button 1A:</span>     <span class="button a       one output"></span></div>
      <div><span>Separator Red:</span> <span class="separator-red  one output"></span></div>
      <div><span>Button 1B:</span>     <span class="button b       one output"></span></div>
      <div class="separator"></div>
      <div><span>Product Two:</span>   <span class="product-title  two output"></span></div>
      <div><span>Separator Blue:</span><span class="separator-blue two output"></span></div>
      <div><span>Button 2A:</span>     <span class="button a       two output"></span></div>
      <div><span>Separator Red:</span> <span class="separator-red  two output"></span></div>
      <div><span>Button 2B:</span>     <span class="button b       two output"></span></div>
    </div>

    <h3>Flexbox Example (single row)</h3>
    <div class="flexbox">
      <div class="flexrow">
        <div class="product-title two">Product Two Longer Title</div>
        <div class="separator-blue two"></div>
        <div class="button two a">BUTTON 1A</div>
        <div class="separator-red two"></div>
        <div class="button two b">BUTTON 1B</div>
      </div>
    </div>


    <h3>Flexbox Issue (Multiple rows)</h3>
    <div>This illustrates the issue of using flexbox, is that we can not base the width of the first column on the longest max-content... we could do min-width: 30px; max-width: 200px; but then you would need to manually set this based on the content</div>

    <div class="flexbox">
      <div class="flexrow">
        <div class="product-title">Product One</div>
        <div class="separator-blue"></div>
        <div class="button a">BUTTON 1A</div>
        <div class="separator-red"></div>
        <div class="button b">BUTTON 1B</div>
      </div>
       <div class="flexrow">
        <div class="product-title">Product Two Longer Title</div>
        <div class="separator-blue"></div>
        <div class="button a">BUTTON 2A</div>
        <div class="separator-red"></div>
        <div class="button b">BUTTON 2B</div>
      </div>
    </div>

    <script type="text/javascript">
      /* Resize Observer Size Monitoring */
      function monitor(outputId, monitorClass) {
          const ro = new ResizeObserver(entries => { 
              document.querySelector(outputId).innerText = entries[0].contentRect.width 
          })
          ro.observe(document.querySelector(monitorClass))
      }
      monitor('.product-title.one.output',  '.product-title.one')        
      monitor('.separator-blue.one.output', '.separator-blue.one')        
      monitor('.button.one.a.output',       '.button.one.a')  
      monitor('.separator-red.one.output',  '.separator-red.one')        
      monitor('.button.one.b.output',       '.button.one.b')  

      monitor('.product-title.two.output',  '.product-title.two')        
      monitor('.separator-blue.two.output', '.separator-blue.two')        
      monitor('.button.two.a.output',       '.button.two.a')  
      monitor('.separator-red.two.output',  '.separator-red.two')        
      monitor('.button.two.b.output',       '.button.two.b')  
    </script>
</body>
</html>
```
### Zip File Containing the HTML, CSS, JS Above
[grid-track-explicit-sizing-controls.html.zip](https://github.com/w3c/csswg-drafts/files/3425077/grid-track-explicit-sizing-controls.html.zip)










 





Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/4147 using your GitHub account

Received on Wednesday, 24 July 2019 04:57:21 UTC