🔍 Начните печатать, чтобы искать по книге или перейти к нужной странице по номеру

Удобно листать не только прокруткой, но и клавишами‑стрелками:

 
между важными местами
Shift
между
разворотами
Васи­лий Полов­нёв, Игорь Пет­ров

ХТМЛ.
Вёрстка сайтов

Изда­тель­ство Бюро Гор­бу­нова
2021
Василий Половнёв, Игорь Петров

ХТМЛ.
Вёрстка сайтов

Издательство Бюро Горбунова
2021
удк 004.42
ббк З973.42
П52
Васи­лий Полов­нёв, Игорь Пет­ров
П52
ХТМЛ. Вёрстка сай­тов для дизай­не­ров, редак­то­ров и руко­во­ди­те­лей.—
М.: Изд‑во Бюро Гор­бу­нова, 2021

Эта книга — поша­го­вая инструк­ция по вёрстке сай­тов на язы­ках ХТМЛ и ЦСС. Вы узна­ете не только как свер­стать сайт, но и как опуб­ли­ко­вать его в интер­нете, настро­ить кра­си­вый шаринг в соц­сети и под­клю­чить системы аналитики.

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

Оглавление

удк 004.42
ббк З973.42
П52
П52
Василий Половнёв, Игорь Петров
ХТМЛ. Вёрстка сайтов для дизайнеров, редакторов и руководителей.—
М.: Изд‑во Бюро Горбунова, 2021

Эта книга — пошаговая инструкция по вёрстке сайтов на языках ХТМЛ и ЦСС. Вы узнаете не только как сверстать сайт, но и как опубликовать его в интернете, настроить красивый шаринг в соцсети и подключить системы аналитики.

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

Оглавление

Скрыто 189 разворотов

Мобиль­ная вер­сия «в лоб» даёт дубли — повто­ря­ю­щи­еся этажи с оди­на­ко­вой рас­клад­кой. Чтобы изба­виться от этого, вве­дём моди­фи­ка­торы ширины для мобиль­ной версии:

.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;
  }
}
Скрыто 26 разворотов