Многоколоночные макеты

При вёрстке сайта, одной из первых задач верстальщика является создание основного шаблона.Основным шаблоном в данном случае называется вёрстка без контента - это совокупность шапки сайта, меню, футера и прочих элементов, которые встречаются на каждой странице.

Итак, попробуем сверстать простой макет сайта. Сверху у нас будет шапка сайта, слева будет навигация по разделу сайта, справа контент, а снизу футер.Стандартный шаблон, ничего необычного. Начнём с HTML.

<div class="main-container">
  <header>Header</header>
  <nav>Site navigation</nav>
  <main>Site content</main>
  <footer>Footer</footer>
</div>

Если мы запустим это прямо сейчас, мы лишь увидим, как все эти элементы расположились друг под другом. Придётся добавить стилей.

Float

В древние времена существовало несколько вариантов вёрстки макетов, а самым адекватным из них был float. Как мы выяснили в одной из предыдущих глав,с помощью float можно сделать так, чтобы картинка обтекалась текстом. То же можно применить и к блокам. Если мы возьмём два блочных элемента с определенными заранее шириной и высотой, то, добавив к нимfloat: left;, мы увидим, как эти два блочных элемента выстроятся в один ряд, если ширина страницы это позволит.

.main-container {
  margin: 0 auto;
  max-width: 800px;
}
header {
  height: 50px;
  background: cornflowerblue;
}
nav {
  height: 80px;
  background: darksalmon;
  width: 20%;   /* Задаём ширину блока навигации */
  float: left;  /* Устанавливаем обтекание */
}
main {
  height: 120px;
  background: khaki;
  width: 80%;   /* Задаём ширину контента */
  float: left;  /* Устанавливаем обтекание */
}
footer {
  clear: both;  /* Сбрасываем обтекание */
  height: 50px;
  background: darkseagreen;
}

Казалось бы, на этом можно закончить наш урок, но, если хорошо подумать, можно увидеть, как много проблем мы породили этим решением:

  1. Мы не можем жёстко зафиксировать ширину навигационного меню
  2. Если перед футером нам потребуется вставить какой-то блок, например, рекламный, вёрстка "поедет", ведь мы благополучно забудем проclear: both;
  3. Высота навигации никак не зависит от высоты контента и наоборот. Под более коротким блоком будет пустота.

Подробнее о float читайте тут: float

Flex

Рассмотрим более современный способ. Мы можем использоватьdisplay: flex;у.main-container, тогда мы сможем избавиться от пары недостатков предыдущего примера.

.main-container {
  margin: 0 auto;
  max-width: 800px;
  display: flex;        /* Объявляем контейнер флексом */
  align-items: stretch; /* Высота элементов должна подстраиваться по высоте самого высокого элемента */
  flex-wrap: wrap;      /* Если не хватает места в строке, переносить блок на следующую */
}
header {
  width: 100%;          /* Обязательно задаём ширину для шапки */
  height: 50px;
  background: cornflowerblue;
}
nav {
  background: darksalmon;
  width: 20%;           /* Задаём ширину навигационного меню */
}
main {
  height: 120px;
  background: khaki;
  width: 80%;           /* Задаём ширину контента */
}
footer {
  width: 100%;          /* Обязательно задаём ширину для футера */
  height: 50px;
  background: darkseagreen;
}

Как мы видим, каким бы длинным ни был контент, под навигацией не образуется пробела, а так как больше нет флоатов, то перед футером можно вставить любой блок.К сожалению, таким способом мы не смогли решить последнюю проблему: как задать у меню фиксированную ширину, а ширина контента при этом была переменной?

Больше о flex здесь:

Grid

Самым современным способом вёрстки макетов на сегодняшний день является т.н. Grid layout. Грид позволяет выстраивать элементы внутри себя по заранее заданнойсетке. Для начала разберём пример, а затем разберёмся подробнее, как это работает.

.main-container {
  margin: 0 auto;
  max-width: 800px;
  display: grid;                        /* Объявляем контейнер гридом */
  grid-template-rows: 50px 1fr 50px;    /* (1) Задаём строки */
  grid-template-columns: 200px 1fr;     /* (2) Задаём колонки */
  grid-template-areas: 'header header'  /* (3) Задаём названия областей */
    'nav content'
    'footer footer';
}
header {
    grid-area: header;  /* Шапка займёт область с названием header */
    background: cornflowerblue;
}
nav {
  background: darksalmon;
  grid-area: nav;     /* Навигация займет область nav */
}
main {
  height: 120px;
  background: khaki;
  grid-area: content;  /* Контент займёт content */
}
footer {
  grid-area: footer;  /* Футер - footer */
  background: darkseagreen;
}

Как можно заметить, мы избавились от всех негативных последствий. А теперь разберёмся подробнее, как это работает.

  1. grid-template-rows: 50px 1fr 50px;означает, что если смотреть на сайт по вертикали, то у нас образуются три строки: шапка, навигация с контентом, футер. Высоту первой строки, шапки, установим равной 50px, высота второй строки нам неизвестна, а высота третьей, футера, тоже 50px.
  2. grid-template-columns: 200px 1fr;означает, что если смотреть на сайт по горизонтали, то образуются два столбца: слева навигация, справа контент. Ширину навигации установим равной 200px, а ширина контента нам неизвестна и она займёт всё доступное пространство.
  3. Итак, на этом этапе мы поняли, что у нас будет три строки и два столбца. Это и указываем далее: grid-template-areas: 'header header' 'nav content' 'footer footer';. Этот код создаёт три строки и два столбца, состоящие из именованных областей. Именовать эти области можно как угодно, их названия будут указаны в дочерних элементах в свойствеgrid-area. Здесь мы видим, что шапка займёт две колонки; навигация — одну, левую колонку; контент — одну правую; футер — две колонки.

Таким образом, мы получили идеальную сетку для нашего сайта. Подробнее о grid layout читать здесь:

Calc

А теперь настало время удивляться. Вернёмся к примеру с флексом и добавим еще немного CSS:

nav {
  width: 200px;
}
main {
  width: calc(100% - 200px);
}

Таким образом мы избавились от проблемы фиксированной ширины меню. Благодаря функцииcalc(), мы заставили ширину контента зависеть от ширины блока навигации. О calc() можно почитать здесь

Ещё один вариант вёрстки шаблона

На самом деле, есть еще один вариант. Он сложный и ненадёжный, но для общего развития, стоит упомянуть и его.

.main-container {
  margin: 0 auto;
  max-width: 800px;
  position: relative;
  padding: 50px 0 50px 200px;
  box-sizing: border-box;
}
header {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 50px;
  background: cornflowerblue;
}
nav {
  background: darksalmon;
  position: absolute;
  top: 50px;
  bottom: 50px;
  left: 0;
  width: 200px;
  overflow-y: auto;
}
main {
  height: 120px;
  background: khaki;
  width: 100%;
}
footer {
  position: absolute;
  bottom: 0;
  right: 0;
  left: 0;
  height: 50px;
  background: darkseagreen;
}

Что тут сделано?

  1. Основной контейнер спозиционирован относительно, чтобы абсолютно-спозиционированные элементы выстраивались внутри него.
  2. Шапка и футер прижимаются к верхней и нижней части контейнера.
  3. Навигация прижимается к левой части контейнера.
  4. Чтобы блоки не налезали друг на друга, с помощью внутренних отступов у основного контейнера, отделим контент от остальных блоков.
  5. Так как теперь высота блока навигации зависит от высоты контента, добавляем свойствоoverflow, чтобы при недостаточной высоте навигации, внутри неё появлялся скроллбар.