Многострочный усеченный текст с кнопкой «Показать больше» на чистом CSS

Я написал скрипт на CSS для размещения многострочного усеченного текста с кнопкой «Читать далее».

Я задался вопросом, позволяет ли CSS правильно оформить усеченный текст так, чтобы разместить в нем несколько строк?  А также по нажатию кнопки «Показать больше» отобразить текст полностью.

Свойство text-overflow: ellipsis не поддерживает многострочность. Но я вспомнил о свойстве line-clamp, которое позволяет обрезать многострочный текст.

Подробнее о кнопке «Показать больше»

Для создания кнопки «Показать больше» я не мог использовать тег <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);
});

А вот расширенная демо-версия:

Пожалуйста, оставьте свои комментарии по текущей теме статьи. За комментарии, отклики, подписки, лайки, дизлайки огромное вам спасибо!

Пожалуйста, опубликуйте свои отзывы по текущей теме материала. За комментарии, подписки, лайки, отклики, дизлайки низкий вам поклон!

Ангелина Писанюкавтор-переводчик статьи «Multiline truncated text with “show more” button (with just CSS)»