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
нужен для визуального удаления бокса элемента и замены его на своё содержимое. Если по-простому, 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
.По стандарту 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>
при его обычном отображении и затем <h2>
с display: contents
. Из него видно, что элемент игнорируется и нода доступности не показывается.
Дерево доступности в Chrome, показывающее <table>
при его обычном отображении и затем <table>
с display: contents
. Из него видно, что элемент игнорируется и нода доступности не показывается.
Дерево доступности в Chrome, показывающее <ul>
при его обычном отображении и затем <ul>
с 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-сброс, но мои твиты с утра, кажется, застали некоторых врасплох, а также помогли мне лучше понять проблемы.
Ого, это открытие делает
display: contents
бесполезным для работы со списками и другими вещами. ?https://twitter.com/aardrian/status/990961243811958787 …#a11y
Амелия Беллами-Ройдз @AmeliasBrain
Это плохое поведение в браузерах, и пока его не исправили, единственное допустимое применение
display: contents
— удаление дополнительных div-ов, добавленные для фолбечной раскладки, и не нужные для грид-раскладки.Не используйте — пока — на семантичных элементах:
<ul>
,<nav>
,<button>
,<header>
, и так далее.
Интересно, что некоторые другие изменения на сайте после его загрузки каким-то образом заставляют контент снова появиться, по крайней мере в Firefox. @jcsteh исследовал это буквально на позапрошлой неделе, и сейчас в Багзилле рассматривается баг про это. Целая ветка: ? https://twitter.com/aardrian/status/990961243811958787
В ответ @MarcoInEnglish @aardrian
Краткая выжимка: на мой взгляд, самая польза от
display:contents
— повышенная доступность, что сейчас нигде не работает из-за этих багов.
В ответ @aardrian @SaraSoueidan
Это… Это же убирает элемент и из дерева CSS-боксов. Если так делать, будет масса нехороших побочных эффектов.
Мало того, что
display: contents
в качестве хака для сброса элементов создаёт изрядную проблему #a11y, так ещё и с поддержкой у него плоховато.Попробуйте вместо этого
all: initial
. Он сбрасывает стили элемента И поддерживается намного лучше.
https://caniuse.com/#feat=css-all https://caniuse.com/#feat=css-display-contents … https://twitter.com/aardrian/status/990961243811958787
Амелия Беллами-Ройдз @AmeliasBrain
В ответ @aardrian @stommepoes
Если бы браузеры реализовали его как задумывалось, оно было бы очень полезно, ведь можно было бы делать семантичные обертки в разметке, не зависящие от раскладки.
напр., взять список
<nav>
, за которым идут панель поиска и выбор языка, и сверстать это всё одним грид- или флекс-контейнером
В ответ @AmeliasBrain и ещё двум участникам беседы
На самом деле есть много разумных причин, чтобы элементы не создавали боксов визуально.
В ответ @aardrian
Да, сейчас оно сломано. Спасибо за отличную мысль про получение фокуса элементами с
display:contents
! Я открыл ишью про это в репозитории CSSWG: https://github.com/w3c/csswg-drafts/issues/2632, заведу баги в браузерах, как только спецификация это прояснит!В #2355 мы прояснили, что только
display:none
может влиять на внешний вид и семантику элемента, поэтому элемент сdisplay: contents
должен сохранять всю свою интерактивность, включая…
Заключение
Пока что, пожалуйста, используйте display: content
только если тестируете вспомогательные технологии и можете подтвердить, что результатом можно пользоваться.
P.S. Это тоже может быть интересно:
Лично я считаю, что если у элемента есть видимое и кликабельное содержимое, то этот элемент должен быть доступным (как браузеры это реализуют технически, если элемента нет в дереве боксов — другой вопрос, но это их задача). Если вы согласны — приглашаю в упомянутое в статье ишью к спецификации, давайте вместе убедим Таба Аткинса! :). Если не согласны — всё равно приглашаю, давайте попробуем в споре «родить истину».