Семантичный CSS с умными селекторами

«Задача всегда диктует внешний вид. Это закон.» Эти слова принадлежат архитектору и «отцу небоскребов»Луису Салливану (Louis Sullivan). Это золотое правило для архитекторов, которые не хотят чтобы сотни людей были раздавлены тяжестью колоссального строения. В дизайне на первом месте всегда должно быть выполнение задачи, которое влечет за собой определённый внешний вид. Если бы на первом месте для архитектора был внешний вид небоскрёба, красивым его сделать было бы намного проще, однако ценой этому стала бы его безопасность.

Это что касается архитекторов. А как насчёт фронтэнд архитекторов, хоть нас часто и не считают архитекторами всерьез? Должны ли мы следовать этому правилу или им можно пренебречь?

С появлением объектно-ориентированного CSS (OOCSS) стало очень модно «разделять семантику представления и семантику разметки». Используя названия классов, которые не имеют какого-либо определённого значения, можно разделить управление документом и его внешним видом.

art_overthink1_mini-ru

В этой статье мы изучим альтернативный подход к написанию CSS, который предусматривает сочетания семантики документа и визуального дизайна при любой возможности. Мы рассмотрим как, используя «умные» селекторы, можно задействовать функциональную суть семантического HTML, чтобы получить правильную разметку. Если у вас все в порядке с кодом, то и дизайн у вас получится такой, как нужно.

Если вы как и я не можете сосредоточиться более чем на одном занятии или мысли за раз, надеюсь применение изложенных в этой статье идей поможет упростить ваш рабочий процесс и поможет с большей легкостью переключаться с проекта на проект. К тому же, в заключительной части мы рассмотрим практическую стратегию: мы создадим CSS-букмарклет, содержащий умные селекторы по атрибуту для тестирования HTML и обнаружения ошибок с помощью псевдо-содержимого.

Умные селекторы

С изобретением таблиц стилей у нас появилась возможность физически разделять разметку документа (HTML) и код, отвечающий за его представление (CSS). Это помогает писать более грамотный и соответствующий стандартам код ровно настолько, насколько изобретение пульта дистанционного управления влияет на качество телевидения. Зато таблицы стилей сделали веб-разработку более удобной. Благодаря возможности стилизировать несколько элементов с помощью одного селектора (например, p для абзацев), необходимость сохранения целостности представления веб-сайта и его поддержки стала существенно менее пугающей.

art_remote_mini-ru

Селектор p является примером самого простого умного селектора. Селектор p является умным потому, что обладает встроенным пониманием своего места в семантической классификации. Он без вмешательства автора знает как обозначить абзац и как его стилизировать, это простой, но эффективный селектор, особенно если вспомнить об автоматически генерируемых абзацах в редакторах WYSIWYG.

Итак, если это умный селектор, какой селектор тогда неумный? Любой селектор, при использовании которого разработчику необходимо вмешаться и внести в документ изменения чтобы добиться стилистического выделения, можно считать неумным. Класс является классическим примером неумного селектора, потому что он не входит в перечень селекторов которые принято считать семантическими. Классам можно присваивать осмысленные названия и рационально располагать их в документе, но они не настолько умны чтобы позаботиться о своем представлении, так же как и браузеры недостаточно умны чтобы позаботиться о представлении классов вместо вас.

Неумные селекторы увеличивают количество времени на разработку веб-сайта, потому, что для каждого отдельного элемента их нужно указывать в индивидуальном порядке. Если бы у нас не было тегов p, для создания абзацев нам пришлось бы использовать неумные селекторы, возможно с классом .paragraph для каждого из них. Одним из недостатков такого положения дел является то, что CSS утрачивает гибкость, то есть его нельзя применить в HTML не прописав предварительно классы в соответствующих местах.

art_paragraph_500_mini

Иногда неумные селекторы кажутся нам необходимыми, или по крайней мере более простыми в применении, и только некоторые отваживаются положиться исключительно на умные селекторы. Однако, иногда применение неумных селекторов доходит до абсурда и может привести к дисбалансу между структурой и представлением документа. Я расскажу о том, как часто применение неумного селектора .button доходит до абсурда.

Да здравствует разнообразие

Умные селекторы не ограничиваются базовыми элементами, предложенными в спецификации HTML. Можно построить сложный умный селектор проведя различие между базовыми элементами с помощью комбинацииконтекстного и функционального определения . Некоторые элементы, например <a>, обладают множеством функциональных различий, которые можно учитывать и использовать. Другие элементы, например <p>, редко отличаются по функциях, однако играют немного разные роли в зависимости от контекста.

header p {
   /* стиль для вступительных абзацев */
}

footer p {
   /* стиль для заключительных абзацев */
}

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

art_heirarchy_500_mini-ru

Разумеется, некоторые приверженцы OOCSS относятся к дочерним селекторам с подозрительностью и рьяно настаивают на разметке похожей на пример ниже, которая соответствует документации БЭМ «Определение».

<ul class="menu">
  <li class="menu__item">…</li>
  <li class="menu__item">…</li>
</ul>

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

Атрибуты гиперссылок

Даже те кто отстаивает необходимость концептуального разделения CSS и HTML, с готовностью признают что некоторые атрибуты (даже большинство из них, кроме классов и пользовательских атрибутов данных) влияют на внутреннее функционирование документа. Без href ваша ссылка не будет никуда вести. Без typeбраузер не будет знать какой тип input нужно отобразить. Без title ваш abbr с текстом «ООП» может обозначать и Общество Охраны Памятников, и Организацию Освобождения Палестины.

Некоторые из этих атрибутов нужны, чтобы улучшить семантику документа, другие отвечают за корректное отображение и работу элемента. Если они отсутствуют, их нужно добавить; а если они включены в элемент, почему бы этим не воспользоваться? Нельзя прописать CSS без HTML.

Атрибут rel

Атрибут rel появился в качестве стандарта для указания зависимости ссылок, способа описания определённого предназначения ссылки. Дело в том, что не у всех ссылок одинаковые функции. Благодаря WordPress rel="prev" и rel="next" являются наиболее часто употребляемыми значениями, которые помогают описать взаимосвязь отдельных страниц блога. С семантической точки зрения, тег a с атрибутомrel остается тегом a, но у нас есть возможность его конкретизировать. В отличии от классов, эта особенность имеет семантическую ценность.

Атрибут rel нужно использовать всегда, когда он уместен, ведь он одобрен в функциональной спецификации HTML и следовательно может быть использован различными браузерами для улучшения опыта использования пользователем и повышения точности поисковых систем. Как можно стилизировать такие ссылки? Конечно же, с помощью простых селекторов атрибута:

[rel="prev"] {
  /* стиль для ссылок «предыдущая страница» */
}

[rel="next"] {
  /* стиль для ссылок «следующая страница» */
}

Такие селекторы атрибутов поддерживаются всеми браузерами кроме самых древних, так что нет причины не использовать их во всех случаях, когда в наличии есть атрибуты. Когда речь идет о специфичности, они обладают тем же весом, что и классы. Так что здесь тоже проблемы нет. Однако, я помню о рекомендации разграничивать семантику разметки и представления. Я не хочу отказываться от атрибутов rel (хотя бы потому что Google их использует), так что я лучше добавлю ничего не значащий атрибут и использую его для стилизации элемента.

<a href="/previous-article-snippet/" rel="prev" class="prev">предыдущая страница</a>

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

Ладно, это все абстрактные размышления; как насчёт влияния на обслуживание сайта? Допустим, что мы используем class для присвоения стиля, давайте посмотрим, что произойдет, когда в связи с правками или перепроектированием сайта некоторые атрибуты будут удалены. Представим что мы добавили перед текстом ссылки [rel="prev"] стрелку с помощью псевдо-элемента:

.prev:before {
  content: '\2190'; /* код для стрелки ("←") */
}

art_previous_500_mini-ru

Удаление класса повлечёт за собой удаление псевдо-контента, что (само собой) в свою очередь повлечёт удаление стрелки. Без стрелки ничто не говорит о связи ссылки с предыдущей статьей. После тех же действий стрелка, привязанная к атрибуту rel останется нетронутой: класс продолжит управлять представлением, неизменно скрывая отсутствие объявленной взаимосвязи в разметке. Лишь привязывая стиль напрямую к семантическому атрибуту, который отвечает за соответствующую функцию элемента, можно сохранить честность и точность кода. Только если что-либо имеет какую-либо функцию в документе, оно имеет право в нем присутствовать.

Подстроки атрибута

Представляю себе что вы думаете: «Все это конечно мило, но в скольких случаях на самом деле я смогу привязывать стиль для ссылок таким образом? Рано или поздно мне придётся использовать класс.» Не согласен. Взгляните на этот неполный список ссылок с различными функциями, в основе всех из них лежит элемент a:

  • ссылки на внешние источники,
  • ссылки на защищенные страницы,
  • ссылки на авторские страницы,
  • ссылки на справочные страницы,
  • ссылки на предыдущую страницу (пример приведен выше),
  • ссылки на следующую страницу (опять таки, пример приведен выше),
  • ссылки на страницы в формате PDF,
  • ссылки на текстовые документы,
  • ссылки на ZIP-архивы,
  • ссылки на исполнимые файлы,
  • ссылки на внутренние части страницы,
  • ссылки которые на самом деле являются кнопками (об этом мы подробнее поговорим позже),
  • ссылки которые на самом деле являются кнопками-переключателями,
  • ссылки, которые запускают почтовый клиент,
  • ссылки для телефонных номеров для смартфонов,
  • ссылки на исходное представление страницы,
  • ссылки, которые открывают новые вкладки и окна,
  • ссылки на файлы JavaScript и JSON,
  • ссылки на каналы RSS и файлы XML.

Такое разнообразие функций впечатляет, причем все они распознаются различными браузерами. Теперь задумайтесь, ведь чтобы все эти ссылки выполняли разные функции, у них должны быть различия в определении. То есть, чтобы работать по-разному, они должны быть прописаны по-разному; а если они прописаны по-разному, значит и стилизировать их можно по-разному.

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

art_auticons_mini

В большинстве случаев Auticons обращается к значению href чтобы определить функцию ссылки. Можно стилизировать элементы в зависимости от того с чего начинаются или заканчиваются значения их атрибутов, а также в зависимости от того какую подстроку содержит их значение. Ниже приведены некоторые распространённые примеры.

Защищённый протокол

Каждый правильно прописанный (т.е. абсолютный) URL начинается с последовательности URI, после которой идёт двоеточие. Самым популярным в сети является http:, но mailto: (для SMTP) и tel: (служит для указания телефонных номеров) также распространены. Если мы знаем как должно начинаться значениеhref у ссылки, можно использовать это семантическое условие для привязки стиля. В следующем примере для защищённых страниц мы используем компаратор ^=, который значит «начинается с».

a[href^="https:"] {
   /* стилевые свойства для ссылок на защищённые страницы */
}

art_secure_500_mini-ru

В Auticons ссылки на защищённые страницы декорируются иконкой навесного замка согласно определённому семантическому шаблону, с идентификацией по атрибуту href. Это даёт следующие преимущества:

  • Ссылки на защищённые страницы (и только на них) визуально выглядят как ссылки на защищённые страницы с помощью иконки висячего замка.
  • Ссылки на страницы, которые перестают быть защищёнными, утрачивают протокол https и вместе с ним соответственное визуальное оформление.
  • Для ссылок на страницы, которые становятся защищёнными, добавляется иконка висячего замка и они автоматически приобретают вид ссылок на защищённые страницы.

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

[Ссылка на защищённую страницу](https://payment.example.com/)

Учтите, что использование префикса [href^="https:"] не является безотказным, поскольку не все страницы использующие протокол HTTPS действительно являются защищёнными. Тем не менее, он является настолько же подверженным ошибкам, насколько и браузеры. Большинство браузеров отображают иконку навесного замка в адресной строке при открытии страницы HTTPS.

paypal_500_mini

Типы файлов

Как было упомянуто ранее, можно стилизировать ссылки согласно окончанию значения href. На практике это значит, что можно использовать CSS чтобы определить на файл какого типа ведет ссылка. Auticons поддерживает .txt.pdf.doc.exe и много других. Вот пример для .zip, в котором окончание hrefопределено с помощью $=:

[href$=".zip"]:before, 
[href$=".gz"]:before {
   content: '\E004'; /* юникод для иконки архива */
}

Сочетание

Вы ведь знаете, что разработку можно сделать полностью объектно-ориентированной и для задания стилей элементов использовать набор из нескольких классов? То же можно сделать и с селекторами атрибутов. Давайте сравним:

/* CSS для подхода с использованием классов */

.new-window-icon:after {
   content: '[new window icon]';
}

.twitter-icon:before {
  content: '[twitter icon]';
}

/* CSS для подхода с использованием селекторов атрибутов */

[target="_blank"]:after {
   content: '[new window icon]';
}

[href*="twitter.com/"]:before {
  content: '[twitter icon]';
}

(Обратите внимание на компаратор *=, который значит «содержит». Стиль будет применён если строка значения содержит подстроку twitter.com/)

<!-- HTML для подхода с использованием классов -->

<a href="http://twitter.com/heydonworks" target="_blank" class="new-window-icon twitter-icon">@heydonworks</a>

<!-- HTML для подхода с использованием селекторов атрибутов -->

<a href="http://twitter.com/heydonworks" target="_blank">@heydonworks</a>

art_twitter_500_mini

Любому редактору, которому нужно добавить ссылку на страницу в Twitter, нужно знать только две вещи: URL-адрес (он наверняка уже знает с каким аккаунтом имеет дело) и как сделать, чтобы ссылка открывалась в новой вкладке (это можно быстро узнать если поискать в Google).

Наследование

Маленькая нерешенная проблемка: что если наша ссылка не содержит ни один из специальных атрибутов? Что если ссылка — это простая старая ссылка без дополнительной информации? Такой селектор легко запомнить и фанатики производительности в восторге от того, что проще него не придумаешь.

art_a_mini

Поязвили и хватит, могу вас уверить, что наследование в каскаде работает для селекторов атрибута точно так же, как для классов. Для начала стилизируйте базовый a — возможно в целях доступности стоит применить правило text-decoration: underline; затем постепенно улучшайте внешний вид, ссылки используя доступные селекторы атрибута. Браузеры вроде Internet Explorer (IE) 7 не поддерживают псевдо-контент вообще. Благодаря наследованию ссылки хотя бы будут выглядеть как ссылки.

a {
  color: blue;
  text-decoration: underline;
}

a[rel="external"]:after {
   content: '[icon for external links]';
}

Кнопки должны быть кнопками

В следующей части мы подробно рассмотрим создание букмарклета CSS для выявления ошибок в коде. Перед тем как к этому приступить, разберёмся как вообще абсурдные селекторы могут попасть на наш сайт.

Приверженцам OOCSS нравятся классы, потому что их можно использовать несколько раз. Следовательно,.button более предпочтителен чем #button. Однако я могу предложить селектор, который еще лучше подойдёт для стилизации кнопки. Его название очень просто запомнить.

art_button_mini

Элемент <button> служит для представления кнопки.

– W3C Wiki

Topcoat — это построенный на основе BEM UI-фреймворк от Adobe, который следует принципам OOCSS. В Topcoat CSS с различными стилевыми правилами для кнопок занимает больше 450 строчек, если учитывать блоки комментариев. В каждом из этих блоков рекомендуется применять стиль для вашей кнопки в духе этого примера:

<a class="topcoat-button">Кнопка</a>\

Этот пример не является кнопкой. Никак нет. Если бы это была кнопка, она была бы прописана в разметке с помощью <button>. По сути, если бы она была прописана как кнопка, можно было бы ожидать что она будет по умолчанию выглядеть как кнопка в любом браузере известном человечеству даже если для нее не были бы применены никакие стили CSS. Но это не кнопка; она прописана с использованием <a>, что делает её ссылкой, более того, ссылкой без href, что значит, что это даже не ссылка. Технически, это всего лишь указатель места вставки ссылки, которую вы ещё не прописали.

shark_dog_mini

Костюм акулы не делает собаку акулой. (Фото: reader of the pack)

Примеры из CSS в Topcoat являются только примерами, но замысел, что предназначение элемента определяет не HTML, а класс, дезориентирует. Никакое количество изменений названия класса с применением «продуманной расстановки дефисов» не может оправдать предложение превратить неумный селектор в абсурдный и просто-напросто неправильно написать код.

Обновление: Уже после того как была написана эта статья в Topcoat.io эти примеры были заменены примерами с <button>. Это прекрасно! Однако, мне все ещё не нравится, что в примерах пропагандируется использование класса .is-disabled вместо более правильного атрибута disabled. Чтобы ознакомиться с мнением обеих сторон, взгляните на мою дискуссию с представителем Topcoat в комментариях. Больше примеров неудачной подстройки веб-стандартов под OOCSS можно найти на semantic-ui.com. «Стандартная кнопка» в их примерах является элементом <div> с пустым <i>.

Ничего не вижу, ничего не слышу

«Если что-либо выглядит как утка, плавает как утка и крякает как утка, скорее всего это и есть утка.»

Многие считают что если ссылка напоминает кнопку и запускает события JavaScript, характерные для кнопок, то это и есть кнопка. Однако это всего лишь значит, что она соответствует первым двум критериям «теста на утку». Пользователи, которые способны применить индуктивное мышление и уловить суть понятия «кнопка», согласятся, что она должна и крякать соответственно. Поскольку в наличии всего лишь ссылка, скринридеры распознают её как ссылку, следовательно ваш аллегорический небоскрёб не приспособлен для инвалидных колясок. Стремление избежать путаницы такого рода для пользователей вспомогательных технологий является не гонкой за семантической безупречностью, а нашей прямой обязанностью на общее благо.

Тем не менее, некоторые продолжат настаивать на использовании a в качестве основы для кнопок. В конце концов, ссылки (немного) проще стилизировать без последствий для контекста. Если ваш выбор — элементa, есть только один способ максимально приблизить его к настоящей кнопке с точки зрения доступности. Вы наверное уже догадались: нужно использовать ещё один осмысленный атрибут роли WAI ARIA. Чтобы убедиться, что ссылка не просто так выглядит как кнопка, примените следующий селектор атрибута.

[role="button"] {
   /* семантический CSS для видоизменённых элементом, которые объявлены «кнопкой» для вспомогательных технологий */
}

Контроль качества селекторов атрибутов

«CSS даёт столько власти атрибуту класса, что разработчики могут поддаться искушению создать свой собственный «язык разметки» состоящий из элементов, которые не имеют почти ничего общего с представлением (таких как DIV и SPAN в HTML) и предусматривающий присвоение стилей с помощью атрибута «class». Разработчикам следует избегать этого, так как у всех структурных элементов языка разметки часто есть общепринятые значения.»

– «Селекторы», CSS Level 2, W3C

У нас есть два элемента, a и button, чтобы семантически разграничить два абсолютно разных типа функциональных взаимодействий. В то время как гиперссылка обозначает возможность перехода куда-либо, кнопка выступает инициатором события или действия. Суть первого в переходе, второго — в трансформации. Первое способствует разделению, второе — взаимодействию.

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

Вдохновение было навеяно частично постом Эрика Мейера (Eric Meyer), некоторые идеи были взяты из DiagnostiCSS. В этой таблице стилей объединены селекторы атрибута и селектор :not (или же псевдо-класс отрицания), чтобы определить проблемы в HTML. В отличии от других реализаций, наше уведомление об ошибке будет выводиться на экран с помощью псевдо-контента. Каждая ошибка будет написана шрифтомComic Sans на розовом фоне.

Когда мы соединяем вместе функцию и форму, в результате видим, что уродливая разметка ведёт к уродливому CSS. Считайте это местью разработчику за надругательство над разметкой. Чтобы попробовать, как все работает, перетащите revenge.css в закладки браузера и щёлкните по закладке, чтобы запустить его на любой странице на ваш выбор. Примечание: на данный момент букмарклет не работает для страниц с протоколом https.

REVENGE.CSS

Перетащите на панель закладок

Правило 1

«Если элемент является ссылкой, у него должен быть атрибут href»

a:not([href]):after {
   content: 'Это должна быть ссылка или кнопка? Она никуда не ведёт!';
   display: block !important;
   background: pink !important;
   padding: 0.5em !important;
   font-family: 'comic sans ms', cursive !important;
   color: #000 !important;
   font-size: 16px !important;
}

Примечание: В этом примере мы проверяем не значение атрибута, а само существование атрибута, т.е. соответствует ли условию [href] элемент, у которого есть атрибут href. Эта проверка должна осуществляться только для гиперссылок, для этого используется префикс a. Правило можно прочитать так: «Для каждого элемента a, который не содержит атрибут [href], должен быть добавлен псевдо-контент с сообщением об ошибке.»

Правило 2

«Если элемент является ссылкой и содержит атрибут href, у него должно быть валидное значение»

a[href=""]:after, a[href$="#"]:after, a[href^="javascript"]:after {
   content: 'Эта ссылка должна быть кнопкой? Она никуда не ведёт!';
   /*… уродливый стиль …*/
}

Примечание: Если href пуст, заканчивается символом # или в нем используется JavaScript, скорее всего мы имеем дело с кнопкой, для которой не используется правильный элемент button. Обратите внимание, что я использую формулировку «начинается с javascript». Обычно чтобы оставить href пустым пишутjavascript:void(0), но мы не можем рассчитывать что так прописано во всех случаях без исключения (возможно отсутствие или присутствие пробела после двоеточия, например).

Правило 3

«Если используется класс button, то элемент должен быть кнопкой, по крайней мере с точки зрения доступности»

.button:not(button):not([role="button"]):not([type="button"]):not([type="submit"]):not([type="reset"]):after,
.btn:not(button):not([role="button"]):not([type="button"]):not([type="submit"]):not([type="reset"]):after, 
a[class*="button"]:not([role="button"]):after {
   content: 'Если вы хотите чтобы этот элемент выглядел как кнопка, сделайте его кнопкой, черт побери!';
   /*… уродливый стиль …*/
}

Примечание: В этом примере мы демонстрируем как можно составить цепочку отрицаний проводя проверку атрибутов. Каждый селектор читается так: «Если для элемента применён класс указывающий на то, что перед нами кнопка, но это не элемент button, и для него не указана соответствующая роль, чтобы он был кнопкой с точки зрения доступности, если это не input, который используется как кнопка, значит… кое-кто врёт». Мне пришлось использовать [class*="button"] чтобы уловить множество разновидностей классов на Topcoat (всего 62!), которые не подходят для превращения ссылки в настоящую кнопку. Я заметил, что некоторые разработчики используют button-container и ему подобные для родительских контейнеров, потому добавил оговорку a чтобы избежать некорректных результатов. Вы можете признавать класс .btn, который используется в Twitter Bootstrap, однако вы должны знать (если внимательно читали документацию) что являются ли ссылки или кнопки кнопками при его использовании — спорный вопрос.

Правило 4

«Если для элемента указано role="button", он должен на что-либо ссылаться при отключённом JavaScript»

a[role="button"]:not([href*="/"]):not([href*="."]):not([href*="?"]):after {
   content: 'Используйте резервную ссылку или элемент button.';
   /*… уродливый стиль …*/
}

Примечание: Можно быть вполне уверенным, что если href не содержит /. (обычно перед разрешением файла) или ? (начало строки запроса), скорее всего он липовый. Если вы хотите, чтобы ссылки вели себя как кнопки и возвращали return: false когда JavaScript включён — ладно, но когда JavaScript отключён они должны вести на какую-то страницу. Кстати, это единственная уважительная причина, чтобы не использовать вместо ссылки <button>.

Правило 5

«Гиперссылку нельзя отключить»

a.button[class*="disabled"]:after, 
a.btn.disabled:after,
a[class*="button"][class*="disabled"]:after {
   content: 'Ссылки нельзя отключать. Используйте элемент button с disabled="disabled".';
   /*… уродливый стиль …*/
}

Примечание: Даже старые агенты пользователя понимают атрибут disabled, так что используйте его с подходящим элементом в соответствии со стандартами. Для атрибутов можно применять конкатенацию точно так же как для классов: в части с последними тремя селекторами говорится: «Если мы имеем дело с ссылкой, которая содержит подстроку button и подстроку disabled, должно быть выведено сообщение об ошибке». В таблице стилей Twitter Bootstrap используется вторая форма, .btn.disabled, но не с префиксом a. Ошибкой считается только использование disabled для ссылок.

Правило 6

«У кнопок в формах должен быть прямо указан тип ввода»

form button:not([type]):after {
  content: 'Это кнопка отправки информации, кнопка обнуления полей или что? Используйте type="submit", type="reset" или type="button"';
}

Примечание: Нам нужно определить указан ли тип ввода для кнопок в формах потому что в таком контексте без чётко указанного типа некоторые браузеры интерпретируют кнопки как type="submit". Мы должны быть полностью уверены, что кнопке не будет присвоена функция отправки информации на сервер если она должна отвечать за какое-либо другое действие.

Правило 7

«И ссылки, и кнопки должны содержать какой-либо контент или ARIA-метку»

a:empty:not([aria-label]):not([aria-labelledby]):after, 
button:empty:not([aria-label]):not([aria-labelledby]):after, 
button:not([aria-label]):not([aria-labelledby]) img:only-child:not([alt]):after, 
a:not([aria-label]):not([aria-labelledby]) img:only-child:not([alt]):after {
   content: 'Все кнопки и ссылки должны содержать текстовый контент, изображение с текстом alt или метку ARIA';
   /*… уродливый стиль …*/
}

Примечание: Кнопки и ссылки, которые не содержат никакой информации о своём предназначении (в текстовой или графической форме) являются неправильными. Последние два селектора наверное самые сложные из всех, которые я когда-либо написал. В версии для ссылки селектор можно прочитать как-то так: «Если мы имеем дело с ссылкой, которая не имеет атрибут aria-label или aria-labelledby и содержит только изображение в качестве содержимого, но у этого изображения нет атрибута alt, нужно вывести уведомление об ошибке.» Также обратите внимание на использование селектора :empty. Можно утверждать что парные теги не должны оставаться пустыми.

Аплодисменты тому, кто первым используя revenge.css обнаружит где я нарушил своё же правило описанное в этой статье. Поверьте, ошибка имеет место без сомнений.

Заключение

Я использую селекторы и приёмы описанные выше не для того чтобы попробовать что-то новое или получить новый материал для статьи. Селекторы атрибутов сами по себе ничем новым не являются. IE 6 —единственный браузер, который их не поддерживает. Я использую их потому что у меня нет времени и ментальных ресурсов на раздельное «сочинение» HTML и CSS. Мой мозг для этого недостаточно хорош. Я стилизирую заголовки страницы с помощью [role="banner"], а не .page-header потому что только так я буду знать, видя желаемый визуальный эффект, что я правильно использовал ориентир для навигации. Как еще за этим можно уследить? Нельзя полагаться на тестирование, потому что на этом этапе уже, как правило, поздно.

Нет такого понятия как семантичный CSS. Есть семантичный HTML и его представление. В этой статье я постарался это продемонстрировать, соединяя напрямую функцию и форму веб-страниц, вы получаете множество возможностей. С одной стороны, вы можете использовать селекторы, которые задействуют визуальные мотивы только в определённой разметке. С другой стороны, вы получаете возможность проверить разметку на плохой код и разрушить визуальный дизайн из-за уродливой реальности, на которой он основан.

Селекторы в вашем арсенале не всегда будут одинаково наделены семантикой и умны. Классы часто необходимы для полифилов очень нужных элементов или атрибутов, которые еще не внесены в стандарты. Именно так .footer стал <footer> и type="text" (с кучей JavaScript) стал type="url". В других случаях они помогают поддерживать несемантическую разметку с сеточными фреймворками и им подобными.

Однако, если вы стремитесь наделить свой CSS полностью самостоятельной логикой, вам придется смириться с ненужными конфликтами формы и функции. В таком случае только неусыпная бдительность поможет избежать недоступности и невалидности. Что еще хуже, попытка построить псевдо-семантику исключительно на классах повышает риск увязнуть в бесконечной дискуссии о том каким должно быть семантичное название. Таким образом вы начнете тратить меньше времени на использование пульта управления от телевизора и больше на его рассматривание и обдумывание.

Жизнь слишком коротка для этого.