Как избавиться от переносов заголовков в неправильных местах
Исправьте разрывы заголовков и длинные URL с нужными CSS и HTML: nbsp, wbr, overflow-wrap:anywhere, word-break:break-all, hyphens:auto, text-wrap.
Заголовок, разрывающийся после короткого слова, одинокое слово, оставшееся на последней строке, и длинный URL, растягивающий свой контейнер, — это три разных дефекта, и каждый из них, как правило, требует своего решения на CSS или HTML.
Есть у них и одна общая неприятная черта: они проявляются только при определённой ширине контейнера. Дизайнер присылает скриншот сломанного заголовка на телефоне шириной 384px, вы открываете страницу на десктопе — и всё выглядит нормально. Плохие переносы обычно живут в узких диапазонах ширины вьюпорта, которые никто не тестировал. Именно поэтому просмотр записей сессий на тех ширинах, с которыми реально заходили пользователи, часто оказывается самым быстрым способом увидеть дефект таким, каким он был.
В этой статье мы разберём каждую из видимых проблем, инструмент для её решения и логику, которая поможет вам в следующий раз выбрать правильный подход.
Ключевые выводы
- Для длинных URL и токенов используйте
overflow-wrap: anywhere, а неbreak-word: значениеanywhereучитывает свои аварийные точки переноса при расчётеmin-content, поэтому flex- или grid-элемент действительно может сжаться. - Чтобы запретить перенос в конкретном месте, замените переносимый символ его неразрывным аналогом:
(U+00A0) для пробела,‑(U+2011) для дефиса,⁠(U+2060) там, где пробела нет вовсе. - Чтобы разрешить перенос в конкретном месте, используйте
<wbr>для переноса без дефиса или­(U+00AD), который остаётся невидимым до момента переноса, а затем отображается как дефис. word-break: break-allрубит обычные слова в конце каждой строки; приберегите его для хешей и токенов, где границы слов не несут смысла.text-wrap: balanceтолько перераспределяет существующие точки переноса, никогда не изменяет размер элемента, и браузеры ограничивают его короткими блоками: шесть строк в Chromium, десять в Firefox.
Как запретить перенос в конкретном месте?
Чтобы браузер не разрывал строку в определённой точке, замените переносимый символ его неразрывным аналогом. Неразрывный пробел ( , U+00A0) удерживает два слова на одной строке, заменяя обычный пробел между ними тем, по которому браузеру запрещено делать перенос. Это решение для заголовка, который переносится сразу после предлога или разрывает название продукта на две строки.
Текст может переноситься и там, где пробела нет, — чаще всего после дефиса. Соединитель слов (U+2060, записывается как именованная ссылка ⁠, чувствительная к регистру) запрещает перенос, не добавляя никакой ширины, а неразрывный дефис (U+2011, ‑, у которого нет именованной сущности) отображает дефис, который не может стать точкой переноса:
<h2>Shipping the new editor to everyone</h2>
<p>Take the exit for I‑95 north.</p>
Как разрешить перенос в конкретном месте?
Зеркальная задача — длинная строка, которую вы хотите перенести, но только в выбранных вами местах. Добавление элемента <wbr> даёт браузеру одну дополнительную возможность завершить строку, причём при переносе ничего видимого не вставляется. Мягкий перенос ­ (U+00AD) обозначает такую же возможность, но при переносе отображает дефис; до этого момента он невидим. Он работает при значении по умолчанию hyphens: manual, так что перестаёт действовать только в том случае, если где-то задано hyphens: none.
<p>https://docs.example.com<wbr>/reference<wbr>/text-wrapping</p>
<p>The rise of hyper­modularization</p>
Используйте <wbr> для отображаемых URL и идентификаторов, ­ — для настоящих слов.
Длинные неразрывные строки: overflow-wrap: anywhere
Для пользовательского контента, длинных URL и токенов, которые нельзя разметить вручную, задайте overflow-wrap: anywhere и позвольте браузеру делать перенос в качестве крайней меры. Ключевая деталь здесь — внутренние (intrinsic) размеры. Оба значения позволяют браузеру разбить длинное слово, но только anywhere позволяет этим аварийным точкам переноса учитываться при расчёте ширины min-content элемента; при break-word эта ширина остаётся равной длине всей неразорванной строки. Вот почему flex-элемент с длинным URL при break-word всё равно может отказаться сжиматься и разломать вёрстку, а при anywhere сжимается корректно. CSS Text Level 4 определяет эти два значения идентично, за исключением этого единственного отличия, — и именно с ним люди сталкиваются на практике.
.card { display: flex; gap: 1rem; }
.card-body {
/* break-word leaves min-content at the full URL width */
overflow-wrap: anywhere;
}
Значение anywhere работает во всех современных движках; последним его реализовал Safari в версии 15.4 (март 2022). В старых кодовых базах иногда встречается устаревшее word-break: break-word, которое в итоге переносит текст точно так же, как overflow-wrap: anywhere, оставляя при этом само word-break в обычном поведении. Замените его.
Где уместен word-break: break-all?
word-break: break-all ставит перенос там, где в строке заканчивается место, даже посреди слова. Дефис при этом не добавляется, и целое слово не переносится на следующую строку ради сохранения его целостности, поэтому обычная проза оказывается разрубленной на каждом конце строки. Это делает его неподходящим инструментом для прозы и подходящим — для строк, где границы слов не несут смысла: хеши коммитов, API-ключи, токены в ячейках таблиц. Его значение keep-all решает противоположную задачу для CJK-языков, подавляя переносы между иероглифами.
.commit-hash {
font-family: monospace;
word-break: break-all; /* never on prose */
}
Узкие колонки: hyphens: auto плюс атрибут lang
Там, где узкая колонка вынуждает делать переносы посреди слова, словарно корректная точка переноса читается куда лучше, чем грубое обрубание. hyphens: auto не делает ничего, если у элемента или одного из его предков нет атрибута lang. Каждый язык разбивает слова по-своему, поэтому браузеру нужно знать, в каком словаре искать, прежде чем расставлять переносы. Свойство имеет статус Baseline Widely available и работает без префиксов во всех браузерах с сентября 2023 года.
<aside lang="en" class="sidebar-note">Uncharacteristically long words wrap cleanly here.</aside>
.sidebar-note { hyphens: auto; }
Заголовки и абзацы: text-wrap balance и pretty
Для случая с одиноким словом на последней строке заголовка text-wrap: balance выравнивает длины строк, иначе распределяя их по уже существующим точкам переноса; новых точек оно не создаёт и размер бокса элемента не меняет, поэтому сбалансированный заголовок сохраняет исходную ширину. Балансировка обходится дорого, поэтому браузеры её ограничивают: Chromium останавливается на шести строках, Firefox — на десяти, а более длинные блоки переносятся обычным образом. text-wrap: pretty рассчитано на основной текст и тратит больше усилий на раскладку, чтобы избежать висячих строк; оно поддерживается в Chromium и в Safari, который выпустил его в версии 26, но пока не в Firefox. Само свойство text-wrap работает во всех актуальных браузерах с марта 2024 года.
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
Справочник «проблема — инструмент»
| Видимый дефект | Инструмент |
|---|---|
| Заголовок переносится после короткого слова | между словами, которые должны остаться вместе |
| Перенос происходит по дефису, который должен держать слово вместе | ‑ (неразрывный дефис) или ⁠ |
| Длинную строку нужно перенести в одном выбранном месте | <wbr> (без дефиса) или ­ (с дефисом при переносе) |
| URL или токен растягивает карточку или ячейку грида | overflow-wrap: anywhere |
| Хеш выходит за пределы ячейки таблицы | word-break: break-all |
| Узкая колонка рубит слова посреди слога | hyphens: auto плюс атрибут lang |
| Одно слово в одиночестве на последней строке заголовка | text-wrap: balance |
| Висячие слова в абзацах основного текста | text-wrap: pretty |
Общий принцип, стоящий за всем этим набором инструментов: определите, что требуется дефекту — запретить перенос, разрешить перенос или задать общую стратегию переноса, — а затем примените самый узконаправленный инструмент, который решает задачу. Разумная база: overflow-wrap: anywhere на контейнерах с пользовательским контентом, balance на заголовках и pretty на абзацах; символьные сущности приберегите для отдельных переносов, на которые дизайнер укажет уже после этого.
Часто задаваемые вопросы
В чём разница между overflow-wrap и word-break?
overflow-wrap вступает в дело только тогда, когда слово слишком длинное, чтобы поместиться на отдельной строке, — всё остальное переносится как обычно. word-break: break-all меняет стратегию для всего элемента и разрывает слово везде, где в строке заканчивается место. Используйте overflow-wrap: anywhere для эпизодических длинных URL внутри обычной прозы, а word-break: break-all — только для хешей и токенов, где границы слов не несут смысла.
word-wrap — это то же самое, что overflow-wrap?
Да. Свойство начиналось как расширение Microsoft под именем word-wrap, позже получило стандартное название overflow-wrap, и браузеры до сих пор принимают старое имя как псевдоним нового. В новом коде пишите overflow-wrap; современным браузерам word-wrap в качестве запасного варианта не нужен, а стили, объявляющие оба, просто дважды задают одно и то же свойство.
Когда следует использовать пробел нулевой ширины вместо элемента wbr?
Используйте пробел нулевой ширины (U+200B, записывается как ссылка ), когда вы можете изменить только саму строку — например, контент, хранящийся в CMS или приходящий из API, — и используйте элемент wbr, когда HTML под вашим контролем. Пробел нулевой ширины — это невидимый символ, который остаётся в тексте, поэтому он путешествует вместе с копированием и вставкой и может мешать поиску по точному совпадению.
Что происходит в браузерах, которые не поддерживают text-wrap: pretty?
Объявление просто игнорируется, и текст переносится по стандартному построчному алгоритму, так что визуально ничего не ломается. CSS отбрасывает объявления, значения которых не распознаёт, что делает text-wrap: pretty и text-wrap: balance безопасным прогрессивным улучшением: поддерживающие браузеры получают более качественные переносы строк, а все остальные отображают текст ровно так же, как раньше. Ни feature query, ни резервное объявление не требуются.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k