Многострочный усечённый текст с кнопкой «Показать больше» - реализация на CSS и рекомендации по доступности
Длинные блоки текста ломают верстку и усложняют доступность, когда требуется усечение и раскрытие контента. Руководство показывает практическую реализацию многострочного усечения с -webkit-line-clamp/line-clamp, использование text-overflow и способы динамического показа кнопки «Показать больше», а также рекомендации по сохранению доступности (ARIA, управление фокусом).
Вы сможете выбрать и внедрить метод усечения с соответствующей кнопкой, сохранив семантику и доступность. По итогу реализуете раскрывающийся блок, подходящий для публикации с учётом экранных читалок.
Подробнее о кнопке «Показать больше»
Для создания кнопки «Показать больше» я не мог использовать тег <button> или <a>. Для этого требуется label и чекбокс. Вот что получилось:
<div class="box">
<input type="checkbox" id="expanded">
<p>Hey, don't cut me off like that. I want to speak my mind and don't appreciate being put into a box.</p>
<!-- Примечание: я не нашел способа поместить дополнительную кнопку/метку в строку внутри абзаца после или перед усечением. -->
<label for="expanded" role="button">read more</label>
</div>Я разместил чекбокс прямо перед абзацем, поэтому можно использовать псевдокласс :checked для активации усечения текста:
input:checked + p {
-webkit-line-clamp: unset;
}Нажмите кнопку, и в абзаце отобразится обрезанная версия текста.
Но как насчет доступности?
Хак с чекбоксом не только семантически неправильный, но и ограничивает возможности доступа к функционалу для людей с плохим зрением. При этом присваивается для чекбокса свойство display: none. Из-за этого нарушается навигация с помощью клавиатуры. Нельзя использовать скрытый чек-бокс, а использование метки не помогает. Событие нажатия клавиши пробела/ввода не перенаправляют событие в чекбокс.
Чтобы решить эту проблему, я сделал чекбокс фокусируемым, хоть и все еще невидимым.
input {
opacity: 0;
position: absolute;
pointer-events: none;
}Теперь чекбокс можно выделить, но без скринридера нельзя узнать, установлен он или нет, так как невидим. Чтобы решить эту проблему, я заставил label наследовать стиль фокуса браузера по умолчанию, когда чекбокс выделен:
input:focus ~ label {
outline: -webkit-focus-ring-color auto 5px;
}Ниже приведена комбинированная демо-версия. Попробуйте нажать на кнопку с помощью мышки, а затем используя клавишу пробела.
Как отображать кнопку динамически?
Но как динамически отображать кнопку, когда текст в абзаце слишком длинный, чтобы поместиться в контейнер. Это единственная функция, которую я не смог реализовать с помощью CSS. Потому что она требует применения селектора :truncated:
.read-more { display: none; }
p:truncated + .read-more { display: block; }Поэтому реализовать это можно только с помощью JavaScript. Например:
const ps = document.querySelectorAll('p');
const observer = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.classList[entry.target.scrollHeight > entry.contentRect.height ? 'add' : 'remove']('truncated');
}
});
ps.forEach(p => {
observer.observe(p);
});А вот расширенная демо-версия:
Комментарии