Grid: дорожки, fr и области страницы

стили≈ 10 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Страница редко бывает одним рядом или одной колонкой. Шапка тянется на всю ширину, под ней рядом стоят меню и статья, внизу — подвал: раскладка идёт сразу по двум осям, и ряд здесь не собрать из одних только flex. Для двумерной раскладки в CSS есть grid: контейнер сначала размечает себя на колонки и ряды, а потом расставляет детей по клеткам. Посчитаем, сколько достаётся доле fr, поручим браузеру самому решить, сколько колонок влезет, и нарисуем страницу картой из слов. А в конце проверим, что эта карта сделала с порядком, в котором страницу читают.

Разделы темы:·

Когда flex делит свободное место, размер начинается с самих элементов: у каждого есть база, а остаток соседи делят по долям. Grid заходит с другой стороны — от контейнера. Свойство display: grid делает элемент grid-контейнером, а grid-template-columns размечает его на колонки: сколько значений через пробел, столько колонок, и каждое значение — ширина своей. Колонку или ряд сетки называют дорожкой. Прямые дети контейнера занимают клетки по порядку: слева направо, ряд за рядом, — как слова в тексте.

Ряды размечают так же, свойством grid-template-rows, но чаще их не задают вовсе. Тогда браузер сам добавляет ряд, когда детям не хватило клеток, а высоту ряду даёт содержимое. Промежутки между дорожками, как и во flex, задаёт gap — сразу между колонками и между рядами. Grid — Baseline widely с 17 апреля 2020 года: пишите смело.

В сетке две колонки, 100px и 60px, промежуток 10px, а детей трое, все высотой 20px. Ширина у детей не задана. Прогноз: три строки — x, y и ширины трёх коробок.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Первые две коробки заняли первый ряд: вторая стоит на 110px, за первой колонкой и промежутком. Третьей клетки в ряду нет, и браузер начал второй ряд: 20px высоты первого ряда и 10px промежутка — третья коробка на 30px. А ширины 100, 60 и 100 пришли не от коробок, а от колонок: элемент сетки по умолчанию растягивается на всю свою клетку.

Во flex размер начинается с элемента, и соседи делят остаток. В grid размер начинается с дорожки, а элемент заполняет свою клетку.

Кроме пикселей, ширину дорожки пишут в fr — от английского fraction, доля. Браузер считает её в два шага, и порядок шагов здесь важен. Сначала он вычитает из ширины контейнера все дорожки с явным размером и все промежутки. Потом делит остаток по числам перед fr: у 2fr две доли, у 1fr одна. Знакомая логика — flex делает то же самое, только отсчёт ведёт от баз элементов, а не от дорожек контейнера.

Сетка шириной 240px, промежуток 15px, три колонки: 60px 1fr 2fr. Прежде чем запускать, заполните последний столбец таблицы на бумаге.

КолонкаРазмерИтоговая ширина
первая60px?
вторая1fr?
третья2fr?

Прогноз — три ширины через запятую, в порядке разметки. Считать их нужно в два шага: сначала вычесть дорожку в пикселях и промежуток, потом поделить остаток на три доли.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Промежутков между тремя колонками два, и вместе с первой колонкой они занимают 60 + 2 × 15 = 90px. Остаётся 150px, долей три, одна доля — 50px. Колонка 1fr вышла уже соседней 60px: fr — не «большая резиновая колонка», а доля остатка, и остаток бывает маленьким.

Во flex рост 2 не делал элемент вдвое шире соседа: доля прибавлялась к базе. Здесь 2fr ровно вдвое шире 1fr: доли делят остаток с нуля, как элементы с flex: 1, у которых база нулевая. Одинаковые колонки пишут короче: repeat(3, 1fr) — то же, что 1fr 1fr 1fr.

Ровно вдвое — пока содержимое помещается. Функция minmax(наименьший, наибольший) задаёт дорожке пределы размера. Ниже две сетки по 200px, в первой колонке каждой лежит полоса шириной 150px. У первой сетки колонки 1fr 1fr, у второй — minmax(0, 1fr) minmax(0, 1fr). Прогноз: четыре ширины — две колонки первой сетки, потом две колонки второй.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Первая сетка разделилась не поровну: 150 и 50. Запись 1fr — сокращение от minmax(auto, 1fr), а auto в нижнем пределе значит «не уже содержимого», как min-width: auto у flex-элемента. Полоса не дала первой колонке сжаться до 100px, и вторая получила то, что осталось. minmax(0, 1fr) опускает нижний предел до нуля: колонки снова по 100px, а полоса вылезает за край своей колонки и прячется под соседнюю — на сцене её хвоста не видно.

Колонки 1fr вышли неравными — внутри есть что-то шире доли: длинное слово, картинка, таблица. Пишите minmax(0, 1fr), а само содержимое переносите или прокручивайте внутри колонки.

Каталог карточек: карточка не уже 50px, колонок — сколько влезет, и пусть они делят ширину поровну. Число колонок заранее неизвестно: на телефоне их две, на широком экране семь. Поэтому вместо числа в repeat пишут auto-fill: repeat(auto-fill, minmax(50px, 1fr)). Браузер снова считает в два шага — сначала сколько колонок наименьшего размера влезет в контейнер вместе с промежутками, потом эти колонки делят всю ширину как 1fr.

В примере одно и то же правило у двух сеток: широкой, 220px, и узкой, 130px. Промежуток 20px, в каждой по четыре карточки. Прогноз: x карточек каждой сетки — по ним видно, сколько вышло колонок.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Широкая сетка. Три колонки по минимуму занимают 3 × 50 + 2 × 20 = 190px и влезают, четырём понадобилось бы 260px. Колонок три, каждая (220 − 40) / 3 = 60px: карточки стоят на 0, 80 и 160, а четвёртая уходит во второй ряд. В узкую сетку три колонки не влезают, 190 больше 130, а две занимают 2 × 50 + 20 = 120px и влезают. Каждая по (130 − 20) / 2 = 55px, карточки стоят на 0 и 75, по две в ряд.

Здесь спрятан приём, который экономит медиазапросы: одна строка repeat(auto-fill, minmax(минимум, 1fr)) даёт сетку, которая сама перестраивается под ширину. На телефоне колонок две, на планшете четыре, на широком экране семь — и ни одного @media в стилях. Медиазапросы остаются для тех случаев, когда на разных ширинах меняется сама раскладка, а не число одинаковых карточек.

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

У auto-fill есть близнец — auto-fit. Разница видна, когда карточек меньше, чем влезло колонок: auto-fill оставляет пустые колонки на месте, а auto-fit схлопывает их, и карточки растягиваются на всю ширину. Посчитаете это во второй задаче.

Теперь страница целиком. В разметке она уже поделена на ориентиры страницы: <header>, <nav>, <main>, <footer>. Grid умеет расставить их по карте, нарисованной словами. Свойство контейнера grid-template-areas получает несколько строк в кавычках: каждая строка — ряд сетки, каждое слово — клетка. Одно имя в соседних клетках сливает их в одну область. Элемент отправляют в область свойством grid-area с её именем.

Сетка шириной 240px, колонки 80px 1fr, карта из трёх рядов: шапка на две клетки, меню и основная часть рядом, подвал на две клетки. Ряды не размечены, их высоту решает содержимое: у шапки 30px, у меню 50px, у основной части 60px, у подвала 20px. Прогноз: y четырёх детей по порядку, потом ширины меню и основной части.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
Меню и основная часть делят второй ряд и стоят на одном y, 30px. Высоту ряду даёт самый высокий из них, 60px, поэтому подвал встал на 90, а не на 80: под меню осталась полоса в 10px. Ширины пришли от колонок: 80px и 240 − 80 = 160px. Карту пишут ряд под рядом и выравнивают имена лишними пробелами: браузеру пробелы не мешают, а прямо в стилях видно, как области лягут на экране.

Главная выгода карты — её видно. grid-template-areas читается как набросок страницы: строки в кавычках повторяют ряды, слова повторяют блоки, и правка раскладки становится правкой рисунка, а не арифметики. Перенести меню справа налево — значит поменять слова местами в двух строках; ни один номер колонки при этом пересчитывать не нужно. Раскладка, которую можно прочитать глазами, реже расходится с тем, что хотел дизайнер.

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

Бывает, что внутри области лежит своя сетка, и её колонки должны встать ровно по колонкам страницы. Тогда вложенной сетке пишут grid-template-columns: subgrid: она не размечает свои дорожки, а берёт у родительской те, на которых стоит сама. Subgrid — Baseline widely с 15 марта 2026 года.

Карту на живой странице показывают инструменты разработчика. В Chrome во вкладке Elements рядом с grid-контейнером стоит значок grid: щелчок рисует поверх страницы дорожки и промежутки, а в панели Layout можно включить номера линий — границ между дорожками — и имена областей. В Firefox то же самое — во вкладке Inspector, в панели Layout, раздел Grid. Прибор такого не рисует, это ручной шаг.

Карта ставит элементы по именам, поэтому в разметке они могут стоять в любом порядке. Здесь основная часть со словом «Статья» записана первой, а шапка — второй; сетка и карта при этом те же. Проверка отвечает двумя строками: порядок в разметке и порядок на экране. Прогноз: совпадут ли они?

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера
разбор вывода
На экране привычная страница: шапка, меню, статья, подвал. Но клавиша Tab и программа чтения с экрана идут по разметке, и для них страница начинается со статьи. Это то же расхождение, что у order во flex, только спрятано оно глубже: там свойство стоит у самого элемента, а здесь порядок разошёлся из-за карты у контейнера.

Правило то же: порядок в разметке — это порядок смысла, а карта решает только, где что нарисовать. Если шапка по смыслу первая, она первая и в разметке. Есть свойство reading-flow: оно велит фокусу идти по сетке так, как её видно. Но оно есть только в Chrome и Edge, а в Firefox и Safari не сработает — это Baseline limited, не рассчитывайте на него. Обход Tab прибор не проверит: соберите такую же страницу со ссылкой в каждой области, сохраните её в файл, откройте в браузере и понажимайте Tab сами (в Safari по ссылкам ходит Option+Tab).

  • display: grid размечает контейнер на дорожки: grid-template-columns задаёт колонки, gap — промежутки между дорожками, а ряды браузер добавляет сам, и высоту им даёт содержимое. Размер задаёт дорожка, а элемент растягивается на свою клетку. Grid — Baseline widely с апреля 2020 года.
  • fr — доля остатка: из ширины сетки вычитаются фиксированные дорожки и все промежутки, остаток делится по числам перед fr. Колонка 1fr может выйти уже фиксированной.
  • 1fr — это minmax(auto, 1fr): колонка не становится уже содержимого. Разъехавшиеся колонки чинит minmax(0, 1fr).
  • repeat(auto-fill, minmax(50px, 1fr)) считает, сколько колонок по минимуму влезет с промежутками, и делит между ними всю ширину. auto-fit вдобавок схлопывает пустые колонки.
  • grid-template-areas рисует страницу картой: строка — ряд, слово — клетка, одно имя в соседних клетках — область-прямоугольник, и элемент встаёт в неё по grid-area. Вложенная сетка с subgrid берёт дорожки родителя, это Baseline widely с марта 2026 года.
  • Карта меняет картинку, а Tab и программа чтения идут по разметке: порядок смысла задают в разметке. Сетку на живой странице показывает значок grid в инструментах разработчика.

1/4Сетка шириной 240px, gap: 10px, колонки 40px 1fr 1fr. Какой ширины колонки?

2/4Сетка шириной 250px, gap: 10px, колонки repeat(auto-fill, minmax(100px, 1fr)), в ней пять карточек. Сколько выйдет колонок и какой ширины?

3/4В карте "head head" "nav main" "nav nav" имя nav стоит буквой Г. Что сделает браузер?

4/4Статья стоит в разметке раньше шапки, а карта рисует шапку наверху. Как сделать, чтобы и Tab начинал с шапки?

Три колонки вместо четырёх

В сетке 230px с промежутком 10px пять карточек, наименьшая ширина колонки — 50px. Сначала предскажите x пяти карточек. Потом поменяйте только наименьший размер в minmax, чтобы колонок стало три. Какие значения подходят?

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Сейчас колонок четыре: 4 × 50 + 3 × 10 = 230px — ровно ширина сетки, и каждая колонка ровно 50px. Вывод — .grid div → x: 0px, 60px, 120px, 180px, 0px, пятая карточка ушла во второй ряд.

Чтобы колонок стало три, четыре не должны влезать, а три должны. Четыре не влезут, когда 4 × минимум + 30 больше 230, то есть при минимуме больше 50px. Три влезают, пока 3 × минимум + 20 не больше 230, то есть до 70px включительно. Подходит любое значение больше 50px и не больше 70px, например:

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Прибор напечатает .grid div → x: 0px, 80px, 160px, 0px, 80px: три колонки по (230 − 20) / 3 = 70px. При 51px и при 70px вывод тот же, а при 71px влезут только две колонки, по 110px.

Две карточки на всю ширину

В каталоге пока две карточки, и они жмутся влево, а справа пусто. Предскажите две ширины. Потом поменяйте в правиле одно слово, чтобы карточки поделили всю ширину сетки, а минимум 50px остался. Подсказка — близнец auto-fill из третьего раздела.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Вывод — .grid div → width: 60px, 60px. По минимуму влезают три колонки: 3 × 50 + 2 × 20 = 190px. auto-fill держит все три, даже пустую: каждая (220 − 40) / 3 = 60px, а третья колонка вместе с промежутком перед ней остаётся пустым местом справа.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Теперь .grid div → width: 100px, 100px. auto-fit считает колонки так же, но пустые схлопывает до нуля вместе с их промежутками. Остаются две колонки и один промежуток: (220 − 20) / 2 = 100px. Когда карточек хватает на все колонки, пустых дорожек нет, и два слова ведут себя одинаково.

Меню справа

Перенесите меню к правому краю: основная часть слева, меню справа, той же ширины 80px. Разметку не трогайте, меняйте только правило .page. Сначала предскажите, что пример печатает сейчас.

js
разметка страницы
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Сейчас пример печатает nav, main → x: 0px, 80px и nav, main → width: 80px, 160px. Поменять нужно и карту, и колонки: карта говорит, какая область в какой клетке, а колонки — какой ширины клетка.

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Прибор напечатает nav, main → x: 160px, 0px и прежние ширины, 80px и 160px. Если поменять только карту, меню встанет справа, но ширину 80px получит основная часть, а меню — 160px: nav, main → width: 160px, 80px.

В разметке <nav> по-прежнему стоит перед <main>, поэтому Tab сначала пройдёт по меню, хотя на экране оно справа. Если по смыслу первой должна идти статья, её переставляют в разметке, а не на карте.