CSS Grid row max height 1fr, scroll content

У меня есть макет календаря с использованием следующих стилей CSS-сетки:

.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)

Первые две строки - это заголовок 'control' и заголовки дней недели, за которыми следуют div для каждого дня месяца:

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

У каждого дня есть .header и .content div. Я хочу, чтобы div .header занимал 1/5 высоты строки, а содержимое занимало 4/5 и прокручивалось, если содержимое переполняется.

Однако любая комбинация стилей, которые я пробовал, просто приводит к .content росту, компенсируя сжатие других строк сетки. Я предполагаю, что это связано с использованием 1fr в контейнерах grid-template-rows.

Можно ли достичь максимальной высоты 4/5 высоты строки сетки, что составляет 1/6 доступного вертикального пространства на .content div?

(Примечание: установка overflow-y: auto; на .day обеспечивает то, что я хочу, но заголовок включается в область прокрутки. Установка overflow-y: auto; на .content, похоже, ничего не дает.)


person Joel    schedule 30.07.2017    source источник


Ответы (3)


Каждая строка в сетке (кроме заголовков) имеет высоту 1fr. Это означает, что каждая строка будет занимать равную долю свободного места в контейнере.

Просто обратите внимание, что fr размер на самом деле не определяет длину ни width, ни height. С fr вы просто распределяете свободное место.

Но для того, чтобы свойство overflow работало, должна быть определена фактическая высота:

Чтобы overflow имел эффект, контейнер уровня блока должен иметь либо заданную высоту (height или max-height), либо white-space, установленную на nowrap.

источник: https://developer.mozilla.org/en-US/docs/Web/CSS/overflow

Поэтому рекомендуется использовать фактическую высоту, а не распределение пространства. Вот пример:

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>

обновленная демонстрация

person Michael Benjamin    schedule 30.07.2017
comment
Это определенно работает, но я надеялся на решение, которое не требовало фиксированной высоты строк. Я хотел, чтобы они занимали 1/6 доступного пространства, но не допустили роста сверх этого. Кажется, правила сетки этого не допускают? - person Joel; 30.07.2017
comment
Это не правила сетки. Это свойство overflow, которое требует фиксированной длины для фактического переполнения. - person Michael Benjamin; 30.07.2017

Хотя ответ @ MichaelB является технически правильным, я обнаружил, что вы можете это сделать, не прибегая к указанию высоты вручную. И сделать это тоже очень просто.

Помимо добавления overflow:auto к элементу сетки, на котором вы хотите видеть полосы прокрутки, вам также необходимо добавить overflow:auto к родительскому элементу этого элемента сетки.

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>

Лучше всего то, что он работает во всех браузерах (протестирован в Firefox, Chrome и Edge), поэтому, скорее всего, это не взлом, а предполагаемое поведение.

Почему так происходит?

Я не могу найти четкого ответа в спецификации пока, но я буду искать и обновлять здесь, когда у меня будет что-то конкретное.

Обновлять

Прочтите следующий комментарий @ Victoria: CSS Максимальная высота строки сетки 1fr, прокрутка содержимого, у которого есть другой способ сделать это - я бы рекомендовал вам использовать этот способ, если ваш дизайн позволяет это.

person kumarharsh    schedule 31.10.2018
comment
Ух ты! Пришлось немного поискать, чтобы найти это, но это работает! - person Victoria; 10.10.2019
comment
Если продолжить поиск, альтернативой размещению auto на родительском элементе является определение размера дорожки сетки minmax (0, 1fr) вместо простого 1fr. См. github.com/w3c/csswg-drafts/issues/1777. - person Victoria; 10.10.2019
comment
@Victoria, вы правы - я тоже узнал об этом несколько месяцев назад. Я обновлю свой ответ. Спасибо :) - person kumarharsh; 15.10.2019

Похоже, если вы установите заголовок position: fixed, установите calc(100%/7) и зададите ему непрозрачный background. Установите .day на overflow-y:auto и дайте своему .content margin-top, который, кажется, работает, а также поддерживает текучесть календаря на высоте браузера.

Если важно, чтобы заголовок был на всю ширину дневного блока с границами, вы можете добавить текущие свойства границы только с правой стороны и добавить 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;
}

Демо

person Joe B.    schedule 30.07.2017