🔍 Начните печатать, чтобы искать по книге или перейти к нужной странице по номеру
Удобно листать не только прокруткой, но и клавишами‑стрелками:
Эта книга — пошаговая инструкция по вёрстке сайтов на языках ХТМЛ и ЦСС. Вы узнаете не только как сверстать сайт, но и как опубликовать его в интернете, настроить красивый шаринг в соцсети и подключить системы аналитики.
Отдельный раздел книги посвящён работе с верстальщиком: как ставить задачи, получать предсказуемый результат и правильно принимать и внедрять вёрстку.
Знакомство с ХТМЛ и ЦСС
Модули
Расстановка
Страницы
Спецэффекты
Контроль качества
Публикация
Как работать с верстальщиком
Эта книга — пошаговая инструкция по вёрстке сайтов на языках ХТМЛ и ЦСС. Вы узнаете не только как сверстать сайт, но и как опубликовать его в интернете, настроить красивый шаринг в соцсети и подключить системы аналитики.
Отдельный раздел книги посвящён работе с верстальщиком: как ставить задачи, получать предсказуемый результат и правильно принимать и внедрять вёрстку.
Знакомство с ХТМЛ и ЦСС
Модули
Расстановка
Страницы
Спецэффекты
Контроль качества
Публикация
Как работать с верстальщиком
Разберём вёрстку самой популярной раскладки портфолио — вёрстку плиткой. Считаем, что в нашем портфолио сетка на 4 колонки с межколонником в 20 пикселей. Сосредоточимся только на раскладке, опустив типографику и стили для сброса.
Начнём с разметки, не забыв об обёртке для всей страницы. Каждый проект — это див с классом project и модификатором размера: sizeL — на все четыре колонки, sizeM — на две колонки, sizeS — на одну колонку. Соберём несколько комбинаций из них:
<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>Обёртка для страницы
Включим гриды, зададим сетку из четырёх колонок и модификаторы ширины плиток:
.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;
}
Разберём вёрстку самой популярной раскладки портфолио — вёрстку плиткой. Считаем, что в нашем портфолио сетка на 4 колонки с межколонником в 20 пикселей. Сосредоточимся только на раскладке, опустив типографику и стили для сброса.
Начнём с разметки, не забыв об обёртке для всей страницы. Каждый проект — это див с классом project и модификатором размера: sizeL — на все четыре колонки, sizeM — на две колонки, sizeS — на одну колонку. Соберём несколько комбинаций из них:
<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>Обёртка для страницы
Включим гриды, зададим сетку из четырёх колонок и модификаторы ширины плиток:
.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;
}
Добавим мобильную версию «в лоб» — перейдём на две колонки:
.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.sizeL {
grid-column: auto / span 2;
}
}Добавим мобильную версию «в лоб» — перейдём на две колонки:
.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.sizeL {
grid-column: auto / span 2;
}
}Мобильная версия «в лоб» даёт дубли — повторяющиеся этажи с одинаковой раскладкой. Чтобы избавиться от этого, введём модификаторы ширины для мобильной версии:
.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;
}
}