Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS Grid row max height 1fr, scroll content

Tags:

html

css

css-grid

I have a 'calendar' layout using the following css-grid styles:

.calendar {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: 1.5em 1.5em repeat(6, 1fr);
  width: 100%;
  height: 100%;
}

(Codepen https://codepen.io/joelhoward0/pen/vJLmWK)

The first two rows are a 'controls' header and days of the week headers, followed by divs for each day in the month:

<div class="day">
  <div class="header">26</div>
  <div class="content"></div>
</div>

Each day has a .header and .content div. I want the .header div to take up 1/5 of the height of the row, and the content to take up 4/5 and scroll if the content overflows.

However, any combination of styles I've tried just leads to .content growing, shrinking the other grid rows to compensate. I assume this is due to the use of 1fr on the container grid-template-rows.

Is it possible to achieve a max height of 4/5 of the grid row height, which is 1/6 of the vertical space available, on the .content div?

(Note: setting overflow-y: auto; on .day achieves what I want, but the header is included in the scrolling area. Setting overflow-y: auto; on .content doesn't seem to do anything.)

like image 292
Joel Avatar asked Jul 30 '17 00:07

Joel


3 Answers

Although @MichaelB's response is technically correct, I have found that you can actually do it without resorting to specifying heights manually. And it's dead simple to do it too.

In addition to adding overflow:auto to the grid-item you want scrollbars on, you also need to add an overflow:auto to the parent of that grid-item.

https://codepen.io/kumarharsh/pen/jejGMm?editors=1100

html,
body {
  height: 100%;
}

body {
  position: relative;
  margin: 0;
}

.calendar {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: 1.5em 1.5em repeat(6, 1fr);
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: solid grey;
  border-width: 1px 0 0 1px;
  position: relative;
}

.dayHeader {
  border: solid grey;
  border-width: 0 1px 1px 0;
}

.day {
  border: solid grey;
  border-width: 0 1px 1px 0;
  overflow: auto; /* added overflow here! */
}

.content {
  overflow-y: auto;
}

.controls {
  grid-column-start: 1;
  grid-column-end: 8;
  border: solid grey;
  border-width: 0 1px 1px 0;
}
<div class="calendar">
  <div class="controls">July 2017</div>
  <div class="dayHeader">Sunday</div>
  <div class="dayHeader">Monday</div>
  <div class="dayHeader">Tuesday</div>
  <div class="dayHeader">Wednesday</div>
  <div class="dayHeader">Thursday</div>
  <div class="dayHeader">Friday</div>
  <div class="dayHeader">Saturday</div>
  <div class="day">
    <div class="header">25</div>
    <div class="content">
      this is a lot of content that I want to have scroll instead of just stretching the grid row and forcing the other rows to be smaller to compensate for this one's increased size due
    </div>
  </div>
  <div class="day">
    <div class="header">26</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">27</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">28</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">29</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">30</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">1</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">2</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">3</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">4</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">5</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">6</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">7</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">8</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">9</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">10</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">11</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">12</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">13</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">14</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">15</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">16</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">17</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">18</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">19</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">20</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">21</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">22</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">23</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">24</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">25</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">26</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">27</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">28</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">29</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">30</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">31</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">1</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">2</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">3</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">4</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">5</div>
    <div class="content"></div>
  </div>
</div>

Best of all, It works in all browsers (tested in Firefox, Chrome, and Edge), so it's likely not a hack, but an intended behaviour.

Why does this happen?

I can't find a clear answer in the spec yet, but I'll keep looking and update here when I have something concrete.

Update

Do read @Victoria's followup comment: CSS Grid row max height 1fr, scroll content which has another way to do it - I'd recommend you use that way if your design allows it.

like image 71
kumarharsh Avatar answered Oct 01 '22 02:10

kumarharsh


Each row in the grid (except for the headers) is set to 1fr height. That means each row will consume an equal proportion of free space in the container.

Just note that fr sizing doesn't actually define a length – either width or height. With fr you are simply distributing free space.

But for the overflow property to work, an actual height must be defined:

In order for overflow to have an effect, the block-level container must have either a set height (height or max-height) or white-space set to nowrap.

source: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow

Therefore, consider using actual heights as opposed to space distribution. Here's an example:

html,
body {
  height: 100%;
}

body {
  position: relative;
  margin: 0;
}

.calendar {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: 1.5em 1.5em repeat(6, 175px);  /* adjusment */
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: solid grey;
  border-width: 1px 0 0 1px;
  position: relative;
}

.dayHeader {
  border: solid grey;
  border-width: 0 1px 1px 0;
}

.day {
  height: 175px;  /* new */
  border: solid grey;
  border-width: 0 1px 1px 0;
}

.header { /* new */
  height: 20%;
  background-color: yellow;
}

.content {
  height: 80%; /* new */
  overflow-y: auto;
  background-color: aqua;
}

.controls {
  grid-column-start: 1;
  grid-column-end: 8;
  border: solid grey;
  border-width: 0 1px 1px 0;
}
<div class="calendar">
  <div class="controls">July 2017</div>
  <div class="dayHeader">Sunday</div>
  <div class="dayHeader">Monday</div>
  <div class="dayHeader">Tuesday</div>
  <div class="dayHeader">Wednesday</div>
  <div class="dayHeader">Thursday</div>
  <div class="dayHeader">Friday</div>
  <div class="dayHeader">Saturday</div>
  <div class="day">
    <div class="header">25</div>
    <div class="content">
      this is a lot of content that I want to have scroll instead of just stretching the grid row and forcing the other rows to be smaller to compensate for this one's increased size due
    </div>
  </div>
  <div class="day">
    <div class="header">26</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">27</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">28</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">29</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">30</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">1</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">2</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">3</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">4</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">5</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">6</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">7</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">8</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">9</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">10</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">11</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">12</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">13</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">14</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">15</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">16</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">17</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">18</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">19</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">20</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">21</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">22</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">23</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">24</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">25</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">26</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">27</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">28</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">29</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">30</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">31</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">1</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">2</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">3</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">4</div>
    <div class="content"></div>
  </div>
  <div class="day">
    <div class="header">5</div>
    <div class="content"></div>
  </div>
</div>

revised demo

like image 34
Michael Benjamin Avatar answered Oct 01 '22 02:10

Michael Benjamin


It looks like if you set the header to position: fixed, set calc(100%/7) and give it a non-transparent background. Set .day to overflow-y:auto and give your .content a margin-top that seems to work as well as keeping the calender fluid to the browser height.

If it's important for the header to be the full width of the day block with borders you can add your current border properties to only the right side and add box-sizing: border-box

.header{
  position:fixed;
  background: white;
  width: calc(100%/7);
  border: solid grey;
  border-width: 0 1px 0 0;
  box-sizing: border-box;
}

.day {
  border: solid grey;
  border-width: 0 1px 1px 0;
  overflow-y: auto;
}

.content {
  margin-top: 20px;
}

Demo

like image 28
Joe B. Avatar answered Oct 01 '22 04:10

Joe B.