Display: contents — не CSS-сброс

Перевод статьи Display: Contents Is Not a CSS Reset с сайта adrianroselli.com для css-live.ru, с разрешения автора — Адриана Розелли

CSS-сбросы — подборки CSS-стилей, которые отменяют дефолтные стили браузеров у многих или большинства HTML-элементов.

Недавно я видел, как разработчики применяли display: contents для списков и заголовков ради удаления внешних и внутренних отступов, и внешне получить то, для чего нужны CSS-сбросы. По сути, они используют display: contents в качестве CSS-сброса «на скорую руку».

Это опасно для доступности

Далее в статье я объясню, почему, поскольку, раз уж мне пришлось провести такое исследование, то вам уж точно не помешает хотя бы прочитать его.

Что такое display: contents?

Это поможет понять, о чём речь. Можете пропустить эту часть (она небольшая), если display: contents уже знаком вам.

От разработчиков

В своей простейшей форме display: contents нужен для визуального удаления бокса элемента и замены его на своё содержимое. Если по-простому, display:contents визуально удаляет бокс элемента и подменяет его содержимым. По сути, он ведет себя с элементом так, как если бы открывающий и закрывающий теги элемента были удалены и на странице осталось его голое содержимое.

Это может помочь при применении его к странице из мешанины <div>-ов, вёрстку которой вы хотите сделать на CSS-гридах или флексах. Возможно, вам досталась в наследство страница на Bootstrap с её тучей <div>-ов, и нужно добавить к ней CSS-гриды в порядке прогрессивного улучшения.

У Ире Адеринокун есть хорошая обзорная статья «Как работает display: contents;», хотя браузеры ведут себя не совсем так, как она утверждает.

Хидде де Врис также объясняет это в его статье «Более доступная разметка с display:contents». Однако, его статья обходит вниманием ту сторону display: contents, с которой я столкнулся на деле.

Согласно W3C

Спецификация CSS 3 также поясняет:

Сам элемент не генерирует никаких боксов, но его дочерние элементы и псевдоэлементы по-прежнему генерируют боксы и текстовые фрагменты как обычно. Для целей генерации бокса и раскладки элемент стоит рассматривать, как если бы в дереве документа он был заменён на своё содержимое (включая как его дочерние элементы в исходном документе, так и его псевдоэлементы ::before and ::after, которые генерируются перед дочерними элементами и после них, как обычно).

Заметка: поскольку затрагивается лишь дерево боксов, то ни на какую семантику на основе дерева документа типа соответствия селекторов, обработки событий и наследования свойств это не влияет.

Это значение ведёт себя как display: none на замещаемых элементах и элементах, отображение которых не всегда может контролироваться CSS; см. подробности в приложении B, «Влияние display: contents на необычные элементы».

Рабочий черновик модуля CSS Display 3 уровня W3C, редакция от 20 апреля 2018 г., раздел: 2.5. Генерация бокса: ключевые слова none и contents.

Интереснее может показаться эта коллекция особых случаев.

<br>, <wbr>, <meter>, <progress>, <canvas>, <embed>, <object>, <audio>, <iframe>, <img>, <video>, <frame>, <frameset>, <input>, <textarea>, <select>

display: contents ведёт себя как display: none.

<legend>

По стандарту HTML <legend> с display:contents не является отображаемой легендой (т.е. подобием заголовка для <fieldset>-а, по умолчанию отображаемым в разрыве его рамки — прим. перев.), так что у него нет «волшебного» поведения display (поэтому он реагирует на display:contents как обычно).

<button>, <details>, <fieldset>

В поведении этих элементов нет ничего особенного, display: contents просто удаляет их основной бокс, и их содержимое отображается как обычно.

Любой другой HTML-элемент

Ведут себя как обычно с display: contents.
W3C CSS 3: HTML-элементы

Следствия для доступности

Сегодня браузеры берут элемент с display: contents и удаляют его из дерева доступности. Если вы читали мою заметку о возвращении элементам <table> табличной семантики с помощью ARIA (после изменения свойства display), то здесь это не работает. Ни разу.

Демо-пример

Ниже я встроил CodePen, хотя тестировать debug-версию куда проще, поскольку она избавлена от всех CodePen-овских оберток.

See the Pen Table with display:contents by Максим (@psywalker) on CodePen.

Пример со скринридером

Для демонстрации я прогнал это через NVDA и Firefox. Я пытаюсь перемещаться по таблице (Т), списку (L), кнопке (B) и заголовку 2-го уровня (2). Скринридер не распознал ничего из них. Возможно, стоит отметить, что ни к одному из этих элементов не применена ARIA-роль, соответствующая его «родной» роли.

NVDA и Firefox 59.02

С тех пор как, как я сделал это видео, я дополнил CodePen двумя <button>-ами, один из них с обработчиком события onkeypress и tabindex="0", чтобы показать, что для пользователей клавиатуры его всё равно что нет. Я сделал это, потому что на другой <button> по-прежнему можно кликнуть, и событие onclick на нём сработает.

Дерево доступности

Чтобы вы напрасно не пеняли на скринридеры, могу заверить, что никакая информация из элемента (включая ARIA) не доходит до скринридера. Вот скриншоты экрана из Chrome 66.

Дерево доступности в Chrome, показывающее h2. Дерево доступности в Chrome, показывающее h2 сdisplay: contents

Дерево доступности в Chrome, показывающее <h2> при его обычном отображении и затем <h2> с display: contents. Из него видно, что элемент игнорируется и нода доступности не показывается.

Дерево доступности в Chrome, показывающее таблицу. Дерево доступности в Chrome, показывающее таблицу с display: contents

Дерево доступности в Chrome, показывающее <table> при его обычном отображении и затем <table> с display: contents. Из него видно, что элемент игнорируется и нода доступности не показывается.

Дерево доступности в Chrome, показывающее список. Дерево доступности в Chrome, показывающее список с display: contents.

Дерево доступности в Chrome, показывающее <ul> при его обычном отображении и затем <ul> с display: contents. Из него видно, что элемент игнорируется и нода доступности не показывается.

Дерево доступности в Chrome, показывающее кнопку. Дерево доступности в Chrome, показывающее кнопку с display: contents.

Дерево доступности в Chrome, показывающее <button> при его обычном отображении и затем <button> с display: contents. Из него видно, что элемент игнорируется и нода доступности не показывается.

Баги браузеров и спецификации

За десять дней до моих тестов Хидде де Врис завел баги для браузеров в связи с использованием display: contents и грид-раскладки.

  • Баг Firefox #1455357: установка display:contents для грид-элемента сбрасывает его роль доступности
  • Проблема в Chromium #835455: элемент с display: contents не отображается в дереве доступности.
  • Баг Safari #39630240: (не смог его увидеть, поскольку у моего AppleID, возможно, нет разрешения для этого)

После сегодняшней беседы с Ильёй Стрельцыным в твитере, он взял на себя инициативу завести ишью для этой CSS-спецификации.

  • CSSWG #2632: [css-display][css-aam][selectors-4]. Как элементы с display:contents получают фокус?

Твиты

Вряд ли я первый, кто заметил, что display: contents используется, как CSS-сброс, но мои твиты с утра, кажется, застали некоторых врасплох, а также помогли мне лучше понять проблемы.

Стэфан Джудис @stefanjudis

Ого, это открытие делает display: contents бесполезным для работы со списками и другими вещами. 😲https://twitter.com/aardrian/status/990961243811958787 …#a11y

Амелия Беллами-Ройдз @AmeliasBrain

Это плохое поведение в браузерах, и пока его не исправили, единственное допустимое применение display: contents — удаление дополнительных div-ов, добавленные для фолбечной раскладки, и не нужные для грид-раскладки.

Не используйте — пока — на семантичных элементах: <ul>, <nav>, <button>, <header>, и так далее.

Марко Зэхе @MarcoInEnglish

Интересно, что некоторые другие изменения на сайте после его загрузки каким-то образом заставляют контент снова появиться, по крайней мере в Firefox. @jcsteh исследовал это буквально на позапрошлой неделе, и сейчас в Багзилле рассматривается баг про это. Целая ветка: 👇 https://twitter.com/aardrian/status/990961243811958787

Хидде @hdv

В ответ @MarcoInEnglish @aardrian

Краткая выжимка: на мой взгляд, самая польза от display:contents — повышенная доступность, что сейчас нигде не работает из-за этих багов.

Тодри Хепберн @tabatkins (за этим ником скрывается ведущий редактор CSS-спецификаций Таб Аткинс — прим. перев.)

В ответ @aardrian @SaraSoueidan

Это… Это же убирает элемент и из дерева CSS-боксов. Если так делать, будет масса нехороших побочных эффектов.

Джеймс Стейнбах @jdsteinbach

Мало того, что display: contents в качестве хака для сброса элементов создаёт изрядную проблему #a11y, так ещё и с поддержкой у него плоховато.

Попробуйте вместо этого all: initial. Он сбрасывает стили элемента И поддерживается намного лучше.
https://caniuse.com/#feat=css-all https://caniuse.com/#feat=css-display-contentshttps://twitter.com/aardrian/status/990961243811958787

Амелия Беллами-Ройдз @AmeliasBrain

В ответ @aardrian @stommepoes

Если бы браузеры реализовали его как задумывалось, оно было бы очень полезно, ведь можно было бы делать семантичные обертки в разметке, не зависящие от раскладки.

напр., взять список <nav>, за которым идут панель поиска и выбор языка, и сверстать это всё одним грид- или флекс-контейнером

Рэйчел Эндрю @rachelandrew

В ответ @AmeliasBrain и ещё двум участникам беседы

На самом деле есть много разумных причин, чтобы элементы не создавали боксов визуально.

SelenIT @SelenIT2

В ответ @aardrian

Да, сейчас оно сломано. Спасибо за отличную мысль про получение фокуса элементами с display:contents! Я открыл ишью про это в репозитории CSSWG: https://github.com/w3c/csswg-drafts/issues/2632, заведу баги в браузерах, как только спецификация это прояснит!

В #2355 мы прояснили, что только display:none может влиять на внешний вид и семантику элемента, поэтому элемент с display: contents должен сохранять всю свою интерактивность, включая…

Заключение

Пока что, пожалуйста, используйте display: content только если тестируете вспомогательные технологии и можете подтвердить, что результатом можно пользоваться.

P.S. Это тоже может быть интересно:

1

Комментарии

  1. SelenIT

    SelenIT

    Лично я считаю, что если у элемента есть видимое и кликабельное содержимое, то этот элемент должен быть доступным (как браузеры это реализуют технически, если элемента нет в дереве боксов — другой вопрос, но это их задача). Если вы согласны — приглашаю в упомянутое в статье ишью к спецификации, давайте вместе убедим Таба Аткинса! :). Если не согласны — всё равно приглашаю, давайте попробуем в споре «родить истину».

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Ваш E-mail не будет опубликован

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.