Подписывайтесь на мой твиттер, там всегда что-нибудь интересное!

Более доступная разметка с display: contents

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

Это перевод статьи More accessible markup with display: contents

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

<div class="container">
  <h1 class="item">Penne with tomato sauce</h1>
  <p class="item">This simple recipe has few ingredients but tastes delicious.</p>
  <div class="item ingredients">
    <h2>You'll need</h2>
    <ul>
      <li>canned tomatoes</li>
      <li>onions</li>
      <li>garlic</li>
    </ul>
  </div>
</div>

И у нас может быть вот такой CSS:

.container { 
  display: grid; /* element is now a grid container */
  grid-template-columns: repeat( 4, 1fr );  /* grid now has 4 columns */

.item:nth-child(1) {
  grid-columns: 1 / 2; /* Place item between grid line 1 and 2 */
}

.item:nth-child(2) {
  grid-columns: 2 / 4; /* Place item between grid line 2 and 4 */
}

.item:nth-child(3) {
  grid-columns: 4 / 5; /* Place item between line 4 and 5 */
}

Я использовал .container и .item как имена классов, так как в основе гридов лежат контейнеры и грид элементы (т.е. items). Но вы само собой можете использовать имена, которые вам удобнее.

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

Мы могли бы добавить список в нашу разметку:

<div class="container">
  <h1 class="item">Penne with tomato sauce</h1>
  <p class="item">This simple recipe has few ingredients but tastes delicious.</p>
  <div class="item ingredients">
    <h2>You'll need</h2>
    <ul>
      <li>canned tomatoes</li>
      <li>onions</li>
      <li>garlic</li>
    </ul>
  </div>
  <ul class="item sponsors">
    <li>Supermarket 1</li>
    <li>Supermarket 2</li>
  </ul>
</div>

Но мы не сможем спозиционировать каждого спонсора в грид. Так происходит, потому что только <ul> является прямым потомком элемента контейнера и следовательно является грид элементом. А <li> уже нет, так как они не являются прямыми потомками того же контейнера, следовательно они уже не будут участвовать в грид-игре. Но что если мы реально захотим прировнять спонсоров к гридам?

Поверхностная разметка

Одним очевидным методом вовлечения в гриды спонсоров является удаление <ul> и использование <div> для каждого спонсора. Но что мы потом будем делать? Это просто сделает нашу разметку поверхностной и не точной.

Выгода от использования <ul> элемента:

Список останется вне контекста нашей страницы, для примера в Safari Reader Mode, он покажется как отдельный список.
Во время печати с отключенными стилями, он покажется опять таки как список. Для людей, которые используют скринридеры, это будет списком, соответственно устройство объявит, что это список из трех элементов.
Если мы сделаем нашу разметку поверхностной, то мы просто потеряем эти полезные свойства.

display: contents идёт на помощь

С помощью display: contents, мы может сохранить нашу разметку и наши гриды. Это свойство делает элемент таким, будто кажется, что его больше не существует. Оно не генерит бокс, так что фоны, границы и другие боксозависимые элементы не будут на нем работать. Грид свойства тоже не будут. Но все это будет работать для потомком элементов. Спицификация говорит, что элемент ведет себя так, будто бы он был перемещен своим же контентом. Если это звучит странно, то я рекомендую прочитать эту статью.

По факту, для наших целей в этом случае использование display: contents на элементе делает следующее: элемент перестает участвовать в гриде, а его контент с другой стороны, начинает. Это позволяет нам указывать спонсоров в гриде, вместо списка в который они помещены изначально.

Это одни из самых интересных последних кейсов указанных в спецификациях, если свойство использовалось на элементах, таких как img или video.

Проблемы доступности в выполнении display: contents современными барузерами

Для людей, которые используют вспомогательные технологии(AT), браузеры применяют свойства доступности, включая role элементы на странице. Это такая штука, которая дает знать AT, что есть что на странице. Множество элементов идут с встроенным role из коробки, для примера у list уже есть role.

И вот где все идет не так с современными браузерами, которые поддерживают display: contents. Они не интерпретируют display: contents только как элемент разметки, они из него суть элемента на странице. Это проблематично и является багом, в соответствии с комментарием на спецификацию по влиянию на отображению шаблона:

Свойство display не имеет эффекта на семантику элемента: это определяется разметкой документа и не подвергается влиянию CSS. Помимо значения none, которое так же влияет на слуховой/голосовй вывод и интерактивность элемента, и его потомков, свойстов display само влияет только на визуальный шаблон: его цель — дать дизайнерам свободу в изменении шаблона поведения элемента без влияния на семантику документа, лежащую в основе.

Смотря на пример списка спонсоров в нашем примере, это означает то, что элемент больше не является списком, но теперь является чем-то другим.

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

Firefox 61

Список получает role как text leaf. Update: теперь это исправлено и будет реализовано в Firefox 62 в августе 2018.

Chrome 66

Список показывется как ‘accessibility node not exposed, element not rendered’

Safari

Список показывается как ‘no accessibility information’

<ul> это просто пример. Такое случается с любым элементом на котором применяется display: contents и у которого была role до этого.

Заключение

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

Я убежден, что роли AT, не должны исчезать при применении display: contents, так как это нивелирует основное назначение display: contents. Я показал вам баги с Firefox, Chromium и Safari по этой теме. Реально надеюсь, что мы сможем использовать display: contents, сохраняя нетронутой доступность ролей в AT для элементов, таким образом мы будем иметь отличные шаблоны и потрясающий AT.