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

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

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

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

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

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

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

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

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

Оглавление

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

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

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

Оглавление

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

Доба­вим ещё ряд и собе­рём всё вместе:

<!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;
  }
}

Рас­кладка как в Пин­те­ресе — это част­ный слу­чай плитки, в кото­рой высота ячеек пере­мен­ная. Кар­точки впи­сы­ва­ются в колонки, а по рядам рас­по­ла­га­ются так, чтобы при­жаться вплот­ную друг к другу.

К сожа­ле­нию, в ЦСС пока нет хоро­шей под­держки этой рас­кладки. В свет­лом буду­щем она могла бы вклю­чаться с помо­щью grid-template-rows: masonry. Но пока это под­дер­жи­ва­ется только в послед­ней вер­сии Сафари и Файр­фокса, если вклю­чить в настройках.

.projects {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-auto-rows: max-content;
  grid-template-rows: masonry;
  column-gap: 20px;
  row-gap: 20px;
}

Пока под­держка в бра­у­зе­рах хро­мает, можно сыми­ти­ро­вать такую рас­кладку вруч­ную. Для этого нужно назна­чить про­ек­там колонки, следя за их высо­той. Не должно быть слиш­ком высо­ких или слиш­ком низ­ких коло­нок, ниж­ний край рас­кладки дол­жен быть как можно ров­нее. В Пин­те­ресе за этим авто­ма­ти­че­ски сле­дит код, в нашем слу­чае — мы сами.

<section class="projects">
  <!-- Добавим колонки, по которым будем расставлять проекты: -->
  <div class="col"></div>
  <div class="col"></div>
  <div class="col"></div>
  <div class="col"></div>
  
  <div class="project col-1"></div> <!-- Этот проект пойдёт в первую колонку -->
  <div class="project col-2"></div> <!-- Этот проект пойдёт во вторую колонку -->
  <div class="project col-3"></div> <!-- В третью колонку -->
  <div class="project col-4"></div> <!-- В четвёртую колонку -->
</section>

<script>
  const columns = document
    .querySelectorAll('.projects > .col') // Находим колонки

  columns.forEach((column, index) => {
    const projects = document
      .querySelectorAll(`.project.col-${index + 1}`)

    column.append(...projects) // Перемещаем в них проекты с подходящим классом
  })
</script>
.projects {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  column-gap: 20px;
}

.projects > .col { /* Теперь грид-контейнер содержит только четыре колонки, а проекты вложены в них */
  display: grid;
  grid-auto-rows: max-content;
  row-gap: 20px;
}

.project {
  padding: 10px 10px 20px;
  background: #f3f5f4;
  border-radius: 4px;
  overflow: hidden;
}

.project a {
  color: var(--textColor);
}

.project-media {
  margin: -10px -10px 0;
}

.project-caption {
  margin-top: .5em;
  font-size: 14px;
  line-height: 1.25;
}

@media (width <= 768px) {
  .projects { /* На мобильных перестраиваем колонки по две в ряд */
    grid-template-columns: 1fr 1fr;
  }
}

И, конечно, есть тре­тий вари­ант сде­лать такую рас­кладку — с помо­щью про­грам­ми­ро­ва­ния и гото­вых библиотек:

Раскладка как в Пинтересе — это частный случай плитки, в которой высота ячеек переменная. Карточки вписываются в колонки, а по рядам располагаются так, чтобы прижаться вплотную друг к другу.

К сожалению, в ЦСС пока нет хорошей поддержки этой раскладки. В светлом будущем она могла бы включаться с помощью grid-template-rows: masonry. Но пока это поддерживается только в последней версии Сафари и Файрфокса, если включить в настройках.

.projects {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-auto-rows: max-content;
  grid-template-rows: masonry;
  column-gap: 20px;
  row-gap: 20px;
}

Пока поддержка в браузерах хромает, можно сымитировать такую раскладку вручную. Для этого нужно назначить проектам колонки, следя за их высотой. Не должно быть слишком высоких или слишком низких колонок, нижний край раскладки должен быть как можно ровнее. В Пинтересе за этим автоматически следит код, в нашем случае — мы сами.

<section class="projects">
  <!-- Добавим колонки, по которым будем расставлять проекты: -->
  <div class="col"></div>
  <div class="col"></div>
  <div class="col"></div>
  <div class="col"></div>
  
  <div class="project col-1"></div> <!-- Этот проект пойдёт в первую колонку -->
  <div class="project col-2"></div> <!-- Этот проект пойдёт во вторую колонку -->
  <div class="project col-3"></div> <!-- В третью колонку -->
  <div class="project col-4"></div> <!-- В четвёртую колонку -->
</section>

<script>
  const columns = document
    .querySelectorAll('.projects > .col') // Находим колонки

  columns.forEach((column, index) => {
    const projects = document
      .querySelectorAll(`.project.col-${index + 1}`)

    column.append(...projects) // Перемещаем в них проекты с подходящим классом
  })
</script>
.projects {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  column-gap: 20px;
}

.projects > .col { /* Теперь грид-контейнер содержит только четыре колонки, а проекты вложены в них */
  display: grid;
  grid-auto-rows: max-content;
  row-gap: 20px;
}

.project {
  padding: 10px 10px 20px;
  background: #f3f5f4;
  border-radius: 4px;
  overflow: hidden;
}

.project a {
  color: var(--textColor);
}

.project-media {
  margin: -10px -10px 0;
}

.project-caption {
  margin-top: .5em;
  font-size: 14px;
  line-height: 1.25;
}

@media (width <= 768px) {
  .projects { /* На мобильных перестраиваем колонки по две в ряд */
    grid-template-columns: 1fr 1fr;
  }
}

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

Промостраница

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

На про­мах обычно раз­ме­щают при­зыв к дей­ствию (call to action) — модуль с кноп­кой покупки, фор­мой реги­стра­ции или теле­фо­ном для связи.

Раз­бе­рём вёрстку про­мо­стра­ницы на при­мере бюро.

Промостраница

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

На промах обычно размещают призыв к действию (call to action) — модуль с кнопкой покупки, формой регистрации или телефоном для связи.

Разберём вёрстку промостраницы на примере бюро.

Выра­зи­тель­ную шапку стра­ницы в запад­ном веб‑дизайне назы­вают «героем» (hero). Часто в её вёрстке исполь­зуют aspect-ratio и абсо­лют­ное пози­ци­о­ни­ро­ва­ние, чтобы рас­ста­вить модули вокруг и поверх текста.

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

<!-- Контейнер на всю ширину -->
<div class="hero">

  <!-- Контентная область с ограниченной шириной -->
  <div class="heroIn">
    <div class="heroInfo">
      <b>Дистанционный курс …</b>
      <p>Перерождение классического курса …</p>
      <p>
        <span class="serif">24 февраля — 22 марта.</span>
        <b class="heroExpire">Запись открыта до ВС 23 фев</b>
      </p>
    </div>

    <h1 class="heroTitle">
      <!-- Здесь br уместен -->
      Переговоры <br/>
      и отношения <br/>
      с клиентами

      <span class="heroTitleVersion">2.0</span>
    </h1>

    <img class="heroAuthor" src="./ilya.png" alt="Фото Ильи Синельникова">

    <img class="heroGorilla" src="./gorilla.png" alt="…">
    <img class="heroChuvak" src="./chuvak.png" alt="…">
  </div>
</div>
.hero {
  background: #FCBF32;
  color: #000;
  padding-top: 60px;
}

.heroIn {
  max-width: 768px;
  margin: 0 auto;
  aspect-ratio: 16/9;
  /* Контекст для позиционирования «абсолютных» модулей */
  position: relative;
}

.heroInfo {
  margin-bottom: 10px;
}

.heroExpire {
  color: #fc5621;
}

.heroTitle {
  font-size: 6vw;
  text-transform: uppercase;
}

.heroTitleVersion {
  display: block;
  text-align: right;
  color: #fc5621;
}

.heroAuthor {
  position: absolute;
  bottom: 0;
  /* Трюк для центровки модуля: сдвиг 50% родителя + смещение -50% собственной ширины*/
  left: 50%;
  translate: -50% 0;

  max-width: 50%;
}

.heroGorilla {
  position: absolute;
  top: 5%;
  right: 3%;
  max-width: 25%;
  transform: rotate(-5deg);
}

.heroChuvak {
  position: absolute;
  bottom: 25%;
  left: 7%;
  max-width: 15%;
  transform: rotate(-5deg);
}

Выразительную шапку страницы в западном веб‑дизайне называют «героем» (hero). Часто в её вёрстке используют aspect-ratio и абсолютное позиционирование, чтобы расставить модули вокруг и поверх текста.

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

<!-- Контейнер на всю ширину -->
<div class="hero">

  <!-- Контентная область с ограниченной шириной -->
  <div class="heroIn">
    <div class="heroInfo">
      <b>Дистанционный курс …</b>
      <p>Перерождение классического курса …</p>
      <p>
        <span class="serif">24 февраля — 22 марта.</span>
        <b class="heroExpire">Запись открыта до ВС 23 фев</b>
      </p>
    </div>

    <h1 class="heroTitle">
      <!-- Здесь br уместен -->
      Переговоры <br/>
      и отношения <br/>
      с клиентами

      <span class="heroTitleVersion">2.0</span>
    </h1>

    <img class="heroAuthor" src="./ilya.png" alt="Фото Ильи Синельникова">

    <img class="heroGorilla" src="./gorilla.png" alt="…">
    <img class="heroChuvak" src="./chuvak.png" alt="…">
  </div>
</div>
.hero {
  background: #FCBF32;
  color: #000;
  padding-top: 60px;
}

.heroIn {
  max-width: 768px;
  margin: 0 auto;
  aspect-ratio: 16/9;
  /* Контекст для позиционирования «абсолютных» модулей */
  position: relative;
}

.heroInfo {
  margin-bottom: 10px;
}

.heroExpire {
  color: #fc5621;
}

.heroTitle {
  font-size: 6vw;
  text-transform: uppercase;
}

.heroTitleVersion {
  display: block;
  text-align: right;
  color: #fc5621;
}

.heroAuthor {
  position: absolute;
  bottom: 0;
  /* Трюк для центровки модуля: сдвиг 50% родителя + смещение -50% собственной ширины*/
  left: 50%;
  translate: -50% 0;

  max-width: 50%;
}

.heroGorilla {
  position: absolute;
  top: 5%;
  right: 3%;
  max-width: 25%;
  transform: rotate(-5deg);
}

.heroChuvak {
  position: absolute;
  bottom: 25%;
  left: 7%;
  max-width: 15%;
  transform: rotate(-5deg);
}

Ино­гда нет вре­мени или воз­мож­но­сти вер­стать слож­ную типо­гра­фику заголовка.

Можно сде­лать заго­ло­вок кар­тин­кой, но это плохо для поис­ко­вых робо­тов и про­грамм чте­ния с экрана.

Трюк: раз­ме­стите поверх кар­тинки визу­ально скры­тый «чест­ный» заго­ло­вок. Поставьте текст как можно точ­нее по кар­тинке, чтобы выде­ле­ние тек­ста выгля­дело адекватно.

Скройте текст, сде­лав его прозрачным.

Опасность. Исполь­зуйте трюк исклю­чи­тельно для дуб­ли­ро­ва­ния тек­ста, содер­жа­ще­гося на кар­тинке. Наме­рен­ное скры­тие тек­ста от поль­зо­ва­те­лей назы­ва­ется гостин­гом (ghosting) и сурово нака­зы­ва­ется поис­ко­ви­ками, вплоть до исклю­че­ния сайта из поис­ко­вой выдачи.

<div class="fakeHeading">
  <!-- По возможности используйте СВГ -->
  <img class="fakeHeadingImg" src="./fake-heading.svg" alt="Красивый заголовок: переговоры и отношения с клиентами">

  <h1 class="fakeHeadingHidden">
    Переговоры <br>
    и отношения <br>
    с клиентами
  </h1>
</div>
.fakeHeading {
  max-width: 100%;
  position: relative;
  overflow: hidden; /* На всякий случай, чтобы текст настоящего заголовка не протёк за пределы модуля */
}

.fakeHeadingImg {
  display: block;
  width: 100%;
}

.fakeHeadingHidden {
  position: absolute;
  top: 0;
  left: 0;
  font-size: 5vw;
  color: transparent;

  /* Подгоняем текст по к картинке */
  margin: 0;
  text-transform: uppercase;
  font-size: 12.2vw;
  line-height: 1.05;
}
Пере­го­воры
и отно­ше­ния
с кли­ен­тами
Пере­го­воры
и отно­ше­ния
с кли­ен­тами
Переговоры
и отношения
с клиентами
Переговоры
и отношения
с клиентами

Иногда нет времени или возможности верстать сложную типографику заголовка.

Можно сделать заголовок картинкой, но это плохо для поисковых роботов и программ чтения с экрана.

Трюк: разместите поверх картинки визуально скрытый «честный» заголовок. Поставьте текст как можно точнее по картинке, чтобы выделение текста выглядело адекватно.

Скройте текст, сделав его прозрачным.

Опасность. Используйте трюк исключительно для дублирования текста, содержащегося на картинке. Намеренное скрытие текста от пользователей называется гостингом (ghosting) и сурово наказывается поисковиками, вплоть до исключения сайта из поисковой выдачи.

<div class="fakeHeading">
  <!-- По возможности используйте СВГ -->
  <img class="fakeHeadingImg" src="./fake-heading.svg" alt="Красивый заголовок: переговоры и отношения с клиентами">

  <h1 class="fakeHeadingHidden">
    Переговоры <br>
    и отношения <br>
    с клиентами
  </h1>
</div>
.fakeHeading {
  max-width: 100%;
  position: relative;
  overflow: hidden; /* На всякий случай, чтобы текст настоящего заголовка не протёк за пределы модуля */
}

.fakeHeadingImg {
  display: block;
  width: 100%;
}

.fakeHeadingHidden {
  position: absolute;
  top: 0;
  left: 0;
  font-size: 5vw;
  color: transparent;

  /* Подгоняем текст по к картинке */
  margin: 0;
  text-transform: uppercase;
  font-size: 12.2vw;
  line-height: 1.05;
}
Скрыто 22 разворота