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

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

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

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

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

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

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

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

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

Оглавление

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

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

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

Оглавление

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

Типичная шапка из трёх элементов: логотипа, меню и телефона

В контейнере <nav> — navigation — собирают ссылки для навигации по сайту

Если не свешивать круглый логотип за линию сетки, он будет казаться сдвинутым

Шапка и подвал сайта чаще всего имеют форму строк, прижатых к верху и низу страницы

Мы используем max‑content, а не min‑content, чтобы пункты меню из нескольких слов не переносились

Аналогично можно было бы выделить текущий пункт меню начертанием или цветом

По умолчанию содержимое грид‑ячеек выравнивается по верхнему краю

В гридах align‑items управляет выравниванием по вертикали, а justify‑items — по горизонтали

В гридах justify‑self управляет выравниванием внутри ячейки

Иногда подчёркивание делают с помощью border‑bottom, background‑image или box‑shadow

Свер­стаем про­стую шапку «в линию», встре­ча­ю­щу­юся на боль­шин­стве сай­тов. Сосре­до­то­чимся только на рас­кладке, опу­стив типо­гра­фику и стили для сброса.

Нач­нём с раз­метки. Собе­рём шапку в кон­тей­нере <header>. Для лого­типа возь­мём див с кар­тин­кой, завёр­ну­той в ссылку на глав­ную стра­ницу. Меню собе­рём в нену­ме­ро­ван­ный спи­сок <ul>. Теле­фон сде­лаем кли­ка­бель­ной ссыл­кой с хит­рым про­то­ко­лом tel:

<header class="header">
  <div class="logo">
    <a href="/">
      <img src="logo.svg">
    </a>
  </div>

  <nav class="nav">
    <ul>
      <li class="nav__item">
        <a href="/products/">Продукция</a>
      </li>
      <li class="nav__item">
        <a href="/services/">Услуги</a>
      </li>
      <li class="nav__item is__active">
        <a href="/blog/">Блог</a>
      </li>
      <li class="nav__item">
        <a href="/contacts/">Контакты</a>
      </li>
    </ul>
  </nav>

  <div class="tel">
    <a href="tel:+74954005050">
      +7 495 400‑50‑50
    </a>
  </div>
</header>

Стилизуем логотип. Зафик­си­руем его раз­меры и све­сим кру­жок лого­типа за линии сетки:

.logo {
  width: 40px;
  height: 40px;
  margin: 0 -5px;
}

Стилизуем меню. Вклю­чим гриды и раз­ло­жим пункты меню по колонкам:

.nav ul {
  display: grid;
  grid-auto-flow: column;
}

Опре­де­лим авто­ма­ти­че­ские колонки по мак­си­маль­ному содер­жи­мому и зада­дим отступ между пунк­тами меню:

.nav ul {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  column-gap: 20px;
}

Стилизуем пункты меню. Уве­ли­чим кли­ка­бель­ную область ссы­лок и сти­ли­зуем теку­щий пункт меню:

.nav ul {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  column-gap: 20px;
  align-items: center;
}

.nav__item a {
  position: relative;
  z-index: 1;
}

.nav__item a::before {
  content: '';
  position: absolute;
  inset: -3px -7px;
  z-index: -1;
}

.nav__item.is__active a {
  color: #fff;
  text-decoration: none;
}

.nav__item.is__active a::before {
  background-color: #ff5722;
  border-radius: 8px;
}

Настроим раскладку шапки в целом. Вклю­чим гриды и раз­ло­жим лого­тип, меню и теле­фон по колон­кам. Заодно доба­вим шапке верх­ний падинг и сде­лаем её кон­тей­не­ром‑обёрт­кой, чтобы она не при­ли­пала к краям и пра­вильно управ­ляла своей шириной:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding-top: 10px;
}
<header class="header layoutContainer">
  <!-- … -->
</header>

Отцен­три­руем всё по вертикали:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding-top: 10px;
  align-items: center;
  line-height: 1;
}

Ото­жмём теле­фон к пра­вому краю:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding: 10px;
  align-items: center;
}

.header .tel {
  justify-self: end;
}

Убе­рём под­чер­ки­ва­ния у ссы­лок в шапке:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding: 10px;
  align-items: center;
}

.header .tel {
  justify-self: end;
}

.header a {
  text-decoration: none;
}

Типичная шапка из трёх элементов: логотипа, меню и телефона

В контейнере <nav> — navigation — собирают ссылки для навигации по сайту

Если не свешивать круглый логотип за линию сетки, он будет казаться сдвинутым

Шапка и подвал сайта чаще всего имеют форму строк, прижатых к верху и низу страницы

Мы используем max‑content, а не min‑content, чтобы пункты меню из нескольких слов не переносились

Аналогично можно было бы выделить текущий пункт меню начертанием или цветом

По умолчанию содержимое грид‑ячеек выравнивается по верхнему краю

В гридах align‑items управляет выравниванием по вертикали, а justify‑items — по горизонтали

В гридах justify‑self управляет выравниванием внутри ячейки

Иногда подчёркивание делают с помощью border‑bottom, background‑image или box‑shadow

Сверстаем простую шапку «в линию», встречающуюся на большинстве сайтов. Сосредоточимся только на раскладке, опустив типографику и стили для сброса.

Начнём с разметки. Соберём шапку в контейнере <header>. Для логотипа возьмём див с картинкой, завёрнутой в ссылку на главную страницу. Меню соберём в ненумерованный список <ul>. Телефон сделаем кликабельной ссылкой с хитрым протоколом tel:

<header class="header">
  <div class="logo">
    <a href="/">
      <img src="logo.svg">
    </a>
  </div>

  <nav class="nav">
    <ul>
      <li class="nav__item">
        <a href="/products/">Продукция</a>
      </li>
      <li class="nav__item">
        <a href="/services/">Услуги</a>
      </li>
      <li class="nav__item is__active">
        <a href="/blog/">Блог</a>
      </li>
      <li class="nav__item">
        <a href="/contacts/">Контакты</a>
      </li>
    </ul>
  </nav>

  <div class="tel">
    <a href="tel:+74954005050">
      +7 495 400‑50‑50
    </a>
  </div>
</header>

Стилизуем логотип. Зафиксируем его размеры и свесим кружок логотипа за линии сетки:

.logo {
  width: 40px;
  height: 40px;
  margin: 0 -5px;
}

Стилизуем меню. Включим гриды и разложим пункты меню по колонкам:

.nav ul {
  display: grid;
  grid-auto-flow: column;
}

Определим автоматические колонки по максимальному содержимому и зададим отступ между пунктами меню:

.nav ul {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  column-gap: 20px;
}

Стилизуем пункты меню. Увеличим кликабельную область ссылок и стилизуем текущий пункт меню:

.nav ul {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  column-gap: 20px;
  align-items: center;
}

.nav__item a {
  position: relative;
  z-index: 1;
}

.nav__item a::before {
  content: '';
  position: absolute;
  inset: -3px -7px;
  z-index: -1;
}

.nav__item.is__active a {
  color: #fff;
  text-decoration: none;
}

.nav__item.is__active a::before {
  background-color: #ff5722;
  border-radius: 8px;
}

Настроим раскладку шапки в целом. Включим гриды и разложим логотип, меню и телефон по колонкам. Заодно добавим шапке верхний падинг и сделаем её контейнером‑обёрткой, чтобы она не прилипала к краям и правильно управляла своей шириной:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding-top: 10px;
}
<header class="header layoutContainer">
  <!-- … -->
</header>

Отцентрируем всё по вертикали:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding-top: 10px;
  align-items: center;
  line-height: 1;
}

Отожмём телефон к правому краю:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding: 10px;
  align-items: center;
}

.header .tel {
  justify-self: end;
}

Уберём подчеркивания у ссылок в шапке:

.header {
  display: grid;
  grid-template-columns: min-content max-content 1fr;
  column-gap: 20px;
  padding: 10px;
  align-items: center;
}

.header .tel {
  justify-self: end;
}

.header a {
  text-decoration: none;
}

Адап­ти­руем полу­чив­шу­юся шапку для мобиль­ных устройств. Будем счи­тать, что в нашем дизайне мобиль­ные устрой­ства — это устрой­ства с экра­ном шири­ной менее 992 пикселей.

Обер­нём <nav> в див с клас­сом navHolder — он при­го­дится нам для про­крутки и обре­за­ния меню:

<header class="header">
  <div class="logo">…</div>

  <div class="navHolder">
    <nav class="nav">
      <!-- … -->
    </nav>
  </div>

  <div class="tel">…</div>
</header>

Поме­няем рас­кладку шапки. Вме­сто трёх коло­нок сде­лаем сетку 2×2:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
}

Пере­ста­вим теле­фон в пра­вый верх­ний угол, меню поста­вим вниз и рас­тя­нем на две колонки:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
  }
}

Рас­тя­нем меню на всю ширину экрана:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }
}

Вклю­чим гори­зон­таль­ную про­крутку в меню:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }
  
  .nav {
    overflow-x: scroll; /* Зададим возможность горизонтальной прокрутки */
    -webkit-overflow-scrolling: touch; /* Для Сафари дополнительно зададим инерцию при прокрутке. С инерцией меню будет «оттягиваться» при попытке прокрутить меню за его пределы. */
  }
}

Из‑за overflow обре­за­лись поля у теку­щего пункта в меню. Плюс при про­крутке появ­ля­ется гори­зо­наль­ный скрол­бар. Устра­ним эти проблемы:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }
  
  .nav {
    padding: 10px; /* Добавим дополнительные поля */
    margin: -10px 0; /* С помощью отрицательных отступов сверху-снизу компенсируем «нарощенные» поля */
    scrollbar-width: none; /* Скроем скролбар в Хроме и Файрфоксе */
    overflow-x: scroll;
    -webkit-overflow-scrolling: touch;
  }

  .nav::-webkit-scrollbar {
    display: none; /* Скроем скролбар в Сафари */
  }
}

Доба­вим немного кос­ме­тики в виде «забе­ле­ния» по краям с помо­щью маски и градиента:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }

  .nav {
    padding: 10px;
    margin: -10px 0;
    scrollbar-width: none;
    overflow-x: scroll;
    -webkit-overflow-scrolling: touch;
    mask-image: linear-gradient(
      90deg,
      transparent 0,
      rgba(0, 0, 0, .25) 8px,
      #000 16px,
      #000 calc(100% - 16px),
      rgba(0, 0, 0, .25) calc(100% - 8px),
      transparent
    );
  }

  .nav::-webkit-scrollbar {
    display: none;
  }
}

Окру­жим меню линей­ками, чтобы пока­зать, что оно про­кру­чи­ва­ется неза­ви­симо от дру­гих эле­мен­тов страницы:

@media (width < 992px) {
  .navHolder {
    position: relative; /* Мы используем обертку для линеек, потому что линейки у .nav попадали бы под маску и забеление */
  }

  .navHolder::before,
  .navHolder::after {
    content: ''; /* Используем псевдоэлементы, а не рамку, потому что их позицией можно управлять */
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background: rgba(0, 0, 0, .07);
  }

  .navHolder::before {
    top: -9px;
  }

  .navHolder::after {
    bottom: -8px;
  }
}

Оста­лась одна про­блема. При неко­то­рых шири­нах экрана меню обре­за­ется так, что кажется, что всё поме­сти­лось и про­кру­чи­вать нечего. Чтобы попра­вить это, можно напи­сать неболь­шой скрипт, кото­рый будет ана­ли­зи­ро­вать ширину эле­мен­тов и менять зна­че­ние column-gap в меню так, чтобы послед­ний види­мый эле­мент все­гда обрезался.

Если меню ста­тич­ное и вряд ли будет меняться, проще задать под­хо­дя­щие зна­че­ния column-gap для раз­ной ширины экрана.

Адаптируем получившуюся шапку для мобильных устройств. Будем считать, что в нашем дизайне мобильные устройства — это устройства с экраном шириной менее 992 пикселей.

Обернём <nav> в див с классом navHolder — он пригодится нам для прокрутки и обрезания меню:

<header class="header">
  <div class="logo">…</div>

  <div class="navHolder">
    <nav class="nav">
      <!-- … -->
    </nav>
  </div>

  <div class="tel">…</div>
</header>

Поменяем раскладку шапки. Вместо трёх колонок сделаем сетку 2×2:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
}

Переставим телефон в правый верхний угол, меню поставим вниз и растянем на две колонки:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
  }
}

Растянем меню на всю ширину экрана:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }
}

Включим горизонтальную прокрутку в меню:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }
  
  .nav {
    overflow-x: scroll; /* Зададим возможность горизонтальной прокрутки */
    -webkit-overflow-scrolling: touch; /* Для Сафари дополнительно зададим инерцию при прокрутке. С инерцией меню будет «оттягиваться» при попытке прокрутить меню за его пределы. */
  }
}

Из‑за overflow обрезались поля у текущего пункта в меню. Плюс при прокрутке появляется горизональный скролбар. Устраним эти проблемы:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }
  
  .nav {
    padding: 10px; /* Добавим дополнительные поля */
    margin: -10px 0; /* С помощью отрицательных отступов сверху-снизу компенсируем «нарощенные» поля */
    scrollbar-width: none; /* Скроем скролбар в Хроме и Файрфоксе */
    overflow-x: scroll;
    -webkit-overflow-scrolling: touch;
  }

  .nav::-webkit-scrollbar {
    display: none; /* Скроем скролбар в Сафари */
  }
}

Добавим немного косметики в виде «забеления» по краям с помощью маски и градиента:

@media (width < 992px) {
  .header {
    grid-template-columns: min-content 1fr;
    grid-template-rows: min-content min-content;
    row-gap: 15px;
  }
  
  .header .tel {
    grid-column: 2;
    grid-row: 1;
  }
  
  .header .navHolder {
    grid-column: 1 / span 2;
    grid-row: 2;
    margin-left: -10px;
    margin-right: -10px;
  }

  .nav {
    padding: 10px;
    margin: -10px 0;
    scrollbar-width: none;
    overflow-x: scroll;
    -webkit-overflow-scrolling: touch;
    mask-image: linear-gradient(
      90deg,
      transparent 0,
      rgba(0, 0, 0, .25) 8px,
      #000 16px,
      #000 calc(100% - 16px),
      rgba(0, 0, 0, .25) calc(100% - 8px),
      transparent
    );
  }

  .nav::-webkit-scrollbar {
    display: none;
  }
}

Окружим меню линейками, чтобы показать, что оно прокручивается независимо от других элементов страницы:

@media (width < 992px) {
  .navHolder {
    position: relative; /* Мы используем обертку для линеек, потому что линейки у .nav попадали бы под маску и забеление */
  }

  .navHolder::before,
  .navHolder::after {
    content: ''; /* Используем псевдоэлементы, а не рамку, потому что их позицией можно управлять */
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background: rgba(0, 0, 0, .07);
  }

  .navHolder::before {
    top: -9px;
  }

  .navHolder::after {
    bottom: -8px;
  }
}

Осталась одна проблема. При некоторых ширинах экрана меню обрезается так, что кажется, что всё поместилось и прокручивать нечего. Чтобы поправить это, можно написать небольшой скрипт, который будет анализировать ширину элементов и менять значение column-gap в меню так, чтобы последний видимый элемент всегда обрезался.

Если меню статичное и вряд ли будет меняться, проще задать подходящие значения column-gap для разной ширины экрана.

Адап­ти­руем шапку к мобиль­ной вер­сии с помо­щью гам­бур­гер­ного меню. Будет скры­вать меню и пока­зы­вать «гам­бур­гер» при той ширине экрана, при кото­рой меню не поме­ща­ется в него целиком.

Возь­мём шапку без мобиль­ной вер­сии и доба­вим кнопку гамбургер:

<header class="header">
  <div class="logo">
    <a href="/">
      <img src="logo.svg" width="40" height="40">
    </a>
  </div>
  
  <nav class="nav">
    <ul>
      <li class="nav__item">
        <a href="/products/">Продукция</a>
      </li>
      <li class="nav__item">
        <a href="/services/">Услуги</a>
      </li>
      <li class="nav__item is__active">
        <a href="/blog/">Блог</a>
      </li>
      <li class="nav__item">
        <a href="/contacts/">Контакты</a>
      </li>
      <li class="nav__item">
        <a href="tel:+74954005050">+7 495 400‑50‑50</a>
      </li>
    </ul>
  </nav>
  
  <div class="tel">
    <a href="tel:+74954005050">
      +7 495 400‑50‑50
    </a>
  </div>

  <!-- aria-label подскажет смысл кнопки программам чтения с экрана -->
  <button class="hamburger" type="button" aria-label="Открыть меню">
    <span class="hamburger-box">
      <span class="hamburger-inner"></span>
    </span>
  </button>
</header>

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

@media (width > 690px) {
  .header .hamburger {
    display: none;
  }
}

@media (width <= 690px) {
  .header {
    position: relative; /* Задаём контекст для позиционирования меню */
    grid-template-columns: min-content 1fr 1fr min-content; /* Определяем четыре колонки: первую и последнюю — по ширине логотипа и кнопки, вторую и третью — 50% от оставшегося места */
  }

  .header .hamburger {
    grid-column: 4; /* Кнопку ставим в последнюю колонку */
  }

  .header .tel {
    grid-column: 2 / span 2; /* Телефону отдаём две центральные колонки */
    justify-self: stretch;
  }

  .nav {
    position: absolute; /* Меню позиционируем абсолютно, сразу после шапки поверх содержимого. Из-за этого кнопка перестанет нажиматься, исправим это на следующем шаге */
    top: 0;
    left: 0;
    width: 100%; /* Растягиваем меню на всю ширину экрана */
    padding: 65px 10px 20px; /* Добавляем настолько большой верхний падинг, чтобы меню встало точно под шапкой */
  }

  .nav ul {
    grid-auto-flow: row; /* Раскладываем меню рядами */
    grid-auto-rows: max-content;
    row-gap: 10px;
  }
}

Сти­ли­зуем кнопку. Для про­стоты возь­мём гото­вые стили и ани­ма­цию из биб­лио­теки «гам­бур­гер­ных» ико­нок на ЦСС:

<link rel="stylesheet" src="styles/hamburgers.css">

<header class="header">
  <div class="logo">
    <a href="/">
      <img src="logo.svg" width="40" height="40">
    </a>
  </div>
  
  <nav class="nav">
    <ul>
      <li class="nav__item">
        <a href="/products/">Продукция</a>
      </li>
      <li class="nav__item">
        <a href="/services/">Услуги</a>
      </li>
      <li class="nav__item is__active">
        <a href="/blog/">Блог</a>
      </li>
      <li class="nav__item">
        <a href="/contacts/">Контакты</a>
      </li>
      <li class="nav__item">
        <a href="tel:+74954005050">+7 495 400‑50‑50</a>
      </li>
    </ul>
  </nav>
  
  <div class="tel">
    <a href="tel:+74954005050">
      +7 495 400‑50‑50
    </a>
  </div>

  <button class="hamburger hamburger--squeeze" type="button" aria-label="Открыть меню">
    <span class="hamburger-box">
      <span class="hamburger-inner"></span>
    </span>
  </button>
</header>
.header .hamburger {
  grid-column: 4;
  grid-row: 1;
  color: inherit; /* Чтобы иконка унаследовала цвет текста, а не была белой */
  line-height: 0; /* Чтобы иконка не увеличивала высоту меню */
  padding: 0;
}

.header .hamburger-inner,
.header .hamburger-inner::before,
.header .hamburger-inner::after {
  border-radius: 0; /* Убираем скругления в гамбургере */
}

«Гамбургер» и его анимацию можно было бы сверстать вручную или заменить на иконки

Научим кнопку «кли­каться». Пус­кай пока она меняет своё состо­я­ние и добав­ляет или уби­рает класс у меню:

<script>
  document
    .querySelectorAll('.hamburger') // Находим кнопку-гамбургер
    .forEach(button => {
     const nav = button
      .closest('.header')
      .querySelector('.nav') // Находим ближайшее к кнопке меню

     button.addEventListener('click', () => { // По клику на кнопку добавляем или удаляем класс is-active у кнопки и меню
       button
         .classList
         .toggle('is-active')

       nav
        .classList
        .toggle('is-active')
     })
  })
</script>

Внутри <script> пишут текст на Яваскрипте — языке программирования, встроенном в браузеры. Яваскрипт используют для создания интерактивных страниц и веб‑приложений

Под­клю­чим меню к кнопке и доба­вим ани­ма­цию. Пусть меню при­ез­жает сверху:

 @media (width > 690px) {
   .header .hamburger {
     display: none;
   }
 }

 @media (width <= 690px) {
   .header {
     position: relative;
     grid-template-columns: min-content 1fr 1fr min-content;
   }
   
   .header .hamburger {
     grid-column: 4;
     grid-row: 1;
   }
   
   .header .tel {
     grid-column: 2 / span 2;
     grid-row: 1;
     justify-self: stretch;
   }

   .nav {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
     padding: 65px 10px 20px;
     transform: translateY(-100%); /* Прячем меню за верхнюю границу экрана */
     transition: transform 75ms ease; /* Анимируем изменение позиции меню, синхронизировав с анимацией кнопки */
     transition-delay: 120ms;
   }

   .nav.is-active {
     transform: translateY(0); /* Когда меню активно, ставим его под шапку */
     transition-timing-function: cubic-bezier(.215, .61, .355, 1);
   }

   .nav ul {
     grid-auto-flow: row;
     grid-auto-rows: max-content;
     row-gap: 10px;
   }
}

Тайминги и скорость анимации скопировали из кнопки, чтобы не было рассинхрона

Появи­лась про­блема с напол­за­нием. Выез­жа­ю­щее меню пере­кры­вает лого­тип. Попра­вим это, доба­вив слой, раз­де­ля­ю­щий выез­жа­ю­щее меню и лого­тип с кнопкой:

 @media (width > 690px) {
   .header .hamburger {
     display: none;
   }
 }

 @media (width <= 690px) {
   .header {
     position: relative;
     grid-template-columns: min-content 1fr 1fr min-content;
   }
   
   .header .hamburger {
     grid-column: 4;
     grid-row: 1;
   }
   
   .header .tel {
     grid-column: 2 / span 2;
     grid-row: 1;
     justify-self: stretch;
   }

   .nav {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
     padding: 65px 10px 20px;
     transform: translateY(calc(60px - 100%)); /* Спрячем меню под слой-разделитель */
     z-index: 1; /* Меню поедет на самом нижнем уровне */
     transition: transform 75ms ease;
     transition-delay: 120ms;
   }
  
  .nav.is-active {
    transform: translateY(0);
    transition-timing-function: cubic-bezier(.215, .61, .355, 1);
  }
  
  .nav ul {
     grid-auto-flow: row;
     grid-auto-rows: max-content;
     row-gap: 10px;
   }

  .header::before {
    content: '';
    background: inherit;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 2; /* Слой-разделитель скроет меню, пока оно проезжает под шапкой */
  }

  .header .logo,
  .header .tel,
  .header .hamburger {
    position: relative;
    z-index: 3; /* Логотип, телефон и кнопка встанут на самом верху */
  }
}

Адаптируем шапку к мобильной версии с помощью гамбургерного меню. Будет скрывать меню и показывать «гамбургер» при той ширине экрана, при которой меню не помещается в него целиком.

Возьмём шапку без мобильной версии и добавим кнопку гамбургер:

<header class="header">
  <div class="logo">
    <a href="/">
      <img src="logo.svg" width="40" height="40">
    </a>
  </div>
  
  <nav class="nav">
    <ul>
      <li class="nav__item">
        <a href="/products/">Продукция</a>
      </li>
      <li class="nav__item">
        <a href="/services/">Услуги</a>
      </li>
      <li class="nav__item is__active">
        <a href="/blog/">Блог</a>
      </li>
      <li class="nav__item">
        <a href="/contacts/">Контакты</a>
      </li>
      <li class="nav__item">
        <a href="tel:+74954005050">+7 495 400‑50‑50</a>
      </li>
    </ul>
  </nav>
  
  <div class="tel">
    <a href="tel:+74954005050">
      +7 495 400‑50‑50
    </a>
  </div>

  <!-- aria-label подскажет смысл кнопки программам чтения с экрана -->
  <button class="hamburger" type="button" aria-label="Открыть меню">
    <span class="hamburger-box">
      <span class="hamburger-inner"></span>
    </span>
  </button>
</header>

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

@media (width > 690px) {
  .header .hamburger {
    display: none;
  }
}

@media (width <= 690px) {
  .header {
    position: relative; /* Задаём контекст для позиционирования меню */
    grid-template-columns: min-content 1fr 1fr min-content; /* Определяем четыре колонки: первую и последнюю — по ширине логотипа и кнопки, вторую и третью — 50% от оставшегося места */
  }

  .header .hamburger {
    grid-column: 4; /* Кнопку ставим в последнюю колонку */
  }

  .header .tel {
    grid-column: 2 / span 2; /* Телефону отдаём две центральные колонки */
    justify-self: stretch;
  }

  .nav {
    position: absolute; /* Меню позиционируем абсолютно, сразу после шапки поверх содержимого. Из-за этого кнопка перестанет нажиматься, исправим это на следующем шаге */
    top: 0;
    left: 0;
    width: 100%; /* Растягиваем меню на всю ширину экрана */
    padding: 65px 10px 20px; /* Добавляем настолько большой верхний падинг, чтобы меню встало точно под шапкой */
  }

  .nav ul {
    grid-auto-flow: row; /* Раскладываем меню рядами */
    grid-auto-rows: max-content;
    row-gap: 10px;
  }
}

Стилизуем кнопку. Для простоты возьмём готовые стили и анимацию из библиотеки «гамбургерных» иконок на ЦСС:

<link rel="stylesheet" src="styles/hamburgers.css">

<header class="header">
  <div class="logo">
    <a href="/">
      <img src="logo.svg" width="40" height="40">
    </a>
  </div>
  
  <nav class="nav">
    <ul>
      <li class="nav__item">
        <a href="/products/">Продукция</a>
      </li>
      <li class="nav__item">
        <a href="/services/">Услуги</a>
      </li>
      <li class="nav__item is__active">
        <a href="/blog/">Блог</a>
      </li>
      <li class="nav__item">
        <a href="/contacts/">Контакты</a>
      </li>
      <li class="nav__item">
        <a href="tel:+74954005050">+7 495 400‑50‑50</a>
      </li>
    </ul>
  </nav>
  
  <div class="tel">
    <a href="tel:+74954005050">
      +7 495 400‑50‑50
    </a>
  </div>

  <button class="hamburger hamburger--squeeze" type="button" aria-label="Открыть меню">
    <span class="hamburger-box">
      <span class="hamburger-inner"></span>
    </span>
  </button>
</header>
.header .hamburger {
  grid-column: 4;
  grid-row: 1;
  color: inherit; /* Чтобы иконка унаследовала цвет текста, а не была белой */
  line-height: 0; /* Чтобы иконка не увеличивала высоту меню */
  padding: 0;
}

.header .hamburger-inner,
.header .hamburger-inner::before,
.header .hamburger-inner::after {
  border-radius: 0; /* Убираем скругления в гамбургере */
}

«Гамбургер» и его анимацию можно было бы сверстать вручную или заменить на иконки

Научим кнопку «кликаться». Пускай пока она меняет своё состояние и добавляет или убирает класс у меню:

<script>
  document
    .querySelectorAll('.hamburger') // Находим кнопку-гамбургер
    .forEach(button => {
     const nav = button
      .closest('.header')
      .querySelector('.nav') // Находим ближайшее к кнопке меню

     button.addEventListener('click', () => { // По клику на кнопку добавляем или удаляем класс is-active у кнопки и меню
       button
         .classList
         .toggle('is-active')

       nav
        .classList
        .toggle('is-active')
     })
  })
</script>

Внутри <script> пишут текст на Яваскрипте — языке программирования, встроенном в браузеры. Яваскрипт используют для создания интерактивных страниц и веб‑приложений

Подключим меню к кнопке и добавим анимацию. Пусть меню приезжает сверху:

 @media (width > 690px) {
   .header .hamburger {
     display: none;
   }
 }

 @media (width <= 690px) {
   .header {
     position: relative;
     grid-template-columns: min-content 1fr 1fr min-content;
   }
   
   .header .hamburger {
     grid-column: 4;
     grid-row: 1;
   }
   
   .header .tel {
     grid-column: 2 / span 2;
     grid-row: 1;
     justify-self: stretch;
   }

   .nav {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
     padding: 65px 10px 20px;
     transform: translateY(-100%); /* Прячем меню за верхнюю границу экрана */
     transition: transform 75ms ease; /* Анимируем изменение позиции меню, синхронизировав с анимацией кнопки */
     transition-delay: 120ms;
   }

   .nav.is-active {
     transform: translateY(0); /* Когда меню активно, ставим его под шапку */
     transition-timing-function: cubic-bezier(.215, .61, .355, 1);
   }

   .nav ul {
     grid-auto-flow: row;
     grid-auto-rows: max-content;
     row-gap: 10px;
   }
}

Тайминги и скорость анимации скопировали из кнопки, чтобы не было рассинхрона

Появилась проблема с наползанием. Выезжающее меню перекрывает логотип. Поправим это, добавив слой, разделяющий выезжающее меню и логотип с кнопкой:

 @media (width > 690px) {
   .header .hamburger {
     display: none;
   }
 }

 @media (width <= 690px) {
   .header {
     position: relative;
     grid-template-columns: min-content 1fr 1fr min-content;
   }
   
   .header .hamburger {
     grid-column: 4;
     grid-row: 1;
   }
   
   .header .tel {
     grid-column: 2 / span 2;
     grid-row: 1;
     justify-self: stretch;
   }

   .nav {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
     padding: 65px 10px 20px;
     transform: translateY(calc(60px - 100%)); /* Спрячем меню под слой-разделитель */
     z-index: 1; /* Меню поедет на самом нижнем уровне */
     transition: transform 75ms ease;
     transition-delay: 120ms;
   }
  
  .nav.is-active {
    transform: translateY(0);
    transition-timing-function: cubic-bezier(.215, .61, .355, 1);
  }
  
  .nav ul {
     grid-auto-flow: row;
     grid-auto-rows: max-content;
     row-gap: 10px;
   }

  .header::before {
    content: '';
    background: inherit;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 2; /* Слой-разделитель скроет меню, пока оно проезжает под шапкой */
  }

  .header .logo,
  .header .tel,
  .header .hamburger {
    position: relative;
    z-index: 3; /* Логотип, телефон и кнопка встанут на самом верху */
  }
}

Свер­стаем про­стой под­вал. Сосре­до­то­чимся только на рас­кладке, опу­стив типо­гра­фику и стили для сброса.

Нач­нём с раз­метки. Собе­рём под­вал в кон­тей­нере <footer>. Для копи­рай­тов и адреса возь­мём дивы. Ссылки на соц­сети собе­рём в нену­ме­ро­ван­ный спи­сок <ul>

<footer class="footer">
  <div class="footer__copyright">
    <p>© Айс-бейби</p>
    <p>
      <a href="mailto:mail@icebaby.ru">
        mail@icebaby.ru
      </a>
      <br>
      +7 495 400‑50‑50
    </p>
  </div>

  <div class="footer__address">
    <p>
      <a href="/welcome/">Стать клиентом</a>
    </p>
    <p>
      Большая улица, 36, строение 2
      <br>
      Москва, Россия, 127015
    </p>
  </div>

  <ul class="footer__social">
    <li>
      <a href="https://t.me/icebaby">Телеграм</a>
    </li>
    <li>
      <a href="https://vk.com/icebaby">VK</a>
    </li>
  </ul>
</footer>

Зада­дим рас­кладку для под­вала целиком:

.footer {
  display: grid;
  grid-template-columns: max-content max-content 1fr; /* Пусть копирайты и адрес займут минимум места, а соцсети — всё оставшееся */
  padding: 10px;
  column-gap: 40px;
}

Зада­дим отступы между абзацами:

.footer {
  display: grid;
  grid-template-columns: max-content max-content 1fr;
  padding: 10px;
  column-gap: 40px;
}

.footer * + p {
  margin-top: 1em;
}

Пре­вра­тим спи­сок ссы­лок на соц­сети в грид‑кон­тей­нер с авто­ма­ти­че­ской рас­клад­кой по колон­кам, чтобы ссылки выстро­и­лись в строку:

.footer__social {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content; /* Автоматически создаём колонки по количеству ссылок с шириной по их содержимому */
  column-gap: 20px; /* Задаём межколонник в 20 пк */
}

В колонке с соц­се­тями оста­лось сво­бод­ное место, эле­менты при­жа­лись к левому краю. Ото­жмём ссылки вниз и вправо, но только на десктопе:

@media (width >= 992px) {
  .footer__social {
    align-content: end;
    justify-self: end;
  }
}

Заме­ним тек­сто­вые ссылки на соц­сети икон­ками в СВГ:

<!-- СВГ-спрайт с иконками -->
<svg width="0" height="0" class="hidden">
  <symbol id="telegram" fill="none" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48">
    <path fill-rule="evenodd" d="M46 24c0 12.15-9.85 22-22 22S2 36.15 2 24 11.85 2 24 2s22 9.85 22 22Zm-29.66-2.22c-2.08.91-4.22 1.84-6.17 2.92-1.02.75.34 1.28 1.61 1.77.2.08.4.16.59.23l.48.15c1.41.45 2.99.95 4.37.2 2.26-1.3 4.39-2.8 6.51-4.29.7-.49 1.39-.98 2.1-1.46l.11-.07c.6-.39 1.94-1.26 1.44-.06-1.18 1.28-2.43 2.42-3.7 3.57-.87.77-1.71 1.56-2.55 2.37-.73.59-1.48 1.78-.67 2.61 1.88 1.31 3.78 2.6 5.69 3.88.62.42 1.24.83 1.86 1.25 1.05.84 2.69.16 2.92-1.15l.31-1.81c.57-3.33 1.14-6.65 1.64-9.99.07-.52.15-1.05.22-1.57.19-1.27.38-2.54.44-3.82-.15-1.27-1.7-.99-2.55-.71-4.42 1.68-8.79 3.49-13.14 5.32-.49.22-.99.44-1.5.66h-.01Z" fill="#000" />
  </symbol>
  <symbol id="vk" fill="none" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48">
    <path fill-rule="evenodd" d="M2 24C2 11.85 11.85 2 24 2s22 9.85 22 22-9.85 22-22 22S2 36.15 2 24Zm23.35 8.21s.49-.05.75-.32c.23-.24.22-.71.22-.71s-.03-2.16.99-2.48c1.01-.31 2.3 2.09 3.68 3.01 1.03.7 1.82.54 1.82.54l3.67-.05s1.92-.12 1.01-1.6c-.07-.12-.53-1.1-2.73-3.1-2.3-2.09-1.99-1.76.78-5.38 1.69-2.21 2.36-3.55 2.15-4.13-.2-.55-1.44-.4-1.44-.4l-4.13.02s-.31-.04-.53.09c-.22.13-.36.44-.36.44s-.65 1.71-1.52 3.16c-1.84 3.06-2.57 3.23-2.88 3.04-.7-.44-.52-1.78-.52-2.73 0-2.97.46-4.21-.89-4.53-.45-.11-.78-.18-1.93-.19-1.47-.01-2.72 0-3.43.34-.47.22-.83.73-.61.76.27.04.89.16 1.22.6.42.56.41 1.83.41 1.83s.24 3.5-.57 3.93c-.56.3-1.32-.31-2.96-3.09-.84-1.42-1.47-3-1.47-3s-.12-.29-.34-.45c-.26-.19-.63-.25-.63-.25l-3.92.03s-.59.02-.81.27c-.19.22-.02.68-.02.68s3.07 7.05 6.55 10.61c3.19 3.26 6.81 3.05 6.81 3.05h1.64l-.01.01Z" fill="#000" />
  </symbol>
</svg>

<footer class="footer">
  <div class="footer__copyright">
    <p>© Айс-бейби</p>
    <p>
      <a href="mailto:mail@icebaby.ru">
        mail@icebaby.ru
      </a>
      <br>
      +7 495 400‑50‑50
    </p>
  </div>
  
  <div class="footer__address">
    <p>
      <a href="/welcome/">Стать клиентом</a>
    </p>
    <p>
      Большая улица, 36, строение 2
      <br>
      Москва, Россия, 127015
    </p>
  </div>

  <ul class="footer__social">
    <li>
      <a href="https://t.me/icebaby">
        <svg class="icon" width="36" height="36">
          <use xlink:href="#telegram"></use>
        </svg>
      </a>
    </li>
    <li>
      <a href="https://vk.com/icebaby">
        <svg class="icon" width="36" height="36">
          <use xlink:href="#vk"></use>
        </svg>
      </a>
    </li>
  </ul>
</footer>

Иконки — social‑icons на Гитхабе, спрайты — SVGSprit.es

Доба­вим мобиль­ную вер­сию, выстроив всё в одну колонку:

@media (width < 992px) {
  .footer {
    grid-template-columns: 1fr; /* Меняем сетку на 1 колонку и N рядов с отступом в 1.25em */
    grid-auto-rows: min-content;
    row-gap: 1.25em;
  }
}

Типичный подвал из трёх колонок: копирайтов, адреса и ссылок на соцсети

Иногда в подвалах размещают кладбище ссылок, дублируя меню из шапки сайта

Используем max‑content, а не min‑content, чтобы адрес из нескольких слов не переносился

По умолчанию браузеры задают отступы в 1em до и после абзацев

Было бы здорово вынести значения межколонников в ЦСС‑переменные

На мобильных ссылки на соцсети останутся прижатыми к левому краю

СВГ‑спрайт — одна картинка со всеми иконками, которые можно использовать отдельно

Стать клиентом

Большая улица, 36, строение 2
Москва, Россия, 127015

Меняем сетку, раскладывая всё в одну колонку и N рядов по содержимому

Стать клиентом

Большая улица, 36, строение 2
Москва, Россия, 127015

Стать клиентом

Большая улица, 36, строение 2
Москва, Россия, 127015

Типичный подвал из трёх колонок: копирайтов, адреса и ссылок на соцсети

Иногда в подвалах размещают кладбище ссылок, дублируя меню из шапки сайта

Используем max‑content, а не min‑content, чтобы адрес из нескольких слов не переносился

По умолчанию браузеры задают отступы в 1em до и после абзацев

Было бы здорово вынести значения межколонников в ЦСС‑переменные

На мобильных ссылки на соцсети останутся прижатыми к левому краю

СВГ‑спрайт — одна картинка со всеми иконками, которые можно использовать отдельно

Сверстаем простой подвал. Сосредоточимся только на раскладке, опустив типографику и стили для сброса.

Начнём с разметки. Соберём подвал в контейнере <footer>. Для копирайтов и адреса возьмём дивы. Ссылки на соцсети соберём в ненумерованный список <ul>

<footer class="footer">
  <div class="footer__copyright">
    <p>© Айс-бейби</p>
    <p>
      <a href="mailto:mail@icebaby.ru">
        mail@icebaby.ru
      </a>
      <br>
      +7 495 400‑50‑50
    </p>
  </div>

  <div class="footer__address">
    <p>
      <a href="/welcome/">Стать клиентом</a>
    </p>
    <p>
      Большая улица, 36, строение 2
      <br>
      Москва, Россия, 127015
    </p>
  </div>

  <ul class="footer__social">
    <li>
      <a href="https://t.me/icebaby">Телеграм</a>
    </li>
    <li>
      <a href="https://vk.com/icebaby">VK</a>
    </li>
  </ul>
</footer>

Зададим раскладку для подвала целиком:

.footer {
  display: grid;
  grid-template-columns: max-content max-content 1fr; /* Пусть копирайты и адрес займут минимум места, а соцсети — всё оставшееся */
  padding: 10px;
  column-gap: 40px;
}

Зададим отступы между абзацами:

.footer {
  display: grid;
  grid-template-columns: max-content max-content 1fr;
  padding: 10px;
  column-gap: 40px;
}

.footer * + p {
  margin-top: 1em;
}

Превратим список ссылок на соцсети в грид‑контейнер с автоматической раскладкой по колонкам, чтобы ссылки выстроились в строку:

.footer__social {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content; /* Автоматически создаём колонки по количеству ссылок с шириной по их содержимому */
  column-gap: 20px; /* Задаём межколонник в 20 пк */
}

В колонке с соцсетями осталось свободное место, элементы прижались к левому краю. Отожмём ссылки вниз и вправо, но только на десктопе:

@media (width >= 992px) {
  .footer__social {
    align-content: end;
    justify-self: end;
  }
}

Заменим текстовые ссылки на соцсети иконками в СВГ:

<!-- СВГ-спрайт с иконками -->
<svg width="0" height="0" class="hidden">
  <symbol id="telegram" fill="none" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48">
    <path fill-rule="evenodd" d="M46 24c0 12.15-9.85 22-22 22S2 36.15 2 24 11.85 2 24 2s22 9.85 22 22Zm-29.66-2.22c-2.08.91-4.22 1.84-6.17 2.92-1.02.75.34 1.28 1.61 1.77.2.08.4.16.59.23l.48.15c1.41.45 2.99.95 4.37.2 2.26-1.3 4.39-2.8 6.51-4.29.7-.49 1.39-.98 2.1-1.46l.11-.07c.6-.39 1.94-1.26 1.44-.06-1.18 1.28-2.43 2.42-3.7 3.57-.87.77-1.71 1.56-2.55 2.37-.73.59-1.48 1.78-.67 2.61 1.88 1.31 3.78 2.6 5.69 3.88.62.42 1.24.83 1.86 1.25 1.05.84 2.69.16 2.92-1.15l.31-1.81c.57-3.33 1.14-6.65 1.64-9.99.07-.52.15-1.05.22-1.57.19-1.27.38-2.54.44-3.82-.15-1.27-1.7-.99-2.55-.71-4.42 1.68-8.79 3.49-13.14 5.32-.49.22-.99.44-1.5.66h-.01Z" fill="#000" />
  </symbol>
  <symbol id="vk" fill="none" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 48 48">
    <path fill-rule="evenodd" d="M2 24C2 11.85 11.85 2 24 2s22 9.85 22 22-9.85 22-22 22S2 36.15 2 24Zm23.35 8.21s.49-.05.75-.32c.23-.24.22-.71.22-.71s-.03-2.16.99-2.48c1.01-.31 2.3 2.09 3.68 3.01 1.03.7 1.82.54 1.82.54l3.67-.05s1.92-.12 1.01-1.6c-.07-.12-.53-1.1-2.73-3.1-2.3-2.09-1.99-1.76.78-5.38 1.69-2.21 2.36-3.55 2.15-4.13-.2-.55-1.44-.4-1.44-.4l-4.13.02s-.31-.04-.53.09c-.22.13-.36.44-.36.44s-.65 1.71-1.52 3.16c-1.84 3.06-2.57 3.23-2.88 3.04-.7-.44-.52-1.78-.52-2.73 0-2.97.46-4.21-.89-4.53-.45-.11-.78-.18-1.93-.19-1.47-.01-2.72 0-3.43.34-.47.22-.83.73-.61.76.27.04.89.16 1.22.6.42.56.41 1.83.41 1.83s.24 3.5-.57 3.93c-.56.3-1.32-.31-2.96-3.09-.84-1.42-1.47-3-1.47-3s-.12-.29-.34-.45c-.26-.19-.63-.25-.63-.25l-3.92.03s-.59.02-.81.27c-.19.22-.02.68-.02.68s3.07 7.05 6.55 10.61c3.19 3.26 6.81 3.05 6.81 3.05h1.64l-.01.01Z" fill="#000" />
  </symbol>
</svg>

<footer class="footer">
  <div class="footer__copyright">
    <p>© Айс-бейби</p>
    <p>
      <a href="mailto:mail@icebaby.ru">
        mail@icebaby.ru
      </a>
      <br>
      +7 495 400‑50‑50
    </p>
  </div>
  
  <div class="footer__address">
    <p>
      <a href="/welcome/">Стать клиентом</a>
    </p>
    <p>
      Большая улица, 36, строение 2
      <br>
      Москва, Россия, 127015
    </p>
  </div>

  <ul class="footer__social">
    <li>
      <a href="https://t.me/icebaby">
        <svg class="icon" width="36" height="36">
          <use xlink:href="#telegram"></use>
        </svg>
      </a>
    </li>
    <li>
      <a href="https://vk.com/icebaby">
        <svg class="icon" width="36" height="36">
          <use xlink:href="#vk"></use>
        </svg>
      </a>
    </li>
  </ul>
</footer>

Иконки — social‑icons на Гитхабе, спрайты — SVGSprit.es

Добавим мобильную версию, выстроив всё в одну колонку:

@media (width < 992px) {
  .footer {
    grid-template-columns: 1fr; /* Меняем сетку на 1 колонку и N рядов с отступом в 1.25em */
    grid-auto-rows: min-content;
    row-gap: 1.25em;
  }
}

Стать клиентом

Большая улица, 36, строение 2
Москва, Россия, 127015

Визитка

Визитка — это стра­ница с тек­стом о себе. Часто визитку сов­ме­щают с резюме, добав­ляя опыт работы, навыки, доклады и выступ­ле­ния на кон­фе­рен­циях. Боль­шин­ство визи­ток состоит из тек­ста, списка ссы­лок и фото­гра­фии автора, зада­ю­щей настро­е­ние. Они не очень объ­ём­ные, но с типо­гра­фи­че­скими изысками.

Визитка

Визитка — это страница с текстом о себе. Часто визитку совмещают с резюме, добавляя опыт работы, навыки, доклады и выступления на конференциях. Большинство визиток состоит из текста, списка ссылок и фотографии автора, задающей настроение. Они не очень объёмные, но с типографическими изысками.

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