Обрезка текста в CSS: два подхода и кнопка «Развернуть» без JavaScript

Ограничение объёма текста внутри элемента — частая задача веб‑верстки. Часто это карточки с трех‑ или четырехстрочным анонсом и кнопкой, которая раскрывает полный текст. Это можно решить комбинацией CSS + JavaScript, но также возможно обойтись только CSS. Ниже — подробный разбор двух методов, плюсы/минусы и готовые приёмы для продакшен‑кейсов.
Краткая структура примера
Исходная HTML‑структура простая и семантичная: секция содержит несколько article, в каждом — заголовок, параграф и input, который превратим в кнопку.
Пример HTML (index.html):
Document
Article 1
300‑word text goes here
Article 2
200‑word text goes here
Article 3
100‑word text goes here
В примерах ниже я покажу CSS‑правила для раскладки, обрезки текста и для кнопки.
Техника WebKit (‑webkit‑line‑clamp)
Идея: использовать специфику WebKit — display: -webkit-box и -webkit-line-clamp для ограничения числа видимых строк.
Основной стиль страницы (style.css — начало):
*, *::before, *::after {
box-sizing: border-box;
}
body {
background: #f3f3f3;
overflow: hidden;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
}
.card-group {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: .5rem;
align-items: flex-start;
padding: 1rem;
}
.card {
background: white;
padding: 1rem;
border: 1px solid black;
border-radius: .25em;
}
h2, p {
margin: 0;
}
h2 {
margin-bottom: 1rem;
}А теперь — сам WebKit‑приём для ограничение числа строк:
.cuttoff-text {
--max-lines: 3;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: var(--max-lines);
}Плюсы:
- Очень короткое решение.
- Работает без вычисления высоты.
Минусы:
- Требует display: -webkit-box — это сильно ограничивает варианты layout (не подходит, если элемент должен быть flex или grid контейнером).
- Поведение зависит от реализации движка; можно получить разницу в переносах слов.
- Поддержка у всех браузеров исторически разная (работает в Chromium/WebKit‑браузерах).
Более гибкий подход: вычисляемая высота + градиент
Идея: установить фиксированную высоту, основанную на количестве строк и line‑height, а затем добавить плавный градиент‑фейд внизу для естественного перехода.
Преимущество этого метода в том, что блок может оставаться любым display (flex, grid и т.д.), и вы полностью контролируете высоту.
Пример CSS:
.cuttoff-text {
--max-lines: 5;
--line-height: 1.4;
/* высота = число строк * размер шрифта * line-height */
height: calc(var(--max-lines) * 1em * var(--line-height));
line-height: var(--line-height);
overflow: hidden;
position: relative; /* нужно для псевдоэлемента */
}
.cuttoff-text::before {
content: "";
position: absolute;
height: calc(2em * var(--line-height));
width: 100%;
bottom: 0;
pointer-events: none;
background: linear-gradient(to bottom, rgba(255,255,255,0), white);
}Плюсы:
- Поддерживает любые display‑режимы.
- Можно тонко настроить визуальный эффект (длина фейда, цвет плашки и т.д.).
Минусы:
- Нужно точно учитывать размер шрифта и line‑height (при динамическом масштабе/настройках пользователя возможны неточности).
- Псевдоэлемент закрывает нижнюю область — нужно думать про доступность и выделение текста.
Добавление динамической кнопки «Развернуть/Свернуть» без JavaScript
Мы используем input type=”checkbox” как переключатель и селектор :has() для изменения высоты контейнера. Обратите внимание на вопросы совместимости: :has() поддерживается в современных движках, но если вам нужен полифилл для старых браузеров, см. раздел «Альтернативы».
CSS для оформления кнопки‑переключателя:
.expand-btn {
appearance: none;
border: 1px solid black;
padding: .5em;
border-radius: .25em;
cursor: pointer;
margin-top: 1rem;
display: inline-block;
}
.expand-btn:hover {
background-color: #ccc;
}
/* Текст кнопки через content */
.expand-btn::before {
content: "Развернуть";
}
.expand-btn:checked::before {
content: "Свернуть";
}Чтобы раскрывать текст, используем селектор :has в связке с соседством элементов. В примере элемент input идёт после параграфа, поэтому можно воспользоваться селектором у параграфа:
/* Когда рядом с cuttoff-text расположен checked .expand-btn — делаем высоту auto */
.cuttoff-text:has(+ .expand-btn:checked) {
height: auto;
}Замечания по доступности:
- input должен иметь aria‑label или быть связан с
- Предпочтительнее использовать реальную кнопочную семантику:
Альтернативы и гибридные варианты
- JavaScript‑fallback: используйте JS для переключения класса (например, .is‑expanded) у контейнера и управляйте высотой через max‑height с анимацией. Этот вариант универсален по совместимости и даёт плавные переходы.
- CSS с использованием details/summary: нативный раскрывающийся блок, доступный и простой, но менее гибкий по дизайну.
- Комбинация CSS‑only + JS: базовый функционал на CSS, а при наличии JS — улучшения (плавность, переходы, анимации).
Когда CSS‑only может не подойти:
- Нужно плавное открытие/закрытие с высотой неизвестной высоты (в этом случае удобнее управлять max‑height через JS).
- Поддержка старых браузеров, где :has или -webkit-line-clamp отсутствуют.
Практические советы и частые ошибки
- Всегда учитывайте line‑height при расчёте фиксированной высоты; по умолчанию 1em ≈ размер шрифта.
- Если пользователь увеличивает масштаб страницы, фиксированные расчёты на rem/em ведут себя лучше, чем px.
- Псевдоэлемент для градиента не должен блокировать pointer‑events — устанавливайте pointer-events: none.
- Если делаете «копируемый текст», тестируйте выделение: градиент может визуально скрыть конец, но выделение всё равно работает.
- Для SEO и доступности не прячьте смысловой контент от поисковых роботов/скринридеров: используйте aria‑labels и отвечайте ожиданиям пользователя.
Совместимость и ограничения
- -webkit-line-clamp — прост в применении, но требует display: -webkit-box.
- :has — мощный селектор (очень удобен для CSS‑only паттернов), но его поддержка может отличаться в старых браузерах. Для критичных проектов делайте фолбэки.
- Для мобильных браузеров учитывайте, как изменяется перенос строк при разных ширинах.
Таблица совместимости (ориентир):
| Приём | Поддержка | Ограничения |
|---|---|---|
| -webkit-line-clamp | Поддерживается в WebKit/Chromium‑движках | Требует display: -webkit-box |
| Явная высота + градиент | Универсальна | Нужно рассчитывать line‑height |
| :has() | Современные браузеры | Может отсутствовать в устаревших версиях |
Критерии приёмки
- Текст в карточке по умолчанию отображается не больше заданного числа строк на всех важных целевых разрешениях.
- Кнопка «Развернуть/Свернуть» читаемо сменяет своё состояние (визуально и для экранных читалок через aria‑атрибуты).
- При раскрывании весь текст доступен и копируется; макет не ломается.
- Проверены фолбэки для браузеров без :has (либо JS‑fallback, либо graceful degradation).
Чек‑лист по ролям
Для разработчика:
- Реализовать расчёт высоты на основе line‑height и числа строк.
- Добавить aria‑label/role для input/button.
- Тестировать в ключевых браузерах (Desktop/Mobile).
Для дизайнера:
- Уточнить поведение градиента (цвет/высота фейда).
- Согласовать состояние кнопки в активном/hover состоянии.
Для QA:
- Проверить текст при увеличенном шрифте (браузерные настройки).
- Проверить копирование и выделение текста в усечённом виде.
- Проверить фолбэки на старых браузерах.
Мини‑методология внедрения (шаги)
- Выбрать цель: нуждаетесь ли вы в точном числе видимых строк или только в визуальном ограничении?
- Выбрать метод: WebKit для простых случаев или вычисляемая высота для гибкости.
- Реализовать вариант, добавить accessibility атрибуты и тесты.
- Добавить фолбэк: либо JS (class toggle), либо graceful degradation.
- Провести cross‑browser тестирование и принять по критериям.
Decision tree
flowchart TD
A[Нужно ограничить строки в блоке?] --> B{Нужна ли максимальная гибкость layout?}
B -- Да --> C[Использовать вычисляемую высоту + градиент]
B -- Нет --> D[Можно использовать -webkit-line-clamp]
C --> E{Требуется кнопка «Развернуть» без JS?}
D --> E
E -- Да --> F{Поддерживает ли целевой браузер :has?}
E -- Нет --> G[Использовать JS‑fallback для раскрытия]
F -- Да --> H[Использовать :has + input checkbox]
F -- Нет --> GШаблон — небольшой cheat‑sheet (быстрое решение)
/* 1) Базовая карточка */
.card { background:#fff; padding:1rem; border-radius:6px; }
/* 2) Обрезка через line‑clamp */
.cuttoff { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; }
/* 3) Обрезка через высоту */
.cuttoff2 { --lh:1.4; --lines:4; height:calc(var(--lines) * 1em * var(--lh)); overflow:hidden; position:relative; }
.cuttoff2::before{ content:''; position:absolute; bottom:0; height:2.2em; width:100%; background:linear-gradient(to bottom, rgba(255,255,255,0), #fff); pointer-events:none; }Советы по локализации и UI‑текстам
- Переводите надписи кнопок: «Развернуть» / «Свернуть» — в большинстве локалей это ожидаемая семантика.
- Если контент многоязычный, используйте aria‑labels с соответствующим языковым тегом.
Короткое резюме
- Для простых случаев используйте -webkit‑line‑clamp, если он покрывает ваши целевые браузеры. Это быстрый путь.
- Если нужен гибкий layout или вы хотите контролировать fade‑эффект, рассчитывайте высоту через line‑height и используйте псевдоэлемент для градиента.
- Для кнопки раскрытия без JS используйте input + :has(), но обеспечьте фолбэк для старых браузеров.
Важно: всегда тестируйте поведение при масштабировании шрифтов и на мобильных устройствах.
Ключевые проверки:
- Поведение в наиболее популярных браузерах, мобильная адаптация, корректность aria‑атрибутов.
Спасибо — если нужно, могу подготовить готовый репозиторий с рабочим примером, полифиллами и автотестами для нескольких браузеров.
Похожие материалы
Как устроить идеальную вечеринку для просмотра ТВ
Как распаковать несколько RAR‑файлов сразу
Приватный просмотр в Linux: как и зачем
Windows 11 не видит iPod — способы исправить
PS5: как настроить игровые пресеты