🔍 Начните печатать, чтобы искать по книге или перейти к нужной странице по номеру
Удобно листать не только прокруткой, но и клавишами‑стрелками:
Эта книга — пошаговая инструкция по вёрстке сайтов на языках ХТМЛ и ЦСС. Вы узнаете не только как сверстать сайт, но и как опубликовать его в интернете, настроить красивый шаринг в соцсети и подключить системы аналитики.
Отдельный раздел книги посвящён работе с верстальщиком: как ставить задачи, получать предсказуемый результат и правильно принимать и внедрять вёрстку.
Знакомство с ХТМЛ и ЦСС
Модули
Расстановка
Страницы
Спецэффекты
Контроль качества
Публикация
Как работать с верстальщиком
Эта книга — пошаговая инструкция по вёрстке сайтов на языках ХТМЛ и ЦСС. Вы узнаете не только как сверстать сайт, но и как опубликовать его в интернете, настроить красивый шаринг в соцсети и подключить системы аналитики.
Отдельный раздел книги посвящён работе с верстальщиком: как ставить задачи, получать предсказуемый результат и правильно принимать и внедрять вёрстку.
Знакомство с ХТМЛ и ЦСС
Модули
Расстановка
Страницы
Спецэффекты
Контроль качества
Публикация
Как работать с верстальщиком
Мобильная версия «в лоб» даёт дубли — повторяющиеся этажи с одинаковой раскладкой. Чтобы избавиться от этого, введём модификаторы ширины для мобильной версии:
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
min-height: 33vh;
overflow: hidden;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.l,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}
И расставим их, чередуя ритм один проект, два проекта:
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeL"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
</section>
</div>
Мобильная версия «в лоб» даёт дубли — повторяющиеся этажи с одинаковой раскладкой. Чтобы избавиться от этого, введём модификаторы ширины для мобильной версии:
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
min-height: 33vh;
overflow: hidden;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.l,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}
И расставим их, чередуя ритм один проект, два проекта:
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeM mobileSizeL"></div>
<div class="project sizeM mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeS mobileSizeL"></div>
<div class="project sizeS mobileSizeS"></div>
<div class="project sizeS mobileSizeS"></div>
</section>
</div>
Чтобы не расставлять классы мобильной раскладки вручную, автоматизируем их назначение с помощью небольшого скрипта.
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
</section>
</div>
<script>
const MOBILE_LAYOUT = ['L', 'S', 'S'] // Вариации, которые будем чередовать на мобильных
const projects = document.querySelectorAll('.project') // Находим все проекты на странице
projects.forEach((project, projectIndex) => {
const layoutClass = MOBILE_LAYOUT[projectIndex % MOBILE_LAYOUT.length] // Последовательно выбираем текущую вариацию: L → S → S → L → S → S и так далее
const mobileClass = `mobileSize${layoutClass}` // Собираем правильный класс
project.classList.add(mobileClass) // И добавляем его проекту
})
</script>Чтобы не расставлять классы мобильной раскладки вручную, автоматизируем их назначение с помощью небольшого скрипта.
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeM"></div>
<div class="project sizeM"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
<div class="project sizeS"></div>
</section>
</div>
<script>
const MOBILE_LAYOUT = ['L', 'S', 'S'] // Вариации, которые будем чередовать на мобильных
const projects = document.querySelectorAll('.project') // Находим все проекты на странице
projects.forEach((project, projectIndex) => {
const layoutClass = MOBILE_LAYOUT[projectIndex % MOBILE_LAYOUT.length] // Последовательно выбираем текущую вариацию: L → S → S → L → S → S и так далее
const mobileClass = `mobileSize${layoutClass}` // Собираем правильный класс
project.classList.add(mobileClass) // И добавляем его проекту
})
</script>Когда раскладка готова, заполним ячейки карточками проектов. Каждая карточка — это ссылка, в которую вложена иллюстрация и подпись проекта. Отразим это в разметке:
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL">
<a href="/projects/maximilyahov-identity/"> <!-- Каждому проекту — отдельную страницу -->
<div class="project-media"> <!-- Контейнер для иллюстрации проекта — картинки, гифки или видео -->
<img src="/projects/maximilyahov-identity/files/cover.jpg"> <!-- Храним файлы, связанные с проектом, рядом с самим проектом -->
</div>
<div class="project-caption"> <!-- Считаем, что стили для подписей и базовая типографика уже есть -->
Максим Ильяхов десять лет учит интернет писать. Теперь у него есть фирстиль
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-font/">
<div class="project-media">
<img src="/projects/worldchess-font/files/cover.jpg">
</div>
<div class="project-caption">
Шрифт для цен Ворлд Чесс
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/worldchess-2/">
<div class="project-media">
<img src="/projects/worldchess-2/files/cover.jpg">
</div>
<div class="project-caption">
Вторая версия главной страницы Ворлд Чесса
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-broadcasts/">
<div class="project-media">
<img src="/projects/worldchess-broadcasts/files/cover.jpg">
</div>
<div class="project-caption">
Ворлд Чесс ведёт трансляции шахматных турниров
</div>
</a>
</div>
</section>
</div>
Уберём минимальную высоту ячеек, чтобы их высота определялась содержимым, и стилизуем карточки:
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
min-height: 33vh;
overflow: hidden;
}
.project-media {
margin: -10px -10px 0; /* Вытягиваем иллюстрацию проекта на поля ячейки */
aspect-ratio: 1.91 / 1; /* Резервируем место под картинки, опираясь на их пропорции, чтобы страница не дёргалась при загрузке */
}
.project-caption { /* Настраиваем подписи */
margin-top: .5em;
font-size: 12px;
line-height: 1.2;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.sizeL,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}Когда раскладка готова, заполним ячейки карточками проектов. Каждая карточка — это ссылка, в которую вложена иллюстрация и подпись проекта. Отразим это в разметке:
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL">
<a href="/projects/maximilyahov-identity/"> <!-- Каждому проекту — отдельную страницу -->
<div class="project-media"> <!-- Контейнер для иллюстрации проекта — картинки, гифки или видео -->
<img src="/projects/maximilyahov-identity/files/cover.jpg"> <!-- Храним файлы, связанные с проектом, рядом с самим проектом -->
</div>
<div class="project-caption"> <!-- Считаем, что стили для подписей и базовая типографика уже есть -->
Максим Ильяхов десять лет учит интернет писать. Теперь у него есть фирстиль
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-font/">
<div class="project-media">
<img src="/projects/worldchess-font/files/cover.jpg">
</div>
<div class="project-caption">
Шрифт для цен Ворлд Чесс
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/worldchess-2/">
<div class="project-media">
<img src="/projects/worldchess-2/files/cover.jpg">
</div>
<div class="project-caption">
Вторая версия главной страницы Ворлд Чесса
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-broadcasts/">
<div class="project-media">
<img src="/projects/worldchess-broadcasts/files/cover.jpg">
</div>
<div class="project-caption">
Ворлд Чесс ведёт трансляции шахматных турниров
</div>
</a>
</div>
</section>
</div>
Уберём минимальную высоту ячеек, чтобы их высота определялась содержимым, и стилизуем карточки:
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
min-height: 33vh;
overflow: hidden;
}
.project-media {
margin: -10px -10px 0; /* Вытягиваем иллюстрацию проекта на поля ячейки */
aspect-ratio: 1.91 / 1; /* Резервируем место под картинки, опираясь на их пропорции, чтобы страница не дёргалась при загрузке */
}
.project-caption { /* Настраиваем подписи */
margin-top: .5em;
font-size: 12px;
line-height: 1.2;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.sizeL,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}Наведём порядок в ссылках. Завернём в <u> те части подписей, что должны выглядеть как ссылки — проекты или клиенты:
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL">
<a href="/projects/maximilyahov-identity/">
<div class="project-media">
<img src="/projects/maximilyahov-identity/files/cover.jpg">
</div>
<div class="project-caption">
<u>Максим Ильяхов</u> десять лет учит интернет писать. Теперь у него есть фирстиль
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-font/">
<div class="project-media">
<img src="/projects/worldchess-font/files/cover.jpg">
</div>
<div class="project-caption">
Шрифт для цен Ворлд Чесс
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/worldchess-2/">
<div class="project-media">
<img src="/projects/worldchess-2/files/cover.jpg">
</div>
<div class="project-caption">
Вторая версия главной страницы Ворлд Чесса
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-broadcasts/">
<div class="project-media">
<img src="/projects/worldchess-broadcasts/files/cover.jpg">
</div>
<div class="project-caption">
Ворлд Чесс ведёт трансляции шахматных турниров
</div>
</a>
</div>
</section>
</div>
И стилизуем их:
:root {
--textColor: #000; /* Вводим переменные для цвета текста и ссылок */
--linkColor: #00aff0;
--keyColor: #ff5e00;
}
body {
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
color: var(--textColor);
}
a,
a u {
text-decoration: underline; /* Настраиваем тонкие полупрозрачные подчёркивания у ссылок */
text-decoration-thickness: 1px; /* Задаём толщину подчёркивания */
text-underline-offset: .175em; /* Задаём сдвиг подчёркивания */
text-decoration-color: color-mix(in srgb, currentColor, transparent 80%); /* Для подчёркивания используем текущий цвет текста с прозрачностью */
text-decoration-skip: none; /* Запрещаем подчёркиванию пропускать выносные элементы букв */
text-decoration-skip-ink: none;
color: var(--linkColor);
}
a:has(u) {
text-decoration: none; /* Если внутри ссылки есть тег <u>, то подчёркиваем только его */
}
a:hover,
a:hover u {
color: var(--keyColor);
text-decoration-color: currentColor; /* Когда курсор над ссылкой, подчёркиваем и перекрашиваем её в ключевой цвет страницы */
}
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
overflow: hidden;
}
.project a,
.project a:hover {
color: var(--textColor); /* Перекрашиваем подписи в цвет текста */
}
.project-media {
margin: -10px -10px 0;
aspect-ratio: 1.91 / 1;
}
.project-caption {
margin-top: .5em;
font-size: 12px;
line-height: 1.2;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.sizeL,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}
Наведём порядок в ссылках. Завернём в <u> те части подписей, что должны выглядеть как ссылки — проекты или клиенты:
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL">
<a href="/projects/maximilyahov-identity/">
<div class="project-media">
<img src="/projects/maximilyahov-identity/files/cover.jpg">
</div>
<div class="project-caption">
<u>Максим Ильяхов</u> десять лет учит интернет писать. Теперь у него есть фирстиль
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-font/">
<div class="project-media">
<img src="/projects/worldchess-font/files/cover.jpg">
</div>
<div class="project-caption">
Шрифт для цен Ворлд Чесс
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/worldchess-2/">
<div class="project-media">
<img src="/projects/worldchess-2/files/cover.jpg">
</div>
<div class="project-caption">
Вторая версия главной страницы Ворлд Чесса
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-broadcasts/">
<div class="project-media">
<img src="/projects/worldchess-broadcasts/files/cover.jpg">
</div>
<div class="project-caption">
Ворлд Чесс ведёт трансляции шахматных турниров
</div>
</a>
</div>
</section>
</div>
И стилизуем их:
:root {
--textColor: #000; /* Вводим переменные для цвета текста и ссылок */
--linkColor: #00aff0;
--keyColor: #ff5e00;
}
body {
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
color: var(--textColor);
}
a,
a u {
text-decoration: underline; /* Настраиваем тонкие полупрозрачные подчёркивания у ссылок */
text-decoration-thickness: 1px; /* Задаём толщину подчёркивания */
text-underline-offset: .175em; /* Задаём сдвиг подчёркивания */
text-decoration-color: color-mix(in srgb, currentColor, transparent 80%); /* Для подчёркивания используем текущий цвет текста с прозрачностью */
text-decoration-skip: none; /* Запрещаем подчёркиванию пропускать выносные элементы букв */
text-decoration-skip-ink: none;
color: var(--linkColor);
}
a:has(u) {
text-decoration: none; /* Если внутри ссылки есть тег <u>, то подчёркиваем только его */
}
a:hover,
a:hover u {
color: var(--keyColor);
text-decoration-color: currentColor; /* Когда курсор над ссылкой, подчёркиваем и перекрашиваем её в ключевой цвет страницы */
}
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
overflow: hidden;
}
.project a,
.project a:hover {
color: var(--textColor); /* Перекрашиваем подписи в цвет текста */
}
.project-media {
margin: -10px -10px 0;
aspect-ratio: 1.91 / 1;
}
.project-caption {
margin-top: .5em;
font-size: 12px;
line-height: 1.2;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.sizeL,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}
Добавим ещё ряд и соберём всё вместе:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Портфолио Романа</title>
<link rel="stylesheet" href="reset.css">
</head>
<body>
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL">
<a href="/projects/maximilyahov-identity/">
<div class="project-media">
<img src="/projects/maximilyahov-identity/files/cover.jpg">
</div>
<div class="project-caption">
<u>Максим Ильяхов</u> десять лет учит интернет писать. Теперь у него есть фирстиль
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-font/">
<div class="project-media">
<img src="/projects/worldchess-font/files/cover.jpg">
</div>
<div class="project-caption">
<u>Шрифт для цен</u> Ворлд Чесс
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/worldchess-2/">
<div class="project-media">
<img src="/projects/worldchess-2/files/cover.jpg">
</div>
<div class="project-caption">
<u>Вторая версия главной страницы</u> Ворлд Чесса
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-broadcasts/">
<div class="project-media">
<img src="/projects/worldchess-broadcasts/files/cover.jpg">
</div>
<div class="project-caption">
<u>Ворлд Чесс</u> ведёт трансляции шахматных турниров
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/vashrepetitor-logo/">
<div class="project-media">
<img src="/projects/vashrepetitor-logo/files/cover.jpg">
</div>
<div class="project-caption">
<u>Логотип</u> «Вашего репетитора» — сервиса подбора репетиторов
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/mailganer/">
<div class="project-media">
<img src="/projects/mailganer/files/cover.jpg">
</div>
<div class="project-caption">
<u>Сайт Мейлганера</u> — кросс-канальной платформы маркетинга
</div>
</a>
</div>
</section>
</div>
<script>
const MOBILE_LAYOUT = ['L', 'S', 'S']
document
.querySelectorAll('.project')
.forEach((project, index) => {
const layoutClass = MOBILE_LAYOUT[index % MOBILE_LAYOUT.length]
const mobileClass = `mobileSize${layoutClass}`
project
.classList
.add(mobileClass)
})
</script>
</body>
</html>
:root {
--textColor: #000;
--linkColor: #00aff0;
--keyColor: #ff5e00;
}
body {
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
color: var(--textColor);
}
a,
a u {
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: .175em;
text-decoration-color: color-mix(in srgb, currentColor, transparent 80%);
text-decoration-skip: none;
text-decoration-skip-ink: none;
color: var(--linkColor);
}
a:has(u) {
text-decoration: none;
}
a:hover,
a:hover u {
color: var(--keyColor);
text-decoration-color: currentColor;
}
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
min-height: 33vh;
overflow: hidden;
}
.project a,
.project a:hover {
color: var(--textColor);
}
.project-media {
margin: -10px -10px 0;
aspect-ratio: 1.91 / 1;
}
.project-caption {
margin-top: .5em;
font-size: 12px;
line-height: 1.2;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.sizeL,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}Добавим ещё ряд и соберём всё вместе:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Портфолио Романа</title>
<link rel="stylesheet" href="reset.css">
</head>
<body>
<div class="layoutContainer">
<section class="projects">
<div class="project sizeL">
<a href="/projects/maximilyahov-identity/">
<div class="project-media">
<img src="/projects/maximilyahov-identity/files/cover.jpg">
</div>
<div class="project-caption">
<u>Максим Ильяхов</u> десять лет учит интернет писать. Теперь у него есть фирстиль
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-font/">
<div class="project-media">
<img src="/projects/worldchess-font/files/cover.jpg">
</div>
<div class="project-caption">
<u>Шрифт для цен</u> Ворлд Чесс
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/worldchess-2/">
<div class="project-media">
<img src="/projects/worldchess-2/files/cover.jpg">
</div>
<div class="project-caption">
<u>Вторая версия главной страницы</u> Ворлд Чесса
</div>
</a>
</div>
<div class="project sizeS">
<a href="/projects/worldchess-broadcasts/">
<div class="project-media">
<img src="/projects/worldchess-broadcasts/files/cover.jpg">
</div>
<div class="project-caption">
<u>Ворлд Чесс</u> ведёт трансляции шахматных турниров
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/vashrepetitor-logo/">
<div class="project-media">
<img src="/projects/vashrepetitor-logo/files/cover.jpg">
</div>
<div class="project-caption">
<u>Логотип</u> «Вашего репетитора» — сервиса подбора репетиторов
</div>
</a>
</div>
<div class="project sizeM">
<a href="/projects/mailganer/">
<div class="project-media">
<img src="/projects/mailganer/files/cover.jpg">
</div>
<div class="project-caption">
<u>Сайт Мейлганера</u> — кросс-канальной платформы маркетинга
</div>
</a>
</div>
</section>
</div>
<script>
const MOBILE_LAYOUT = ['L', 'S', 'S']
document
.querySelectorAll('.project')
.forEach((project, index) => {
const layoutClass = MOBILE_LAYOUT[index % MOBILE_LAYOUT.length]
const mobileClass = `mobileSize${layoutClass}`
project
.classList
.add(mobileClass)
})
</script>
</body>
</html>
:root {
--textColor: #000;
--linkColor: #00aff0;
--keyColor: #ff5e00;
}
body {
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
color: var(--textColor);
}
a,
a u {
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: .175em;
text-decoration-color: color-mix(in srgb, currentColor, transparent 80%);
text-decoration-skip: none;
text-decoration-skip-ink: none;
color: var(--linkColor);
}
a:has(u) {
text-decoration: none;
}
a:hover,
a:hover u {
color: var(--keyColor);
text-decoration-color: currentColor;
}
.projects {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-rows: max-content;
column-gap: 20px;
row-gap: 20px;
}
.project {
padding: 10px 10px 20px;
border-radius: 4px;
background: #f3f5f4;
min-height: 33vh;
overflow: hidden;
}
.project a,
.project a:hover {
color: var(--textColor);
}
.project-media {
margin: -10px -10px 0;
aspect-ratio: 1.91 / 1;
}
.project-caption {
margin-top: .5em;
font-size: 12px;
line-height: 1.2;
}
.project.sizeS {
grid-column: auto / span 1;
}
.project.sizeM {
grid-column: auto / span 2;
}
.project.sizeL {
grid-column: auto / span 4;
}
@media (width <= 768px) {
.projects {
grid-template-columns: 1fr 1fr;
grid-auto-flow: dense;
}
.project.mobileSizeS,
.project.mobileSizeM {
grid-column: auto / span 1;
}
.project.sizeL,
.project.mobileSizeL {
grid-column: auto / span 2;
}
}