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

Здесь мы видим группу-кнопок с названиями рубрик сайта. Основная проблема — названия очень сильно могут отличаться по длине. Они варьируются от короткого «перекусы» до длиннющего «Физическая нагрузка: советы и рекомендации».
Дизайнер изящно решил эту проблему, сделав перенос на две строки, и так сбалансировал разбивку на строки и ширину контейнера кнопки, что текст на кнопке отлично заполняет контейнер.
CSS, к сожалению, не даёт инструментов верстальщику, чтобы решить эту задачу, задав единообразно стили для всех кнопок. При этом на практике всё может оказать еще сложнее, т.к. названия могут быть еще длиннее. Такого кейса даже нет в предложенном дизайне.
Использование max-width
Если мы зададим максимальную ширину кнопки, то задача будет решена только для случая с короткими названиями, которые умещаются в одну строку. Ширина кнопки будет подстраиваться под длину строки.
1 |
max-width: 250px; |

Двустрочные контейнеры будут заполнены не сбалансировано (типично: полная 1я строка и неполная вторая), максимальная ширина контейнера определит место переноса строки.
Длинные текст будут обрезаны всегда в одном месте. А очень длинные займут больше строк, чем нужно.
Ограничение в две строки
Начнем с решения простых проблем, и вернемся потом к более сложным.
В CSS есть способ ограничить вывод нужным количеством строк. В нашем случае их нужно две:
1 2 3 4 5 6 7 8 9 |
.two-lines-container { overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; } |
Данный CSS код ограничит текст двумя строками, если текст длиннее — то появится характерное многоточие (ellipsis).

Балансировка сток
Ещё не давно в чистом CSS сделать именно автоматическое равномерное разбиение текста на 2 строки — было не то что не тривиально, а не возможно, потому что движок верстки ломает строки по правилам переноса/пробелов, а не «по середине всей строки текста».
Сейчас у CSS есть экспериментальное свойство text-wrap: balance
, которое является частью CSS Text Level 4. При наличии этого свойства, браузер старается распределить слова равномерно по строкам.
Поддержка пока: Chromium 114+, Safari 16.4+, Firefox в эксперименте.
Т.е мы добавим еще одну строку в стили:
1 |
text-wrap: balance; |
И получим:

У сожалению, свойство оказывает эффект уже после расчета ширины контейнера и потому, хоть длины строк сбалансированы, но мы имеем пустоты в контейнерах.
Идея: квантование ширины
Чтобы обойти ограничение, можно использовать подход «квантования ширины». Вместо того чтобы подгонять ширину кнопки идеально под каждое значение, мы задаём несколько «ступенек» — фиксированные диапазоны ширины.
1 2 3 4 5 6 7 8 |
.dtl { max-width: 320px; } .dtl12-2 { max-width: 150px; } /* до 24 символов */ .dtl12-3 { max-width: 190px; } /* до 36 символов */ .dtl12-4 { max-width: 230px; } /* до 48 символов */ .dtl12-5 { max-width: 270px; } /* до 60 символов */ |
Я создаю набор классов — размерной сетки. Они подключаются в нужные контейнера на сервере, потому js для маркировки нам не нужен.
Здесь в имени классов я зашифровал «data text length 12» — .dtl12-2
читается как «текстовая длина 12 × 2 = 24 символа». То есть если текст укладывается в 24 символа, то кнопка получает этот класс, а её ширина ограничивается 150 пикселями.
Все шаги в совокупности дают вот такой результат:

Пример HTML кода:
1 2 3 |
<span class="two-lines-container dtl dtl12-5"> Бессонница, плохой сон- фактор неэффективного снижения веса </span> |
На практике требуется 5-7 реперных точек сетки, чтобы покрыть нужный диапазон значений. Получается довольно близко к требованиям макета.