Лекция 3. Вёрстка средствами CSS

Веб-программирование

Лекция 3 Вёрстка
средствами CSS

Веб-программирование · лекция 3 из 16

На прошлой лекции

🎯Селекторынаучились выбирать нужный элемент
⚖️Каскадразобрались, кто побеждает в споре
📦Box modelузнали, из чего состоит коробка

Коротко: научились выбрать элемент и оформить его. Сегодня учимся ставить элементы на места.

Ваша страница после лекции 2

Типографика в порядке. Цвета подобраны. Рамки скруглены, отступы расставлены, ссылки подсвечиваются на наведении.

И всё это лежит в одну колонку сверху вниз.

Почему всё
в столбик?
Кто это решил?

Никто не решал

Это поведение по умолчанию, и у него есть имя — поток документа. Браузер раскладывает элементы сам, ещё до того, как вы написали первую строчку про раскладку.

Вся сегодняшняя лекция — про то, как договориться с потоком: где его хватает, где его меняют и где из него выходят совсем.

центрируем div: 1

Сегодня за 90 минут

И ответ на вопрос, который веб задаёт себе двадцать лет.

Часть 1 · 12 минут Поток документа
и display

Что браузер делает сам

Нормальный поток

Алгоритм раскладки по умолчанию: блочные элементы идут сверху вниз, строчные — слева направо, перенося строки как текст.

Его не нужно включать. Он работает всегда, пока вы не попросили иного.

Блочные: каждый с новой строки

<div> — блочный
<p> — блочный
<section> — блочный

занимают всю доступную ширину, даже если текста два слова

Строчные: в ряд, как слова

Обычный текст, внутри него <span>, потом <a>, потом <strong> — и всё это одна строка, которая переносится по границе окна ровно так же, как переносится текст.

ширина по содержимому, перенос по словам

Это не свойство тега

«Блочный» и «строчный» — не врождённое качество элемента. Это значение свойства display, которое пришло из стилей браузера, — того самого user agent stylesheet из прошлой лекции.

div    { display: block; }
span   { display: inline; }

Значит, его можно переписать. И это первое, что мы сделаем.

Что получится?

Отвечаем вслух, потом проверим

Задаём строчному элементу размеры

span {
    width: 300px;
    height: 200px;
    margin-top: 50px;
}

Насколько большим станет <span> и отодвинет ли он соседей?

Проверяем

Не изменится ничего. У строчного элемента width и height не работают, вертикальные margin не раздвигают соседей. Браузер честно пропустит все три строки.

Строчный элемент — часть текстовой строки, а строка не станет двухсот пикселей высотой по просьбе одного слова. Хотите геометрию — выйдите из строки.

inline-block: гибрид

Снаружи

ведёт себя как строчный: встаёт в ряд с соседями

Внутри

ведёт себя как блочный: ширина, высота, отступы работают

150px 200px 120px

И сразу загадка

<span class="box">A</span>
<span class="box">B</span>

Три блока по 33.33% с inline-block не помещаются в строку и последний съезжает вниз. Почему?

Из-за пробела. Между тегами в разметке стоит перевод строки, а это тоже пробельный символ. Браузер рисует его честно — примерно четыре пикселя, которых нет ни в одном вашем правиле.

Хаки той эпохи

/* убить пробелы размером шрифта */
.parent { font-size: 0; }
.child  { font-size: 16px; }  /* и вернуть его детям */
<span>A</span><!--
--><span>B</span>   <!-- перенос внутри комментария -->

Так действительно писали. Сегодня так не пишут: есть gap, до которого дойдём в части 4.

центрируем div: 2

А если содержимое
не влезает?

Блок с фиксированной высотой и лишним текстом

По умолчанию — вылезает наружу

Этот текст не помещается в отведённые ему пятьдесят четыре пикселя и спокойно выезжает за рамку блока.

Привет из лекции 2: браузер не станет ломать вашу высоту ради содержимого.

overflow: что делать с лишним

visibleпо умолчанию — вылезает
hiddenобрезать молча
scrollполосы всегда
autoполосы по необходимости

Есть и раздельные overflow-x и overflow-y.

Важное следствие: всё, кроме visible, делает элемент контейнером прокрутки. Запомните — в части 3 это объяснит одну загадку.

display: none против visibility: hidden

display: none

элемента нет ни в потоке, ни в дереве доступности: места не занимает, скринридер не читает

visibility: hidden

элемент невидим, но место осталось: в потоке зияет дыра точно его размера

Спрятать от глаз и спрятать от всех — разные задачи.

1996

Макет таблицей

<table width="760" cellspacing="0"><tr>
  <td><img src="spacer.gif" width="1" height="1"></td>
  <td><table> ... </table></td>
</tr></table>

spacer.gif — прозрачный пиксель, растянутый до нужного отступа. Распорка.

2005

float придумали для газеты

Задача была одна: обтекание картинки текстом. Потом заметили, что два float: left дают две колонки — и пятнадцать лет веб верстали вот этим:

.clearfix::after {
    content: ""; display: table; clear: both;
}

Последний привет из эпохи

.outer { display: table; width: 100%; height: 300px; }
.inner { display: table-cell; vertical-align: middle; }

Вертикальное центрирование через таблицу — без таблицы. Работало, но требовало лишнего элемента-обёртки и ломалось от любого изменения макета.

центрируем div: 3

Часть 2 · 10 минут Единицы
измерения

Чем мерить, кроме пикселей

До сих пор были только пиксели

Это было сознательное упрощение: в лекции 2 хватало px, и лишние единицы только мешали бы.

Дальше упрощение начинает вредить. Макет должен выживать на разных экранах и при увеличенном шрифте, — а для этого размеры должны быть относительными.

Относительно шрифта: em и rem

em

от размера шрифта текущего элемента

rem

от размера шрифта корня, то есть <html>

Разница в одной букве, а поведение отличается принципиально.

Какой размер
на четвёртом
уровне?

Вложенные списки

html { font-size: 16px; }
li   { font-size: 0.9em; }
<ul><li>первый уровень
  <ul><li>второй
    <ul><li>третий
      <ul><li>четвёртый</li></ul>

Проверяем

Примерно 10.5 пикселя. Каждый уровень считает свои 90 % от уже уменьшенного родителя: 0.9⁴ ≈ 0.66. Текст тает на глазах, и это называется компаундинг.

С 0.9rem все четыре уровня были бы одинаковыми: отсчёт всегда от корня, а не от соседа сверху.

Что где удобнее

rem — для шрифта

  • font-size
  • ширина колонок
  • отступы макета

em — внутри элемента

  • padding кнопки
  • border-radius
  • отступ у иконки

padding: 0.5em 1em у кнопки масштабируется вместе с её текстом — крупная кнопка сама станет просторнее. Это ровно то, что нужно.

Зачем это на самом деле

Пользователь может увеличить базовый размер шрифта в настройках браузера — так делают люди со слабым зрением, и таких больше, чем кажется.

Макет на rem вырастет вместе с ним. Макет на pxнет.

Та же тема, что alt в лекции 1 и :focus в лекции 2. Доступность — не отдельная дисциплина, а следствие мелких решений.

Проценты: главное — от чего

.child { width: 50%; }   /* от ширины родителя — ожидаемо */

С шириной интуиция работает. Дальше она перестаёт.

Проценты в отступах считаются от ширины

.box {
    /* ← 10% от ШИРИНЫ родителя, не от высоты */
    padding-top: 10%;
    margin-top: 10%;     /* ← и здесь тоже от ширины */
}

Даже по вертикали. Это кажется произволом, но именно на этом построен старый трюк с пропорциональными блоками: видео 16:9 задавали через padding-top: 56.25%.

Сегодня для этого есть aspect-ratio.

calc(): арифметика прямо в CSS

.main {
    width: calc(100% - 320px);
}

Главное здесь — смешивание единиц. Проценты знает только браузер, пиксели знаете вы; посчитать это заранее нельзя, а calc() считает уже на месте.

Одна ловушка на всю жизнь

не работает

calc(100%-320px)

работает

calc(100% - 320px)

Пробелы вокруг - и + обязательны, иначе минус читается как часть числа. Правило молча не применится — и вы будете искать опечатку глазами, как в лекции 2.

Почему height: 100% не работает

/* эту строку все копируют не глядя */
html, body { height: 100%; }
.page      { height: 100%; }

Процент высоты считается от высоты родителя. Если у родителя высота auto — считать не от чего, и браузер просто игнорирует правило.

Отсюда цепочка до самого корня. И отсюда же привычка брать вместо этого min-height: 100vh.

Относительно окна

1vw1 % ширины окна
1vh1 % высоты окна
vminпроцент меньшей стороны
vmaxпроцент большей стороны

Удобно для экранов «на всю высоту» и крупных заголовков. И ровно здесь веб столкнулся с телефонами.

100vh на телефоне врёт

Адресная строка то появляется, то прячется при прокрутке. Высота окна меняется на ходу — и блок «ровно на экран» либо прыгает, либо вылезает за нижний край вместе с кнопкой, ради которой его делали.

Несколько лет это чинили джаваскриптом, пересчитывая высоту вручную.

Спецификация догнала реальность

svhsmall — когда строка видна
lvhlarge — когда спрятана
dvhdynamic — как сейчас

Хороший пример того, как работает CSS: сначала появляется боль, потом — единица измерения. 100dvh — то, что вы хотели написать с самого начала.

Ещё три, которые пригодятся

/* ch — ширина нуля в текущем шрифте */
.article { max-width: 65ch; }

Комфортная длина строки — 50–75 знаков. 65ch говорит это прямо, а 750px — только приблизительно и только для одного шрифта.

fr — доля свободного места, живёт только в гридах, встретим в части 5. clamp() — «не меньше, не больше, а между» — в лекции 4.

Часть 3 · 15 минут Позиционирование

Как выйти из потока

Пять значений position

staticпо умолчанию, в потоке
relativeсдвиг, место остаётся
absoluteвне потока, от предка
fixedвне потока, от окна
stickyв потоке, пока не прилипнет

Разбираем по порядку — каждое следующее радикальнее предыдущего.

Сразу о частой ошибке

.box {
    top: 40px;      /* написано */
    left: 20px;     /* написано */
}                   /* и ничего не происходит */

top, right, bottom, left работают только у элементов с position, отличным от static.

Половина вопросов «почему не двигается» — про это.

relative: сдвиг без последствий

сосед сверху
position: relative; left: 60px
сосед снизу — стоит, как стоял

элемент уехал визуально, но место в потоке за ним осталось

Настоящее применение relative

В реальной работе его почти никогда не пишут ради сдвига. Пишут ради побочного эффекта: стать системой отсчёта для абсолютно позиционированного потомка.

/* ничего не сдвинул */
.card   { position: relative; }
.badge  { position: absolute; top: 10px; right: 10px; }

Эта пара — самая частая связка во всём CSS. Дальше поймём, почему.

absolute: элемента больше нет в потоке

сосед сверху
сосед снизу — поднялся вплотную

абсолютный элемент исчез из расчёта: соседи схлопнулись, как будто его и не было

Он никуда не делся — просто теперь он в слое над потоком.

От кого считаются координаты

От ближайшего предка, у которого position не static. Если такого нет — от корня документа.

Вот зачем родителю position: relative, который ничего не сдвигает: он объявляет себя точкой отсчёта и не даёт потомку улететь в угол страницы.

Центрирование абсолютом

.modal {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

top: 50% ставит левый верхний угол в центр — это ещё не центрирование. translate(-50%, -50%) отодвигает элемент на половину его собственного размера, и вот теперь центр совпал.

центрируем div: 4

И ещё один, без transform

.modal {
    position: absolute;
    inset: 0;          /* все четыре стороны */
    margin: auto;
    width: 300px;
    height: 200px;
}

Элемент растянут на все стороны, но размер задан точно — и margin: auto делит остаток поровну.

центрируем div: 5

fixed: привязка к окну

Координаты считаются от окна браузера, а не от документа. Элемент не уезжает при прокрутке: липкие шапки, кнопка «наверх», подложка модального окна.

.header { position: fixed; top: 0; left: 0; right: 0; }

Ловушка, на которой теряют вечер

/* где-то выше по дереву */
.wrapper { transform: translateZ(0); }
/* и он больше не fixed */
.header  { position: fixed; top: 0; }

transform, filter или will-change у любого предка создают новый контейнер, и fixed внутри начинает считаться от него, а не от окна.

Услышать об этом заранее дешевле, чем искать самому.

sticky: гибрид

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

thead th {
    position: sticky;
    /* без этой строки не прилипнет ни к чему */
    top: 0;
}

Три причины, почему «не работает»

Третья — самая обидная: всё написано правильно, а эффекта нет, потому что прокручиваться внутри родителя нечему.

Войны числами

z-index

Кто окажется
сверху?

Два блока, два числа

.parent { opacity: 0.99; }
/* внутри .parent */
.a      { position: relative; z-index: 9999; }

/* снаружи */
.b      { position: relative; z-index: 1; }

Девять тысяч против единицы. Голосуем руками.

Проверяем

Сверху окажется .b с единицей. И никакое число в .a этого не изменит — хоть миллион.

z-index сравнивается только между соседями внутри одного контекста наложения. opacity: 0.99 создала родителю свой контекст — и всё, что внутри, поехало вместе с ним одним слоем.

Что создаёт контекст наложения

position+ любой z-index, кроме auto
opacityменьше единицы
transformлюбой, даже нулевой
filterлюбой
will-changeна этих свойствах
isolationisolate — намеренно

Первые пять создают его побочно: человек хотел прозрачность, а получил новый слой.

История одного файла

z-index: 10;шапка
z-index: 100;выпадающее меню
z-index: 9999;модалка
z-index: 99999;уведомление
z-index: 2147483647;тултип

Числа не решают
проблему слоёв Её решает структура: несколько понятных уровней, у каждого — своё имя и свой контекст. isolation: isolate вместо гонки вооружений

Часть 4 · 26 минут Flexbox

Главный инструмент раскладки

Что было по-настоящему больно

У каждой задачи было по три решения, и все три — с оговорками.

Все эти задачи
про одно и то же Расположить группу элементов вдоль линии и выровнять их. Flexbox придуман ровно для этого

Дорога заняла шесть лет

2009первый черновик, синтаксис box
2011второй подход, flexbox
2012третий, нынешний flex
2015поддержка везде

Три несовместимых синтаксиса подряд. Именно поэтому в старых проектах встречается display: -webkit-box — это привет из первого подхода.

Первая строка

.container { display: flex; }
1
2
3

Одно правило на родителе — и три блочных div встали в ряд. Ни float, ни inline-block, ни clearfix.

Главное, что нужно запомнить сразу

На контейнере

  • display: flex
  • flex-direction
  • justify-content
  • align-items
  • flex-wrap / gap

На элементах

  • flex-grow
  • flex-shrink
  • flex-basis
  • flex (сокращение)
  • align-self / order

Половина ошибок новичка — свойство написано не там.

Две оси

flex-direction: row

главная ось — слева направо
поперечная — сверху вниз

flex-direction: column

главная ось — сверху вниз
поперечная — слева направо

Оси меняются местами,
и все свойства
меняют смысл вместе с ними К этому слайду возвращаемся глазами всю оставшуюся часть

Четыре направления

/* по умолчанию: слева направо */
flex-direction: row;
flex-direction: row-reverse;     /* справа налево */
flex-direction: column;          /* сверху вниз */
flex-direction: column-reverse;  /* снизу вверх */

Про reverse — та же оговорка, что будет про order: меняется картинка, но не порядок чтения.

justify-content — вдоль главной оси

justify-content: flex-start;     /* по умолчанию */
justify-content: center;
/* крайние по краям, остальное поровну */
justify-content: space-between;
justify-content: space-around;
justify-content: space-evenly;
логотип
меню

align-items — поперёк главной оси

/* по умолчанию: растянуть на высоту строки */
align-items: stretch;
align-items: flex-start;
align-items: center;
align-items: baseline;   /* по базовой линии текста */
высокий
низкий
средний

По какой оси
уедет содержимое?

Зал обычно делится ровно пополам

Внимательно

.container {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

Элементы встанут по центру по горизонтали или по вертикали?

Проверяем

По вертикали. При column главная ось направлена сверху вниз, а justify-content всегда работает по главной — значит, теперь он центрирует вертикально.

Чтобы отцентрировать такой контейнер по горизонтали, нужен align-items: center. Да, в точности наоборот привычному.

justify — вдоль
align — поперёк Вдоль чего именно — решает flex-direction, а не привычка читать слева направо

И вот оно

.parent {
    display: flex;
    justify-content: center;
    align-items: center;
}
я в центре

Три строки — и по обеим осям. Ради этого половина зала сюда и шла.

центрируем div: 6

Не гадать, а посмотреть

В DevTools рядом с флекс-контейнером стоит значок flex. Нажмите — браузер нарисует поверх страницы обе оси, промежутки и то, как распределилось свободное место.

Там же, в панели Styles, у justify-content и align-items есть картинки вместо списка: видно, что делает каждое значение, ещё до применения.

Путаницу с осями быстрее всего лечит именно этот значок.

gap — расстояние между

.container { display: flex; gap: 20px; }

Одна строка. Промежутки между элементами, но не по краям контейнера — ровно то, что обычно нужно.

А теперь как это делали раньше

До gap

.item {
    margin-right: 20px;
}
.item:last-child {
    margin-right: 0;
}

Сейчас

.list {
    gap: 20px;
}

А при переносе на две строки сверху вешали ещё и отрицательный margin на контейнер.

Перенос

.container {
    display: flex;
    /* по умолчанию nowrap: сжимает, но не переносит */
    flex-wrap: wrap;
}

Без wrap флекс будет сжимать элементы до последнего, лишь бы уместить их в одну строку. Иногда это ровно то, что нужно; чаще — нет.

align-content против align-items

align-items

как элементы стоят внутри своей строки

align-content

как строки целиком распределены в контейнере

Второе имеет смысл, только когда строк больше одной, то есть при flex-wrap: wrap. Без переноса свойство просто ничего не делает — ещё один повод «не работает, а почему, непонятно».

Гибкость: три свойства элемента

flex-growкак делить лишнее место
flex-shrinkкак отнимать при нехватке
flex-basisот чего вести отсчёт

По отдельности их пишут редко. Почти всегда пишут сокращение.

flex: 1 — самая частая строка

.item { flex: 1; }

/* разворачивается в: */
.item {
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: 0%;    /* ← вот это важно */
}

basis: 0 означает «забудь про содержимое, считай с нуля». Отсюда следующий вопрос.

Будут ли они
одинаковой ширины?

Три блока, разный текст

.item { flex: 1; }
<div class="item">Да</div>
<div class="item">Довольно длинная надпись в три слова</div>
<div class="item">Нет</div>

Проверяем

Да, ровно поровну. flex-basis: 0 обнуляет вклад содержимого, и всё доступное место делится на три равные доли.
Да
Довольно длинная надпись
Нет

А вот flex: auto — это basis: auto, и тогда ширины были бы разными, по содержимому.

Ловушка, о которой узнают поздно

/* и внутри длинная ссылка без пробелов */
.sidebar { flex: 1; }

Флекс-элемент по умолчанию не сжимается уже своего содержимого: у него min-width: auto. Длинное слово, широкая картинка или таблица внутри — и блок распирает макет за границы экрана.

Снаружи это выглядит как «появился горизонтальный скролл, а виноватого не видно».

Лечится одной строкой

.sidebar {
    flex: 1;
    /* разрешаем сжиматься ниже содержимого */
    min-width: 0;
}

Для flex-direction: column то же самое, но min-height: 0.

Человек, который слышал про это один раз, экономит себе вечер.

Исключения для одного элемента

.special {
    /* этот — по-своему, остальные как сказал родитель */
    align-self: flex-end;
    /* и первым, хотя в разметке он третий */
    order: -1;
}

align-self перебивает align-items родителя, order — визуальный порядок.

order меняет только картинку

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

Тот же принцип, что :focus в прошлой лекции: внешний вид не должен расходиться со смыслом.

Продолжение — дома

Flexbox Froggy Томаса Парка: двадцать четыре уровня, от justify-content до align-content. Лучший способ закрепить эту часть, и он занимает двадцать минут.

QR-код со ссылкой на игру Flexbox Froggy flexboxfroggy.com Flexbox Froggy

Есть русская версия. Наша игра на прошлом слайде — короткий разогрев по мотивам, оригинал полнее.

Thomas Park, flexboxfroggy.com

Практикум 1: шапка сайта

.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
Логотип
Главная · Курс · Контакты

Четыре строки. В эпоху float это была задача на полчаса.

Практикум 2: ряд карточек

.cards { display: flex; flex-wrap: wrap; gap: 20px; }
/* не уже 240, дальше тянемся и переносимся */
.card  { flex: 1 1 240px; }

Карточки автоматически одинаковой высоты — бесплатно, из-за align-items: stretch по умолчанию. Раньше ради этого писали скрипт, который мерил высоты и выравнивал их вручную.

Практикум 3: футер внизу окна

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
/* забирает весь остаток — футер уезжает вниз */
main   { flex: 1; }

Футер прижат к низу на короткой странице и идёт следом за содержимым на длинной. Раньше у этой задачи было пять конкурирующих решений.

Часть 5 · 14 минут Grid

Когда осей две

Зачем он, если есть Flexbox

Flexbox

одна ось: элементы выстраиваются в линию, а переносы — это следствие, а не замысел

Grid

две оси сразу: строки и колонки задаются заранее, элементы садятся в готовую сетку

Разница не в мощности, а в направлении мысли.

Правило выбора

Содержимое диктует раскладку — берите Flexbox.
Раскладка диктует содержимому — берите Grid.

Меню, панель кнопок, ряд тегов — флекс: сколько элементов пришло, так и встали. Каркас страницы, галерея, календарь — грид: сетка есть до того, как в неё что-то положили.

И они не конкуренты: грид снаружи, флексы внутри ячеек — обычное дело.

2017

Редчайший случай в истории веба

Первый черновик подала Microsoft в 2011-м, реализация вышла в IE10 с префиксами. Спецификацию довели до ума Рейчел Эндрю и Джен Симмонс.

В марте 2017 Grid въехал в Chrome, Firefox и Safari практически одновременно — и пользоваться им можно было в тот же месяц. Такого в вебе почти не бывает.

Рейчел Эндрю — «The New CSS Layout»

Три строки — и сетка есть

.grid {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
    gap: 20px;
}
200px
1fr
200px

fr — доля свободного места

Единица, которой нет больше нигде в CSS. Сначала браузер вычитает всё фиксированное и все gap, а остаток делит между fr пропорционально.

/* 25% / 50% / 25% остатка */
grid-template-columns: 1fr 2fr 1fr;

В отличие от процентов, fr учитывает промежутки сам — и сетка не вылезает за край от того, что 33 % × 3 плюс два gap больше ста.

Повторы и промежутки

/* вместо 1fr 1fr 1fr 1fr */
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 120px);

gap: 20px;          /* сразу по обеим осям */
gap: 20px 40px;     /* между строками / между колонками */

gap здесь тот же самый, что во флексе, — свойство одно на обе раскладки.

Строка, на которой держатся галереи

.gallery {
    display: grid;
    grid-template-columns:
        repeat(auto-fit, minmax(200px, 1fr));
    gap: 20px;
}

Читается как фраза: повторяй колонки, сколько влезет, каждая — не уже двухсот пикселей и не шире равной доли.

Адаптивность
без единого
медиазапроса Сетка сама решает, сколько колонок помещается, и перестраивается при изменении ширины. А что такое медиазапросы — в следующей лекции

И самый короткий способ центрировать

.parent {
    display: grid;
    place-items: center;
}
я тоже в центре

Две строки, обе оси. place-items — это align-items и justify-items разом.

центрируем div: 7

Макет словами

grid-template-areas:
    "hd  hd    hd"
    "nav main  aside"
    "ft  ft    ft";

.header { grid-area: hd; }
.nav    { grid-area: nav; }
hd nav main aside ft

Самый наглядный синтаксис во всём CSS: макет рисуется прямо в стилях, и схему видно, не запуская браузер.

У грида значок свой

Рядом с грид-контейнером в DevTools — значок grid. Он рисует сетку поверх страницы вместе с номерами линий и именами областей.

Это заодно и ответ на то, чего мы сегодня не проходили: размещение по линиям через grid-column проще всего понять, когда эти линии пронумерованы прямо на макете.

Holy Grail

Шапка, футер, три колонки, средняя тянется, боковые фиксированы, все одинаковой высоты. У этой задачи есть собственное имя — «святой Грааль вёрстки».

Ей посвящали статьи в A List Apart. У неё было не меньше десяти решений, и каждое со своей оговоркой.

Было

.wrap { padding: 0 200px; overflow: hidden; }
.col  { position: relative; float: left; }
.main { width: 100%; }
.left { width: 200px; margin-left: -100%; right: 200px; }
.right{ width: 200px; margin-right: -200px; }
.wrap::after { content: ""; display: table; clear: both; }
/* ...и ещё тридцать строк, чтобы починить высоты и IE6 */

Отрицательные отступы, которые никто не мог объяснить.

Стало

.wrap {
    display: grid;
    grid-template-columns: 200px 1fr 200px;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
        "hd  hd    hd"
        "nav main  aside"
        "ft  ft    ft";
    min-height: 100vh;
}

Комментировать нечего. Слайды говорят сами.

Flex или Grid?

Четыре макета, голосуем руками

Разбираем

Меню в шапкефлекс — сколько пунктов, столько и встало
Каркас страницыгрид — сетка задумана заранее
Теги под статьёйфлекс — переносятся по содержимому
Галерея плиткойгрид — ровные ряды и колонки

Если сомневаетесь — берите флекс. Он прощает больше.

Чего сегодня не было

Всё это есть и всё это пригодится. Цель части скромнее: понимать, зачем грид, и читать чужой грид-код без паники.

И грядки — тоже дома

Grid Garden того же автора: двадцать восемь уровней, поливаем морковь, задавая колонки и строки.

QR-код со ссылкой на игру Grid Garden cssgridgarden.com Grid Garden

Там как раз подробно про линии и grid-column, которые мы сегодня пропустили.

Thomas Park, cssgridgarden.com

Часть 6 · 5 минут Как отцентрировать
div

Долг с прошлой лекции

Вероятно, самый
часто задаваемый вопрос
в истории веба В прошлый раз мы ответили только про горизонталь и обещали вернуться. Возвращаемся

За лекцию накопилось семь

1margin: 0 auto X
2text-align: center + inline-block X
3display: table-cell + vertical-align X Y
4absolute + translate(-50%, -50%) X Y
5absolute + inset: 0 + margin: auto X Y
6flex + justify-content + align-items X Y
7grid + place-items: center X Y

Какой когда

margin: 0 autoколонка текста по горизонтали — до сих пор лучший выбор
flexчаще всего: контейнер уже флекс, добавить две строки
grid + place-itemsкогда нужен просто «центр» и ничего больше
absolute + transformкогда элемент обязан быть вне потока: модалка, тултип

Остальные три — для чтения чужого кода.

place-items: center Честный ответ на вопрос. Две строки, обе оси, работает везде

Но выживают не все

В демо был тумблер «содержимое стало больше». Способ с фиксированными width и height сразу разваливается — и ровно поэтому способов семь, а не один.

Заодно это ответ на вопрос «почему в CSS всё так сложно». Не сложно — просто язык тащит на себе двадцать пять лет совместимости.

Долг закрыт

А тем, кто до сих пор не нашёл все пасхалки на стартовой странице курса: там есть команда center a div.

Теперь вы хотя бы знаете, что искать.

центрируем div: 7

Часть 7 · 4 минутыЧто дальше

И лабораторная

Что освоили

Сначала поток.
Потом Flexbox или Grid.
И только если иначе никак — позиционирование Тот, кто начинает вёрстку с position: absolute, потом воюет числами в z-index

ЛР 2: CSS

По итогам лекций 2 и 3. Берём свою страницу из ЛР 1 — ту самую, с валидной разметкой и видом документа из девяносто шестого, — и верстаем её по-настоящему.

Требования и срок — в задании. Разметку из ЛР 1 менять можно, но семантика должна остаться семантикой.

Что почитать

CSS Tricks«A Complete Guide to Flexbox» и «…to Grid»
MDNподробно и точно, по каждому свойству
Докапо-русски и человеческим языком
QR-код со ссылкой на раздел CSS справочника «Дока» doka.guide/css Справочник по CSS

Лекция 4. Современный CSS

Сетка уже умеет перестраиваться сама. Дальше научим её делать это осмысленно.

Вопросы?

Слайды, демо и обе игры — в репозитории курса