Осмысленный CSS: описывайте стилями свою логику

Перевод статьи Meaningful CSS: Style Like You Mean It с сайта alistapart.com для css-live.ru. Автор — Тим Бакстер.

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

И всё же слишком часто наша разметка напоминает клубок div-ов, а наш CSS — трясина классов, лишь отдаленно связанных с этими div-ами. Мы строим пирамиды вложенных div-ов и навешиваем на каждый div пачку классов — но при взгляде в CSS эти классы едва ли подскажут нам, что именно мы хотели описать. Даже когда разметка семантична и осмысленна, в итоге мы переопределяем ее CSS-классами, взятыми с потолка. У них нет внутреннего значения.

Много лет назад нас предупреждали насчет такого подхода:

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

Джефри Зельдман, «Разработка по веб-стандартам», 1-е изд.

Похожие доводы приводит и W3C:

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

Так почему же всё-таки наш CSS так нещадно злоупотребляет классами, и зачем мы мусорим своими классами в разметке? Что мешает нашему CSS быть столь же семантичным, как разметка? Почему они оба не могут стать семантичнее и осмысленнее, развиваясь в тандеме?

Совершенствуем объекты

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

Системы для надежного создания сложных, многоразовых компонентов породили проблему прогрессирующей злокачественной классянки — настолько, что сегодня разметку слишком часто пишут ради CSS, а не наоборот. Даже пытаясь писать семантичную, доступную разметку, мы всё равно вынуждены лепить поверх нее еще какие-то свои значения, чтобы угодить стилям. И в разметке, и в CSS у нас отразилось то время, когда для описания объектов у нас были div-ы да классы, только и всего. В любой непонятной ситуации добавляй еще тех и других. Так было безопаснее, особенно для старых браузеров, так что мы ориентировались на самые безликие объекты, какие были.

Сегодня мы уже не связаны этим. Мы можем определять объекты куда совершеннее. Мы можем создавать семантичный, самодокументируемый и осмысленный CSS, который понимает, что он описывает, и не уступает лучшей современной разметке в выразительности и доступности. Мы можем определить всего слона, а не писать что-то вроде «.колонна» или «.шланг».

Проясняем пару моментов

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

У Ларсона в серии «Противоположная сторона» есть одна карикатура, на которой человек таскает за собой краску и подписывает всё, что видит. По его входной двери стекает свежая надпись «Дверь», на дереве красуется подпись «Дерево», а на боку у кота отчетливо выведено «Кот». Довольный результатом, человек говорит: «Это должно прояснить пару моментов».

Каждый из нас — этот персонаж Ларсона, радующийся подписям. Мы не задумываясь пишем <table class="table"> и <form class="form">. На одном лишь Github можно наткнуться на множество примеров <main class="main">. Но зачем? Элемент main может быть только один, так что уже ясно, как сослаться на него напрямую. Новым элементам HTML5 вот-вот пойдет второй десяток лет. Ничто не мешает нам вовсю использовать их. И мы уже не можем оправдываться тем, что их, мол, не поймут наши коллеги.

Зачем заново изобретать в классах те семантические значения, которые уже есть в спецификации? Зачем повторять их и делать из них кашу?

Конечный пользователь может и не заметить, если вы налепите на элемент form класс form, и вряд ли вообще об этом задумается — но вот вам придется. Вам нужно думать о раздутой разметке и о лишних миллисекундах ожидания для пользователя. Вам нужно думать о читаемости. И раз уж вам за это платят, то нужно подумать и о том, что вы всё-таки профессионал, а профессионалы не пишут лишнего хлама. Последними словами любителей верстки на таблицах тоже были «Какое мне дело».

Переходим к семантике

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

А на тот случай, когда семантики HTML5 мало, у нас есть ARIA — стандарт, специально призванный закрывать дыры. От ARIA часто отмахиваются как от «доступности и только», но на самом деле — в полном соответствии с названием — он про «Доступные насыщенные интернет-приложения». А значит, расширенной семантики в нем выше крыши.

Например, если хотите определить шапку в верху страницы, можно создать собственный класс .page-header, который не несёт никакого реального смысла. Можно использовать элемент header, но поскольку их может быть несколько, это может не сработать. Но в спецификации ARIA уже есть [role=banner], который как раз четко говорит: «Это шапка в верху страницы».

Когда уже есть <header role="banner">, добавлять еще какой-то класс попросту излишне, это мусор. Мы и так точно знаем, о чем говорим в CSS, никакой неоднозначности.

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

Внимание: не прописывайте ARIA-ролей для элементов, у которых уже есть та же самая семантика. Например, не пишите <button role="button">, поскольку эта семантика уже заложена в самом элементе. Вместо этого используйте [role=button] в элементе, который должен выглядеть и вести себя, как кнопка, и оформляйте соответственно:

button,
[role=button] {
    … 
}

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

Ладно, но почему?

А вот почему:

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

А вот еще сценарий поинтереснее. Вот так (или еще хуже) может выглядеть типичная разметка формы:

<form class="form" method="POST" action=".">
	<div class="form-group">
		<label for="id-name-field">Ваше имя</label>
		<input type="text" class="form-control text-input" name="name-field" id="id-name-field" />
	</div>
	<div class="form-group">
		<input type="submit" class="btn btn-primary" value="Enter" />
	</div>      
</form>

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

Распространённая и оптимальная разметка формы
Как у вас сейчас Как могло бы быть Почему
.form form Большинство ваших форм будут — по крайней мере, в идеале — следовать последовательным паттернам разработки. Оставьте дополнительные идентификаторы для тех форм, у которых их нет. Доверяйте вашим паттернам разработки.
.form-group form > p или fieldset > p W3C рекомендует оборачивать элементы формы в теги абзаца. Это предсказуемый, рекомендуемый паттерн для оборачивания элементов формы.
.form-control или .text-input [type=text] Вы уже знаете, что это поле для текста.
.btn and .btn-primary или .text-input [type=submit] Отправка формы является по сути основным действием.

Некоторые распространенные паттерны разметки форм и их более оптимальная замена

Исходя из этого, вот новая, улучшенная разметка.

<form method="POST" action=".">
	<p>
		<label for="id-name-field">What’s Your Name</label>
		<input type="text" name="name-field" id="id-name-field" />
	</p>
	<p>
		<button type="submit">Enter</button>
	</p>
</form>

Функциональность та же самая.

Или возьмём, к примеру, этот CSS. Вы наверняка разберетесь, что он описывает и что в нем происходит.

[role=tab] {
	display: inline-block;
}
[role=tab][aria-selected=true] {
	background: tomato;
}

[role=tabpanel] {
	display: none;
}
[role=tabpanel][aria-expanded=true] {
	display: block;
}

Заметьте, что здесь можно было бы использовать и [aria-hidden], который семантичнее, чем служебный класс .hide, но aria-expanded подходит лучше. Но вообще панель вкладок может обойтись без них обоих.

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

На первый взгляд может показаться, что это не подходит для тех самых случаев, ради которых создавались системы наименования CSS: на крупных проектах, в больших командах. Это вовсе не факт. Паттерны наименования CSS-классов загоняют разметку в очень жесткие рамки. Другими словами, итоговый HTML вынужден подчиняться CSS-оформлению. Существенное различие в том, что с техникой осмысленного CSS стили отражают разметку, а не наобоорот. Размер проекта здесь вообще ни при чем. При каждом варианте придется соблюдать какие-то правила.

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

Не писать хорошие разметку и CSS, потому что некоторые члены команды не могут понять базовые HTML-структуры и семантику — отговорка. Наша сфера может — и должна — рассчитывать на лучшее. Иначе мы бы по-прежнему строили сайты на таблицах, поскольку CSS-верстка якобы слишком сложна для новичков. Это постыдный аргумент.

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

  • Когда нет подходящих элементов, атрибутов или стандартных структур данных. Иногда, вы и правда могли бы использовать объекты, для которых не нашлось места в спецификации HTML, в ARIA или в микроформатах. Такое не часто, но бывает. Просто убедитесь, что ваш .единорог — это не лошадь с приклеенным к ней рогом.
  • Когда нужно произвольно сгруппировать различную разметку в едином визуальном стиле. В этом примере хочется, чтобы разные по смыслу объекты выглядели одинаково. Скорее всего они должны быть одинаковыми и семантически, но у вас могут быть свои причины для того, чтобы они были разными.
  • Это будет служебный миксин.

Еще может быть опасение, что придется писать гигантские списки селекторов. Иногда полезно создать обёрточный класс, но если честно, у вас не должно быть большой пачки селекторов, поскольку сами элементы семантически различаются и не должны иметь так много общих стилей. Суть осмысленного CSS в том, чтобы знать из CSS, что этот button или [role=button] относится ко всем кнопкам, а [type=submit] — всегда основной элемент действия в форме.

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

Пришло время взяться за наш CSS как следует. Можно остаться догматически привязанными к уже устаревшим паттернам, а можно двигаться вперёд с CSS и разметкой, которые соответствуют официальным спецификациям и стандартам. Теперь можно использовать настоящие объекты, а не создавать их абстрактные представления. Поддержка в браузерах есть. Стандарты и ссылки на месте. Можно начать сегодня. Только привычка останавливает нас.

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

16 Комментарии

  1. Алексей

    кролик.утка это сильно!
    Вообще статья понравилась! Не очень люблю когда учат жизни, поскольку ко многим вещам нужно прийти самому и подталкивать бессмысленно, но когда ненавязчиво дают хорошие ориентиры это радует!
    Если честно, даже не знал о [role=tab][aria-selected=true] таких вещах. Судя по статье — штука мощная, и, главное, как мне кажется, очень может способствовать отлаженной групповой работе…
    Попробую внимательно почитать ссылке в этой статье…

  2. Тахир

    Меня до сих пор удивляет, почему в3орг рекомендует делать так:
    Mushroom
    все-таки как-то не семантично выходит, разве нет? Они сами себе противоречат. Ну сделали бы хотя бы так Mushroom ну или что-нибудь подобное.

    Еще возникает такой вопрос относительно этого примера
    form > p или fieldset > p
    А если я захочу сделать текстовый блок (параграф) в форме, мне придется уже переопределять что ли его классом, например, blabla?

    Как-то все двойственно…

  3. GreLI

    Мы строим пирамиды вложенных div-ов и навешиваем на каждый div пачку классов — но при взгляде в CSS эти классы едва ли подскажут нам, что именно мы хотели описать. Даже когда разметка семантична и осмысленна, в итоге мы переопределяем ее CSS-классами, взятыми с потолка…
    Мы не задумываясь пишем <table class="table"> и <form class="form">.

    Ничего себе сверхобобщение!

    Рекомендации же годятся для совсем простенького сайта. Для сколь-либо сложного сайта, где форма аутентификации и форма отправки данных выглядят совершенно по-разному (другие поля, по-разному выглядящие кнопки действия) без классов уже не обойтись. А что будете делать, если вам вдруг понадобятся «родные» контролы?

    1. SelenIT

      Статейка провокационная, да. Но то, что не иначе как под ее воздействием даже наскоро подправили W3C-версию спеки HTML5.1, говорит, что провокация удалась кое-что она зацепила достаточно метко.

      Насчет обобщения — имхо, две части цитаты относятся к разным тезисам Тима. Первый абзац говорит о том, что вдобавок к описанию структуры страницы в разметке авторы то и дело описывают ее еще и отдельно в виде иерархии классов, во многом дублирующей иерархию тегов (но не всегда и не везде — об этом тоже упомянули в комментариях к оригиналу статьи). А во втором абзаце Тим возражает конкретно против практики именования классов, полностью дублирующих название/назначение тега.

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

      Насчет примера с двумя формами — опять же в комментах к оригиналу, Тиму привели подобное возражение (правда, с формой поиска), в ответ он предложил различать формы по [role="search"]. И с идеей оттуда же насчет использования реального состояния элемента вместо соотв. служебного класса ([disabled] или :disabled вместо .disabled) я скорее согласен, чем нет (по крайней мере, пока не встанет вопрос жесткой оптимизации и т.п.).

  4. Иван Калинин

    А есть ли идеи, как используя этот подход писать стили для сеток? Или я так понимаю, это будет то самое исключение из правил?

    1. SelenIT

      Предполагаю, что в идеальном мире с волшебными единорогами при таком подходе отдельных стилей для сеток не понадобится. Будет, скажем, общий контейнер body с display:flex, в котором будут aside с flex: 1 и main с flex: 3 (к примеру). А содержимое main тоже может, например, выводиться в три колонки — тогда визуально будет ровная сетка из 4 колонок одинаковой ширины и высоты. А в еще более идеальном для верстальщика мире — который должен настать уже где-то этой осенью — вообще можно будет позиционировать что угодно куда угодно с помощью грид-раскладки и подсеток, и никакой разметки чисто ради оформления не понадобится в принципе!

      Но в реальном мире, да, без компромиссов не обойтись. Не случайно ответ на эту статью с обоснованием, почему от «классянки» бывает не только вред, но и польза, в целом больше понравился сообществу, чем эта статья.

  5. Next Big Thing

    Ужасная статья с глупыми советами, которые совершенно не подходят для разработки в современном вебе. Тим Бакстер там, наверно, только домашние странички верстает?

    1. SelenIT

      Тем не менее, Тим не первый, кто высказывает похожие мысли. Конечно, он кидается из одной крайности в другую, но напоминание о том, что HTML-элементы можно использовать по назначению, а не имитировать перекрашенными через CSS самоделками из дивов, спанов и <a href="#">, уже достаточно ценно. У Хейдона Пикеринга была статья с похожими соображениями и остроумным предложением использовать этот подход для обратной задачи — не красиво оформить семантически верные элементы, а наоборот, намеренно уродливо выделить элементы, использованные не по назначению, чтобы они бросались в глаза и ошибки в семантике разметки было легко найти и исправить.

  6. Alex Bondarev

    Люди, а что за вольный перевод?

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

    Про какой язык документа идет речь? Про какие его структурные элементы? Про какие значения этих элементов? :)

    1. SelenIT

      Это как раз донельзя буквальный перевод вольного языка спецификации:

      …since the structural elements of a document language often have recognized and accepted meanings…

      А вот уже в вольном переводе с него на человеческий, «язык документа» — это язык разметки (чаще всего HTML, но может быть и какой-нибудь SVG, MathML или вообще «голый» XML, к примеру), «структурные элементы» — элементы, к которым у браузера привязана какая-нибудь полезная функциональность (типа <a> или <form>), а «значения» — сама эта функциональность (напр. кликабельная ссылка и средство отправки данных на сервер). Про эти «значения» в HTML недавно как раз была отдельная заметка.

      1. Alex Bondarev

        Прошу прощения, SelenIT, а ваша цитата откуда? …since the structural elements of a document language often have recognized and accepted meanings… что-то я не могу её найти в ссылке из статьи. Дело в том, немного непонятно, что конкретно переводилось из того документа, возможно ссылка ошибочная. Я сравнивал перевод с фразой, которая, более всего подходит под то, что переводили именно её:

        Note. CSS gives so much power to the «class» attribute, that in many cases it doesn’t matter what HTML element the class is set on — you can make any element emulate almost any other. Relying on this power is not recommended, since it removes the level of structure that has a universal meaning (HTML elements). A structure based on «class» is only useful within a restricted domain, where the meaning of a class has been mutually agreed upon.

        1. SelenIT

          Цитата переводилась в том виде, как она приведена в самой оригинальной статье Тима Бакстера. По-моему, она совпадает (плюс-минус знаки препинания) с соотв. текстом из текущей редакции CSS 2.х. Почему Бакстер сослался не на нее, а на исторический документ — лучше спросить у него самого (возможно, просто ошибся, а возможно, хотел подчеркнуть неизменность главной сути этого совета на протяжении практически всей истории CSS).

      2. Alex Bondarev

        Просто ведь

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

        никак нельзя назвать дословным, при всём уважении к труду переводчика

        1. SelenIT

          Оригинал из статьи был таким:

          CSS gives so much power to the “class” attribute, that authors could conceivably design their own “document language” based on elements with almost no associated presentation (such as DIV and SPAN in HTML) and assigning style information through the “class” attribute… Authors should avoid this practice since the structural elements of a document language often have recognized and accepted meanings and author-defined classes may not. (emphasis mine)

          1. Alex Bondarev

            Спасибо, что прояснили, да, действительно, претензии к вольности перевода отпадают :) но остаются к Тиму и редактуре A List Apart (казалось бы)

  7. strix

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

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

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

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

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