惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
Jina AI
Jina AI
月光博客
月光博客
博客园 - Franky
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Visual Studio Blog
有赞技术团队
有赞技术团队
V
V2EX
IT之家
IT之家
阮一峰的网络日志
阮一峰的网络日志
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
腾讯CDC
D
DataBreaches.Net
Hugging Face - Blog
Hugging Face - Blog
Martin Fowler
Martin Fowler
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
WordPress大学
WordPress大学
C
Check Point Blog
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Помогаю новичкам найти ошибки в вёрстке ChatGPT
melnik909 (R · 2026-05-12 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение9 мин

Охват и читатели12

Обзор

Привет, Хабр!

Мне кажется, что сегодня начинающие фронтендеры уже не пишут код сами. Они отдают всё на откуп искусственному интеллекту. Хорошо это или плохо — судить не буду.

Я хочу помочь вам найти плохие решения в вёрстке, которые ChatGPT вам предлагает. Для этого я собрал наиболее популярные примеры, которые очень просто исправить. Я вам всё покажу.

Только, пожалуйста, не судите меня строго. Я не очень хорошо умею пользоваться ChatGPT. Но примеры взяты из реальных приложений. Так что о них в любом случае стоит знать.

Давайте посмотрим, что я вам подготовил.

Не копируйте находящийся рядом текст в качестве значения атрибута alt

Альтернативный текст является очень важной штукой для пользователей скринридера и одновременно суперсложной для разработчиков. Сложность заключается в том, что есть ситуации, когда альтернативный текст только ухудшает воспринимаемость интерфейсов.

Давайте рассмотрим пример с вёрсткой раздела «Наши сотрудники». В ChatGPT я отправил вот такой запрос:

«Давай напишем разметку раздела «Наши сотрудники». Блок содержит три блока с информацией о каждом сотруднике. Один такой блок содержит фото сотрудника, имя, должность и ссылки на социальные сети. Название классов надо сделать по БЭМ».

В результате он отправил мне разметку.

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="Иван Петров" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          <!-- здесь разметка соц.сетей -->
        </article>

        <article class="team-card">
          <img alt="Анна Смирнова" class="team-card__photo" src="images/employee-2.jpg"  width="280" height="320">
          <h3 class="team-card__name">Анна Смирнова</h3>
          <p class="team-card__position">Проектировщик</p>
          <!-- здесь разметка соц.сетей -->
        </article>

        <article class="team-card">
          <img alt="Дмитрий Волков" class="team-card__photo" src="images/employee-3.jpg"  width="280" height="320">
          <h3 class="team-card__name">Дмитрий Волков</h3>
          <p class="team-card__position">Менеджер проекта</p>
          <!-- здесь разметка соц.сетей -->
        </article>
      </div>
    </div>
  </section>
</body>

Обращу ваше внимание на то, какой текст добавил ChatGPT в атрибут alt. Он такой же, как в элементе h3, который находится рядом. На примере разметки блока первого сотрудника покажу, какой результат получит пользователь скринридера NVDA.

Сначала я слышу «Иван Петров. Изображение». Нажимаю клавишу со стрелкой вниз (). Слышу «Иван Петров. Заголовок третьего уровня». Ещё раз нажимаю клавишу со стрелкой вниз (). Слышу «Разработчик интерфейсов».

Дважды озвученные имя и фамилия сотрудника бессмысленны для пользователя. Он не получит ничего полезного. Мой незрячий знакомый Илья называет такой текст спамом.

«Для меня подобные описания изображений — это спам. У меня весь день ридер болтает в телефоне и ноутбуке, поэтому я прослушиваю очень много информации. Обычно к вечеру едет крыша. Да и слух притупляется. Поэтому всё лишнее стараюсь отбрасывать».

Лучшим решением этой проблемы будет скрыть изображение, объявив атрибут alt с пустым значением.

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          <!-- здесь разметка соц.сетей -->
        </article>
        <!-- здесь оставшиеся блоки -->
      </div>
    </div>
  </section>
</body>

Теперь скринридер NVDA сначала скажет «Иван Петров. Заголовок третьего уровня». Если нажать клавишу со стрелкой вниз (), то пользователь услышит «Разработчик интерфейсов».

Избегайте детализированных слов в названиях классов

Недавно мне попалась на глаза шпаргалка по названию классов по БЭМу. В целом она очень крутая, но для меня был один важный нюанс, который резал глаза.

При формировании задания в предыдущем примере я использовал фразу ссылки на социальные сети. Мне было интересно посмотреть, какие названия будет использовать ChatGPT для этого блока.

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="Иван Петров" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Frontend Developer</p>
          
          <ul class="team-card__socials">
            <li class="team-card__socials-item">
              <a class="team-card__socials-link" href="#" aria-label="Телеграм">
                <!-- здесь иконка --->
              </a>
            </li>
            <!-- здесь оставшиеся ссылки на соц.сети -->
          </ul>
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

Обращу ваше внимание на класс team-card__socials-link. В нём конкретно передаётся сущность элемента, то есть он является ссылкой. Но что произойдёт, если он будет размечен элементом button?

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="Иван Петров" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Frontend Developer</p>
          
          <ul class="team-card__socials">
            <li class="team-card__socials-item">
              <button class="team-card__socials-link" type="button" aria-label="Телеграм">
                <!-- здесь иконка --->
              </button>
            </li>
            <!-- здесь оставшиеся ссылки на соц.сети -->
          </ul>
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

Уже происходит путаница. В коде используется элемент button, а класс у него содержит слово «ссылка». Читая такой код, у другого разработчика может всплыть вопрос: «Так это кнопка или ссылка?».

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

В нашем примере уже используется классное название team-card__socials-item. Но оно добавлено к элементу li. Давайте у него уберём этот класс и добавим класс team-card__socials-group. А класс team-card__socials-item добавим для элемента a.

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="Иван Петров" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          
          <ul class="team-card__socials">
            <li class="team-card__socials-group">
              <a class="team-card__socials-item" href="#" aria-label="Телеграм">
                <!-- здесь иконка --->
              </a>
            </li>
            <!-- здесь оставшиеся ссылки на соц.сети -->
          </ul>
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

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

Для классов и переменных, которые применяются к кнопкам или ссылкам, я вместо слов link или button использую слово control, потому что эти элементы для меня являются элементами управления.

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="Иван Петров" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          
          <ul class="team-card__socials">
            <li class="team-card__socials-group">
              <a class="team-card__socials-control" href="#" aria-label="Телеграм">
                <!-- здесь иконка --->
              </a>
            </li>
            <!-- здесь оставшиеся ссылки на соц.сети -->
          </ul>
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

А как вы поступаете в таких ситуациях? Какие слова используете в классах, чтобы они одновременно могли применяться к элементам a и button. Поделитесь, пожалуйста, в комментариях.

Связывайте элемент section с его заголовком

Я много внимания уделяю теме адаптации интерфейсов для пользователей скринридера. К сожалению, не всегда можно обойтись только HTML-элементами. Приходится их улучшать ARIA-атрибутами.

Яркий пример — это элемент section. Давайте посмотрим, как скринридер NVDA поймёт разметку раздела «Наши сотрудники» в специальном режиме «Список элементов», нажав комбинацию клавиш insert и f7.

<body>
  <section class="team">
    <div class="team__container">
      <h2 class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          <!-- здесь разметка соц.сетей -->
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

Ничего нет. Для скринридера наш раздел не существует.

Улучшить понимание элемента sectionможно, связав его с заголовком, который относится к нему. Это реализуется с помощью атрибута aria-labelledby . Если вы раньше не слышали о нём, ничего страшного. Сейчас всё расскажу.

На самом деле атрибут aria-labelledby работает как атрибут for для элемента label. Мы просто объявляем его со значением, которое объявлено для атрибута id у элемента, который содержит краткое описание.

Для демонстрации добавлю атрибуты для элементов section и h2.

<body>
  <section class="team">
    <div class="team__container" aria-labelledby="team-heading">
      <h2 id="team-heading" class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          <!-- здесь разметка соц.сетей -->
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

Вот так лучше. В списке отобразился элемент «Наши сотрудники». Теперь скринридер NVDA будет сообщать пользователю, что он попал в раздел «Наши сотрудники».

Откладывайте загрузку второстепенных ресурсов

Раздел «Наши сотрудники» является не самым важным на странице. Но в тоже время он содержит изображения, которые могут загружаться долго. Получается ситуация, когда второстепенные ресурсы могут занимать время, которое могло бы пойти на загрузку более важной информации.

Что можно сделать? Из простых решений можно указать приоритет ресурса. Именно его браузеры используют при загрузке страницы. Если он низкий, то они будут загружать его в последнюю очередь.

В HTML есть атрибут fetchpriority. Он как раз указывает браузерам приоритет ресурса. Если для атрибута объявлено значение high, то у ресурса будет наивысший приоритет. Если же используется значение low, то наименьший.

Например, в нашем разделе у изображений как раз должен быть низкий приоритет. Давайте для каждого элемента img объявим значение low для атрибута fetchpriority.

<body>
  <section class="team">
    <div class="team__container" aria-labelledby="team-heading">
      <h2 id="team-heading" class="team__title">Наши сотрудники</h2>
      <div class="team__list">
        <article class="team-card">
          <img alt="" fetchpriority="low" class="team-card__photo" src="images/employee-1.jpg"  width="280" height="320">
          <h3 class="team-card__name">Иван Петров</h3>
          <p class="team-card__position">Разработчик интерфейсов</p>
          <!-- здесь разметка соц.сетей -->
        </article>

        <!-- здесь оставшиеся элементы -->
      </div>
    </div>
  </section>
</body>

Может быть, ChatGPT не добавил атрибут fetchpriority, потому что он относительно новый. Но вы не бойтесь. Вы можете спокойно использовать его в своих проектах. Атрибут поддерживается во всех браузерах с конца 2024 года.

Заключение

Давайте подведём итог. В этой статье я поделился следующими советами:

  • не дублируйте текст, находящийся рядом, в качестве значения атрибута alt;

  • названия классов не должны содержать слова, обозначающие детализированную информацию (сущность элемента, цвет, позицию и т. д.);

  • свяжите элемент section с вложенным заголовком при помощи атрибутов aria-labelledby и id;

  • откладывайте загрузку изображений с помощью атрибута fetchpriority, если они второстепенные.

Дополнительно хочу попросить вас. Поделитесь, пожалуйста, в комментариях своими советами, касающимися HTML и CSS, которые вы бы дали новичкам.

А если вы только начинаете путь в разработке, пожалуйста, не полагайтесь слепо на помощь ChatGPT. Конечно, он вам помогает. Но он может очень легко внедрить плохие решения в ваш код. Будьте бдительны!

На этом я прощаюсь. Спасибо за чтение!

P. S. Помогаю больше узнать про CSS и дружелюбные интерфейсы в своих закрытых ТГ-каналах CSS isn't magic и UX + Dev = a11y. Присоединяйтесь. Как вступить, написано в профиле.

© 2026 ООО «МТ ФИНАНС»