Grid: редове и колони
Накратко: CSS Grid подрежда елементите в редове и колони наведнъж: колони с fr и gap, каталог с repeat(auto-fill, minmax()) и подредба на страницата с grid-template-areas.
Проблем
Последният продукт е огромен
Каталогът е с flex-wrap от миналия урок. Четири продукта: на първия ред се събират три, а четвъртият е сам на втория ред и е широк колкото целия ред.
Конзола
Това не е грешка във flexbox. Всеки ред на flex контейнер дели своето свободно място сам и не знае нищо за реда над него. На втория ред има един продукт, затова той взима всичко: 768px. Нужно е нещо, което чертае колоните веднъж за целия каталог.
Идеята
Редове и колони
Grid е подредба в две измерения: контейнерът чертае колони и редове, а елементите влизат в клетките една по една, ред по ред.
.catalog { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 24px;}display: gridправи елемента grid контейнер, а децата му са grid елементи.grid-template-columnsказва колко колони има и колко е широка всяка:200px 1frе колона от 200px и колона, която взима останалото.frе дял от свободното място, след като се извадят колоните с размер и gap-овете.1fr 2fr 1frе 1 : 2 : 1.repeat(3, 1fr)е същото като1fr 1fr 1fr.gapе разстоянието между колоните и между редовете.
Разликата с flexbox: при flex елементите решават колко са широки и всеки ред е отделен. При grid контейнерът решава колоните, а елементите само ги заемат. Затова последният продукт остава в своята колона.
Пробвай
Три колони
Направи каталога grid с три еднакви колони и gap 24px. Каталогът е до 720px, затова всяка карта, и последната, трябва да е (720 − 2 × 24) : 3 = 224px.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Каталог</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Каталог</h1>
<div class="catalog">
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Бял хляб</h2>
<p>1,20 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Пълнозърнест хляб</h2>
<p>1,90 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Прясно мляко</h2>
<p>1,60 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Кисело мляко</h2>
<p>1,10 €</p>
</article>
<article class="card">
<img src="img/cheese.svg" alt="" width="48" height="48">
<h2>Сирене</h2>
<p>7,90 €</p>
</article>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.catalog {
max-width: 720px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 24px;
}
.card {
flex: 1 1 200px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 8px 0 4px; font-size: 1rem; }
.card p { margin: 0; }
display: grid и grid-template-columns: 1fr 1fr 1fr (или repeat(3, 1fr)). gap: 24px вече е там и при grid важи между колоните и между редовете. flex: 1 1 200px на картите вече не прави нищо: те не са flex елементи. 1fr 2fr 1fr дава три колони, но средната е двойна.
Идеята
Колкото се съберат
Три колони са добре на компютър. На телефон са по 100px и продуктът не се чете. Не искаме да казваме броя, а правило: „колони от поне 200px, колкото се съберат“.
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));minmax(200px, 1fr): колоната е най-малко 200px и най-много един дял.auto-fillвместо число: браузърът слага колкото колони от най-малката ширина се събират, заедно с gap-овете.- После остатъкът се дели поравно (
1fr) и колоните растат до края на реда.
Виж го
Виж как се брои
Едно и също правило в каталог от 960, 680 и 328px.
.catalog { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px;}Стъпка по стъпка, с думи
- Каталогът е grid. Колоните не са дадени с брой, а с правило: колкото се съберат, всяка поне 200px.
- Каталог от 960px. Браузърът слага колони по 200px с gap 16px, докато се събират: 4 × 200 + 3 × 16 = 848px. Петата би стигнала 1064px: не се събира.
- Остават 960 − 848 = 112px. 1fr ги дели поравно: всяка колона е (960 − 3 × 16) : 4 = 228px.
- На 680px се събират три колони (3 × 200 + 2 × 16 = 632). Четвъртата би искала 848. Всяка става (680 − 32) : 3 = 216px.
- На 328px, телефон, се събира една: две биха искали 416px. Тя взима цялата ширина: 328px.
- Едно правило, три подредби. Броят на колоните следва ширината на каталога, без медийни заявки. Продуктите пълнят колоните ред по ред.
Същият каталог като страница, първо на телефон (360px), после на компютър (1000px). Кодът е един и същ.
Конзола
Конзола
Колоните зависят от ширината на каталога, а не на екрана. Каталог в тясна колона на страницата има по-малко колони и на голям екран.
Пробвай
Едно правило за двете места
Страницата има каталог и тясна колона „Купиха също“ (220px). И двете са .catalog с flex-wrap и последният продукт в каталога е твърде широк. Смени правилото за .catalog: grid, колони от поне 200px, колкото се съберат, разтегнати до края. Проверката очаква 3 колони в каталога, всяка по 208px, и 1 колона от 220px вдясно.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Каталог</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<main>
<h1>Каталог</h1>
<div class="catalog">
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Бял хляб</h2>
<p>1,20 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Пълнозърнест хляб</h2>
<p>1,90 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Прясно мляко</h2>
<p>1,60 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Кисело мляко</h2>
<p>1,10 €</p>
</article>
<article class="card">
<img src="img/cheese.svg" alt="" width="48" height="48">
<h2>Сирене</h2>
<p>7,90 €</p>
</article>
</div>
</main>
<aside>
<h2>Купиха също</h2>
<div class="catalog">
<article class="card">
<img src="img/product.svg" alt="" width="48" height="48">
<h2>Масло</h2>
<p>3,20 €</p>
</article>
<article class="card">
<img src="img/tomatoes.svg" alt="" width="48" height="48">
<h2>Домати</h2>
<p>3,60 €</p>
</article>
</div>
</aside>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.page {
max-width: 900px;
display: grid;
grid-template-columns: 1fr 220px;
gap: 24px;
}
aside h2 { margin: 0 0 12px; font-size: 1.1rem; }
.catalog {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.card {
flex: 1 1 200px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 8px 0 4px; font-size: 1rem; }
.card p { margin: 0; }
display: grid и grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)). Каталогът е 900 − 220 − 24 = 656px: събират се три колони (632px), а остатъкът ги прави по 208px. В колоната от 220px се събира една и тя става 220px. repeat(3, 1fr) прави три колони и в тясната колона, където картите са смачкани. С repeat(auto-fill, 200px) колоните не растат и вдясно остават 20px празни.
Идеята
Области на страницата
Grid нарежда и самата страница: шапка, филтри вляво, каталог, долна част. Областите могат да се нарисуват с имена:
.page { display: grid; grid-template-columns: 220px 1fr; grid-template-areas: "header header" "filters main" "footer footer"; gap: 16px;}header { grid-area: header; }.filters { grid-area: filters; }- Всеки ред в кавички е ред на грида, а всяка дума е клетка. Една дума в две клетки една до друга прави област, която ги заема: шапката е на две колони.
grid-area: headerслага елемента в областта с това име.- Всеки ред трябва да има толкова клетки, колкото са колоните, а областта трябва да е правоъгълник.
Без имена елемент се поставя с номерата на линиите между колоните: grid-column: 1 / 3 е от първата до третата линия (две колони), а grid-column: 1 / -1 е до последната линия. grid-column: span 2 заема две колони там, където браузърът го сложи.
Пробвай
Страницата
Подреди страницата (900px) с grid-template-areas: горе шапката на цялата ширина, под нея филтрите вляво (220px) и каталогът вдясно, долу долната част на цялата ширина. Разстоянието е 16px.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Каталог</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header>
<a href="/">Магазинът</a>
</header>
<aside class="filters">
<h2>Филтри</h2>
<label><input type="checkbox"> Само наличните</label>
<label><input type="checkbox"> До 5 €</label>
</aside>
<main>
<h1>Каталог</h1>
<p>Бял хляб, Пълнозърнест хляб, Прясно мляко, Кисело мляко, Сирене, Ябълки</p>
</main>
<footer>Доставка до 2 работни дни</footer>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.page {
max-width: 900px;
display: grid;
grid-template-columns: 220px 1fr;
grid-template-areas:
"header header"
"filters main"
"footer footer";
gap: 16px;
}
header { grid-area: header; }
.filters { grid-area: filters; }
main { grid-area: main; }
footer { grid-area: footer; }
header, footer { padding: 12px 16px; background: #f3efe6; }
.filters { padding: 12px; border: 1px solid #ddd; border-radius: 8px; }
.filters h2 { margin: 0 0 8px; font-size: 1.1rem; }
.filters label { display: block; }
Две колони, 220px 1fr, и три реда области: "header header", "filters main", "footer footer". Всеки от четирите елемента получава grid-area със своето име. Каталогът става 900 − 220 − 16 = 664px. Елемент без grid-area не влиза в никоя област: браузърът го слага сам в първата свободна клетка. Шапката без grid-area отива горе вляво и е широка 220px.
Пробвай
Намери грешката
Страницата се разпадна: шапката, филтрите, каталогът и долната част са една върху друга. В конзолата няма грешка. Кой ред е причината?
Избери реда с грешката.
На ред 7 има една клетка, а колоните са две. Такава стойност е невалидна и браузърът пропуска цялото grid-template-areas, не само този ред. Тогава области с имената header, filters, main и footer няма, а четирите елемента с grid-area отиват на едно и също място извън колоните, един върху друг. Трябва "footer footer". В инструментите за разработчици (F12) свойството е зачертано.
По-точно, за любопитните
Неявни редове. grid-template-columns и grid-template-rows описват явния грид. Ако елементите са повече, браузърът добавя редове сам. Те са неявни и са auto, колкото съдържанието. Височината им се дава с grid-auto-rows.
auto-fill и auto-fit. Разликата се вижда, когато елементите са по-малко от колоните. В каталог от 960px с два продукта auto-fill пази четирите колони и продуктите са по 228px. auto-fit маха празните колони и двата продукта стават по 472px.
fr и съдържанието. 1fr е minmax(auto, 1fr): колоната не става по-тясна от най-дългата дума в нея. Ако една дълга дума разтяга колоната, minmax(0, 1fr) го спира.
Grid или flex. Flexbox е за една ос: шапка, ред бутони, ред в кошницата. Grid е за две: каталог, страница, табло. Често са заедно: страницата е grid, а шапката в нея е flex.
Още. grid-auto-flow: dense попълва празните клетки с по-късни елементи, но разбърква реда на екрана спрямо дървото. subgrid пуска вложен грид да ползва колоните на родителя.
Упражнение
Продуктът на седмицата
Направи каталога (900px) grid: колони от поне 200px, колкото се съберат, gap 16px. Продуктът на седмицата е първи и заема две колони. Всички редове са високи 160px, и тези, които браузърът добавя сам. Проверката очаква 4 колони по 213px, продукта на седмицата 442px широк и последния продукт 160px висок.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Каталог</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Каталог</h1>
<div class="catalog">
<article class="card featured">
<p class="badge">Продукт на седмицата</p>
<h2>Кашкавал</h2>
<p>9,50 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Бял хляб</h2>
<p>1,20 €</p>
</article>
<article class="card">
<img src="img/bread.svg" alt="" width="48" height="48">
<h2>Пълнозърнест хляб</h2>
<p>1,90 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Прясно мляко</h2>
<p>1,60 €</p>
</article>
<article class="card">
<img src="img/milk.svg" alt="" width="48" height="48">
<h2>Кисело мляко</h2>
<p>1,10 €</p>
</article>
<article class="card">
<img src="img/cheese.svg" alt="" width="48" height="48">
<h2>Сирене</h2>
<p>7,90 €</p>
</article>
<article class="card">
<img src="img/apples.svg" alt="" width="48" height="48">
<h2>Ябълки</h2>
<p>2,10 €</p>
</article>
</div>
</body>
</html>
style.css* { box-sizing: border-box; }
body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; }
h1 { margin: 0 0 12px; font-size: 1.4rem; }
.catalog {
max-width: 900px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 160px;
gap: 16px;
}
.card {
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.card h2 { margin: 8px 0 4px; font-size: 1rem; }
.card p { margin: 0; }
.featured {
grid-column: span 2;
background: #fff4e0;
}
.badge { color: #c2531a; font-weight: 700; }
.catalog { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: 160px; gap: 16px;}.featured { grid-column: span 2; }Четири колони по (900 − 48) : 4 = 213px. Продуктът на седмицата е 2 × 213 + 16 = 442px, защото взима и gap-а между двете колони. Той и шест продукта заемат осем клетки: два реда. grid-template-rows: 160px дава 160px само на първия ред, а вторият е неявен и е висок колкото съдържанието.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: HTML документът · Селектори и каскада · Box model · Flexbox: ред и колона
- Продължава в: Responsive дизайн · Витрината на магазина
- Задачи по темата: CSS: как изглежда страницата