Search code examples
csscss-gridspecifications

Calculating the actual length of fr units


I'm asking here because I suck at maths.

Assuming that the grid size would be 1920px, and I'm not using explicit sizing.

How many fr's would be ie. 1200px ? And how I can calculate it ?

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

The spec says:

12.7.1. Find the Size of an fr

This algorithm finds the largest size that an fr unit can be without exceeding the target size. It must be called with a set of grid tracks and some quantity of space to fill.

  • Let leftover space be the space to fill minus the base sizes of the non-flexible grid tracks.
  • Let flex factor sum be the sum of the flex factors of the flexible tracks. If this value is less than 1, set it to 1 instead.
  • Let the hypothetical fr size be the leftover space divided by the flex factor sum.
  • If the product of the hypothetical fr size and a flexible track’s flex factor is less than the track’s base size, restart this algorithm treating all such tracks as inflexible.
  • Return the hypothetical fr size.

They mean that the "hypothetical fr size" would be for the unit (1fr) ? I guess so

1920/(1+3.3335+1) = 359.988750352 <-- The hypothetical fr size
359.988750352 * 3.3335 = 1200.0224993

So I guess that's a way to get how many pixels are n fr value.

But, what if I want to do the reverse, which would be the proper way/correct formula to calculate how many fr's would be 1200px ?


UPDATE:

Here's a live example, of what I was doing. The minmax(min, max) helped me a lot. I've solved some overflowing errors. And figured out how I can use rem units in grid template tracks but still benefit from shrinking :D

https://codepen.io/jeflopo/pen/OmZBEJ

/**********************************/
/* BODY - THE MAIN GRID CONTAINER */
/**********************************/
/**********************************/
/* DEFINING GRID AREAS            */
/**********************************/
.header {
  grid-area: header;
}

.navigation {
  grid-area: nav;
}

.main {
  grid-area: main;
}

.posts {
  grid-area: posts;
}

.sidebar {
  grid-area: sidebar;
}

.footer {
  grid-area: footer;
}

/**********************************/
/* HOW HAS GRID AREAS TO BEHAVE ? */
/**********************************/
/* I'VE LET THEM TO SHRINK TO 0 AND I WILL CONTROL IT WITH MEDIA QUERIES
SETTING `min` of minmax(min, max) to 0 DRASTICALLY PREVENTS THE LAYOUT TO OVERFLOW WHEN RESIZING THE WINDOW.
*/
.header,
.navigation,
.main,
.footer {
  display: grid;
  grid-template-columns: 1fr minmax(0, 120rem) 1fr;
}

.main .container {
  display: grid;
  grid-gap: 1rem;
  grid-template-columns: minmax(0, 120rem) 30rem;
  grid-template-areas: "posts sidebar";
  margin: 0;
  padding: 1rem 0;
}
.main .posts {
  grid-column: 1/2;
  padding: 1rem;
}
.main .sidebar {
  grid-column: 2/3;
  padding: 1rem;
}

/**********************************/
/* CONTENT WRAPPER                */
/**********************************/
/* THESE GRID AREAS (header, main, and footer) OCCUPY FULL TRACKS.
I USE THIS TO CONSTRAINT THE CONTENT OF GRID AREAS TO THE CENTER COLUMN IN THE MAIN GRID. */
.container {
  grid-column: 2/3;
}

/******************************************/
/* SOME BASIC STYLING PRESETS             */
/******************************************/
html {
  font-size: 10px;
}

body {
  font-family: 'Open Sans', sans-serif;
  font-size: 1.6rem;
  margin: 0;
  padding: 0;
}

a {
  text-decoration: none;
  color: #00f;
}

ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

li {
  display: inline;
}
li a {
  color: #fff;
}

.header {
  background: #888;
}

.navigation {
  background: #666;
}

.posts {
  background: #f66;
}

.sidebar {
  background: #6f6;
}

.footer {
  background: #888;
}
<header class="header">
  <div class="container">
    <div class="header__logo">
      <a href="#"><h1>LOGO</h1></a>
    </div>
  </div>
</header>
<nav class="navigation">
  <ul class="container">
    <li><a href="#">Item 1</a></li>
    <li><a href="#">Item 2</a></li>
    <li><a href="#">Item 3</a></li>
  </ul>
</nav>
<main class="main" role="main">
  <div class="container">
    <section class="posts">
      <article class="post">
        <h1>This is a title</h1>
        <p>This is the body of an article</p>
      </article>
    </section>
    <aside class="sidebar">
      <span>SIDEBAR</span>
    </aside>
  </div>
</main>
<footer class="footer">
  <div class="container">
    <p>FOOTER</p>
  </div>
</footer>


Solution

  • You have a grid container with 1200px of free space along the X or Y axis (doesn't matter).

    If the container has one grid track set to 1fr, that column / row would consume all 1200px.

    If you have grid-template-columns: 1fr 1fr, then each column would receive an equal distribution of the free space. So each column would be 600px wide, similar to this:

    grid-template-columns: 600px 600px
    

    In your case, here's how it works:

    From the spec:

    It must be called with a set of grid tracks and some quantity of space to fill.

    You've called the grid tracks with this rule:

    grid-template-columns: 1fr 3.3335fr 1fr
    

    Your space to fill is 1200px.


    1. Let leftover space be the space to fill minus the base sizes of the non-flexible grid tracks.

    Again, the space to fill ("leftover space") is 1200px.

    There are no non-flexible grid tracks. That would be like a column set to 200px:

    grid-template-columns: 1fr 3.3335fr 200px 1fr
    

    The fr algorithm would subtract that 200px track. The remaining space would be available to the fr's.


    1. Let flex factor sum be the sum of the flex factors of the flexible tracks.

    That means we add up the fr values:

    1 + 3.3335 + 1 = 5.3335
    

    The flex factor sum is 5.3335.


    1. Let the hypothetical fr size be the leftover space divided by the flex factor sum.

    1200px / 5.3335fr = 225px


    Now we know that each fr unit represents 225px.

    • 1fr = 225px
    • 2fr = 450px
    • 3fr = 675px
    • etc...

    This means that:

     grid-template-columns: 1fr 3.3335fr 1fr
    

    is equivalent to:

     grid-template-columns: (1fr x 225px) (3.3335fr x 225px) (1fr x 225px)
    

    which results in:

     grid-template-columns: 225px 750.0375px 225px
    

    It's important to note, however, that the fr unit is not intended to set specific lengths for grid tracks. It's job is to distribute free space in the container (similar to flexbox's flex-grow). So your question appears to be a bit theoretical and impractical. If the container is re-sized even slightly, or an inflexible track is thrown into the mix, all pixel calculations above are thrown off.


    Update (based on comment)

    There's an small error. My grid size is 1920px not 1200... But its an extrapolable example. I wanted 3.3335fr relate to 1200px, and not to be the sum of all tracks... 3.3335fr are ~1200px with two adjacent 1fr columns in a grid thats 1920px width.

    If the grid container is 1920px wide, then the hypothetical fr size is 360px.

    This means that:

     grid-template-columns: (1fr x 360px) (3.3335fr x 360px) (1fr x 360px)
    

    which results in:

     grid-template-columns: 360px 1200px 360px