Skip to content

Latest commit

 

History

History
602 lines (498 loc) · 53.8 KB

styleguide.md

File metadata and controls

602 lines (498 loc) · 53.8 KB

iam-css styleguide

Актуальная, версия 3.2

Краткая документация по iam-css v3

Вступление

iam-css не является самостоятельной методологией, это всего лишь нотация, набор соглашений, призванных облегчить написание и сопровождение css-правил. Соответственно никакого js-движка, использования утилит по сборке не требуется, никаких требований по организации структуры папок и файлов iam-css не предъявляет.

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

Какую проблему решает iam-css

Как и БЭМ, iam-css упрощает сопровождение CSS-селекторов, предлагая способ уменьшения энтрепии, ведущей в CSS hell/specificity escalation CSS hell - состояние проекта, при котором разработчик, для переопределения того или иного css-свойства, вынужден писать более специфичные селекторы, или применять !important. Почему вынужден? Потому что в случае использования библиотек, их код редактировать нет возможности, а в случае со своим проектом порой нет времени на тестирование всех мест/страниц, которые может затронуть изменение одного лишь правила исходного блок-элемента (без модификатора).

Каким образом iam-css решает css-hell?

Для уменьшения "многословности" в именовании css-классов, классический БЭМ позволяет (но не рекомендует) использовать комбинированные селекторы. Существует схожий, но на столько же css-специфичный способ в виде ABEM. Комбинированные селекторы более специфичны, чем селекторы классического БЭМ, потому такие решения не пользуются популярностью. Но, следует заметить, что классический БЭМ при этом нарушает принцип DRY, требуя чтобы каждый модификатор содержал в себе именование блок-элемента.

iam-css, удовлетворяет приципу DRY, позволяя писать селекторы содержащие БЭМ-модификаторы схожим с ABEM образом. При этом iam-css даёт возможность получить минимально-специфичный селектор, сохраняя (как в БЭМ) для каждого модификатора пространство имён (блок-элемент), тогда как классы-модификаторы ABEM, при миксовании блок-элементов, могут привести к непредвиденным проблемам от их пересечения (неуникальности именования отдельного модификатора).

iam-css, как и БЭМ, следует принципам модульности и меньшей связности — каждый селектор должен определять стили только для самого модуля (элемента), а не его дочерних элементов.

Долой вложенные селекторы!

Из этого принципа следует самое главное ограничение: не пользоваться вложенными селекторами, как свободного уровня вложенности .wysiwyg-editor p (не забываем, что селекторы обрабатываются задом наперёд), так и строгого .wysiwyg-editor > .iam-p. Не следует забывать, что такими nesting селекторами зачастую грешат разработчики использующие в CSS-препроцессоры типа LESS/SASS. Это одна из самых популярных фишек препроцессоров, но она делает код лишь визуально проще, удерживая проект в css-hell!

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

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

Как же быть с WYSIWYG-редакторами?! Не заставлять же пользователя WYSIWYG, при вёрстке статьи, оформлять тэги с классами/атрибутами именование которых в следуей версии продукта может измениться? Конечно же нет! С помощью css-переменных можно передать любой родительский контекст, совершенно не увеличивая специфичность селектора! Смотри использование space-toggle в примере со вложенными списками.

Наиболее распространённый способ задать цветовую схему элементам следующий: .theme-dark .nav-item. Как обойтись без вложенных селекторов в данном случае? Как вариант, тема оформления должна быть реализована не с помощью модификаторов в DOM, а через подсоединённые css-файлы (link rel="alternate stylesheet"), либо с помощью контекста css-переменных: .theme-dark { --nav-item-color: #fff }, .nav-item { color: var(--nav-item-color) }

CSS-переменные уже давно предоставили очень гибкие возможности, но по прежнему большинство разработчиков не догадываются использовать этот инструмент для снижения специфичности селекторов, css-hell всё ещё с ними! ;)

Ну, за минимальную специфичность!

Стремление к минимальной специфичности селектора это хорошее стремление, но не следует обрубать себе пальцы и отказываться от стандартных возможностей CSS, в виде checkbox hack, который позволяет вынести boolean state всем знакомого .active в скрытый input. Псевдоклассы так же повышают специфичность, порой нетривиально, но и от них не стоит отказываться вместо изваяния собственного aria-велосипеда. Либо всё же пишем js и не обращаем внимание на данный абзац, тут как кому удобнее.

iam-css = БЭМ + AMCSS

iam-css следует методологии БЭМ. БЭМ оперирует тремя основными понятиями: Блок, Элемент, Модификатор. С модификатором всё понятно, по БЭМу он определяется так: модификатор - сущность, определяющая внешний вид, состояние или поведение блока либо элемента.

Блок блока, элемент элемента, да какая разница?!

С разделением понятий Блок и Элемент всё гораздо сложнее. Очень часто начинающие БЭМеры совершают много ошибок, и это нормальное положение дел, так как верстальщики и js-программисты при работе с HTML-разметкой привыкли оперировать понятием DOM-элемент (тэг).

Относительно БЭМ, iam-css (с версии 3.1) не требует от разработчика явно разделять именования блока и элемента, хотя и не отрицает их разницу. Имя блока и элемента предлагается писать подряд в нижнем регистре, разделяя лишь слова друг от друга (camelCase не удовлетворяет синтаксису именования html-атрибутов).

При этом допущении нет идеологического конфликта с БЭМ-методологией, поскольку блоки и элементы определяются элементарно: внутри вёрстки компонента существуют тэги HTML - это элементы данного компонента (да хоть элементы-модифицированного-элемента-корневого-элемента), и нестандартные HTML-тэги (веб-компоненты) - это другие блоки, к которым можно примиксовывать именование конкретного элемента родительского блока. Именование самого блока устанавливается на корневой html-элемент шаблона, например iam-select

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

При этом следует помнить, что БЭМ предлагает всего один уровень вложенности элементов, а не бесконечный. Что это значит? Это значит, что порой наше желание создать элемент-элемента продиктовано лишь деревом DOM, содержащим такую вложенность. Например:

<article><p>Article paragraph</p><blockquote><p>Quote paragraph</p></blockquote></article>

По БЭМ в проекте Яндекс-дзен, селекторы, написанные джуниором, будут примерно такими:

.dz-article { ... }
.dz-article__p { ... }
.dz-article__quote { ... }
.dz-article__quote__p { ... }

Опытный разработчик, с БЭМом головного мозга, напишет так:

.dz-article { ... }
.dz-article__p { ... }
.dz-article__quote { ... }
.dz-quote { ... }
.dz-quote__p { ... }

По iam-css блоков будет меньше, поскольку при том же плоском списке элементов, для указания родительского контекста пропадает необходимость ручного миксования:

[iam-body ~= "dz"] {
	--iam-article-margin: 0 auto 1rem;
}
[iam-article] {
	--iam-p-font-size: 1.2rem;
	margin: var(--iam-article-margin, initial);
}
[iam-p] {
	font-size: var(--iam-p-font-size, initial);
}
[iam-quote] {
	--iam-p-font-size: 1rem;
}

Что нам даёт использование атрибутов вместо значений class?

iam-css не использует селекторы классов, вместо них используются селекторы атрибутов CSS 2.1! (в своё время БЭМеры отказались от этой затеи).

Все модификаторы рассматриваются в контексте/пространстве имён конкретного блок-элемента (именования атрибута), это уменьшает путаницу с глобальными модификаторами, которые были в общей свалке атрибута class до БЭМ, и уменьшает многословность БЭМа (разработчики Яндекса вынуждены были каждый модификатор предварять текстом "блок-элемент").

Гугл, кстати, решил уйти от блоков, и опирается на элементы, что, без предварительной сборки препроцессорами, возвращает нас к проблемам которые был призван решить БЭМ: например css-каскадирование, использующееся для того чтобы отличить один элемент от такого же элемента, но имеющего другой уровень вложенности.

По прошествию времени, и я сделал вывод, что "блок" это лишь контекстный модификатор. В том числе и более верхний "блок" является лишь модификатором для внешнего вида "элемента". В отличии от oldstyle, где всё размещалось в атрибуте class, custom-атрибуты позволили помодульно разделить модификаторы (каждому блоку или блок__элементу своя группировка).

Версии:

  • iam-css 1.0: .block[element *= "-mod-"],
  • inverted iam-css 1.1: .element[block *= "-mod-"],
  • iam-css 2.0: *[block__element *= "-mod-"],
  • iam-css 2.2: *[element *= "^block^"][element *= "-mod-"],
  • iam-css 3.2: [block-element ~= "mod"]

НО! Если элемент рассматривать как равноправную часть (атрибут содержал бы только именование элемента) сразу в десятке миксов/блоков (указанных в значении атрибута), это усложнит код и его восприятие. Потому в iam-css 3.2 не рекомендуется использовать именование блока в качестве модификатора (то есть так [iam-p ~= "iam-article-main"]), вместо этого в контексте блока (или модифицированного блока) переопределяется значение известной css-переменной элемента.

Interface

Interface - абстрактное понятие как в классическом ООП, определяющее набор свойств/функций объекта, но не его реализацию. Не путать с визуальным интерфейсом!

В БЭМе аналогия Interface соответствует "Блок", а если быть более точным, то БЭМ-блок это, скорее, пространство имён, распространяющееся на маркированное множество dom-элементов. Пространство имён позволяет отделить одну функциональность от другой и сгруппировать воедино все составляющие части этой функциональности. Веб-компоненты со scoped styles или CSS in JS имеют свои реализации пространства имён, и потому не нуждаются вБЭМ. При этом отмечу, что CSS in JS это плохое инженерное решение, поскольку смешивает разные уровни абстракций в одну кучу!

В iam-css Interface выражен в виде определения значения css-свойства с включением css-переменных. Таким образом мы открываем интерфейс переопределения данного свойства, для воздействия на него с любого элемента DOM, расположенного по восходящей линии ancestors. То есть мы можем указать границы/контекст, в рамках которых будет действовать данная установка, вплоть до того, что контекстом может быть любой блок-элемент даже другого блока, расположеного выше в той же ветке иерархии. И самое главное, что эту возможность нам даёт сам CSS, а не специализированный JS-инструмент или CSS-препроцессор!

Attribute

Attribute - основа реализации селекторов по iam-css как преемника AMCSS. Атрибут в iam-css 1.0 аналогичен понятию "Элемент" в БЭМ или (по iam-css с версии 3.1), аналогичен понятию Блок-Элемент (версия 2 поддерживает обе декларации). HTML-атрибут используется в iam-css для ссылки на конкретный DOM-элемент с которым предстоит работать (опеределять js-поведение и оформление).

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

Такова эволюция CSS-селекторов HTML-разметки:

  • до CSS (html3) оформление размещали в множестве атрибутов (align, bgcolor, width...)
  • затем из атрибутов оформление вынесли в inline-стили
  • затем вынесли оформление в отдельный файл, css-селекторы которого основывались на атрибуте "class" и на именовании тэгов
  • после того как атрибут класс был облюбован множеством фреймворков, js-хуков и метками микроданных, разработчикам, во избежание коллизий, пришлось уводить селекторы в custom data-attributes
  • в идеале нас ждёт переход на веб-компоненты, Google уже перешёл!

Если (для валидности) используются data-атрибуты, то рекомендуется префиксом добавлять namespace, отличающий например вашу [data-my-button] от любых других opensource проектов [data-ms-button].

iam-css при работе со стандартными атрибутами (в т. ч. ARIA) не требует фанатичного соблюдения правила, что в значениях атрибута обязательно должно размещаться значение.

/* iam-css 1.0 */
.iTree[role = "treeitem"] {} /* с использованием aria */
.iTree[data-am-item] {} /* без использования aria, свой valid HTML "велосипед" */

/* iam-css 1.1 */
.leaf[x-mas-tree] {}

/* iam-css 2.0 */
*[data-iam-tree__item] {}
*[iam-tree--item] {} /* это не модификатор, просто кто-то любит только дефисы */

/* iam-css 2.2 */
*[x-item *= "^x-tree^"] {}

/* iam-css 3.1.1 */
[iam-tree-item *= "iam-south-park v-pine"] {}
[iam-south-park-tree-item ~= "v-pine"] {}

Идеологически блок как интерфейс не может иметь модификатор, тогда как контейнер блока (соответствующий элемент) может. Практически же, с версии v3, корневой элемент всегда именуется один в один с названием блока. Корневой элемент/контейнер, в котором располагаются все элементы "блока" может также называться holder'ом, либо, если этот контейнер объединяет множество однородных элементов, может называться list или items. Например:

/* iam-css 1.0, когда сам блок не имеет модификатора, а контейнер имеет */
.iTree[data-am-list *= "-small-"] {} /* без использования aria, свой "велосипед" с valid аттрибутами */

/* iam-css 1.1, модификаторы указываются при имени блока - удобно функциональность миксов, при одном и том же элементе, разделать */
.holder[x-mas-tree *= -small-] {} /* инвертированный вариант. Без оглядки на валидность атрибута */

/* iam-css 2.0 */
*[x-tree__holder *= "-small-"] {}

/* iam-css 2.2 с блочно-контекстным модификатором */
*[x-holder *= "^x-tree^-small-"] {}

/* iam-css v3.2.1 valid attr */
[data-iam-tree ~= small] { /* установка space toggler для small в корневом элементе */ }
[data-iam-tree-leaf] { /* проверка родительского space toggler в дочернем элементе  */ }

Modificator

Modificator - Признак-индикатор, указывающий на состояние элемента (state is-) или его особенный внешний вид (view). Значения модификаторов размещаются в html-атрибутах (смотри валидность custom-атрибутов по W3C). В сравнении с БЭМ'ом, для простоты восприятия, в определении исключено упоминание о "Блоке", речь идёт о DOM-элементе.

Наименование модификатора размещается в значении атрибута, и тем самым iam-css решает ту же проблему, что и CSS-modules: даёт возможность иметь "базу" (селектор атрибута без символа "=") и доопределять элемент модификатором при помощи селектора атрибута с его значением, например (первой задаётся "база", за ней следует "модификация") [iam-tree-leaf] {} [iam-tree-leaf ~= small] {}. Cпецифичность селектора "базы" и "модификации" одинакова, синтаксис "модификации" обязательно содержит в себе "базу".

В отличии от БЭМ, в iam-css не требуется дублировать "базу" в самой разметке, поскольку атрибут соответствующий элементу всегда существует, не важно модифицирован он или нет! Такой синтаксис исключает ещё одну БЭМ-ошибку, когда разработчик указывает только класс-модификатор (пусть и со всеми регалиями блок-элемента), но забывает добавить сам класс блок-элемента.

Модификатор vs модификация

Термин "модификация" относится к соответствующему селектору целиком, тогда как термин "модификатор" указывает на конкретное значение атрибута в html или значение правее знака "=" в селекторе-модификации.

Модификация по id

Для тонкой настройки конкретного экземпляра реиспользуемого блока, вместо того чтобы использовать селектор #myId, чтобы вес селектора остался минимальным, рекомендуется воспользоваться атрибутивным селектором, например:

*[x-tree__list *= "-iconed-"]{}
*[id = "folders"]{ /* отличать -iconed- с помощью контекстной переменной */ }

Одновременное использование нескольких модификаторов

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

/* iam-css 2.0 */
*[iam-combobox__item *= -vSpcMinimal-]{
	margin: 0;
	padding: 0;
	line-height: 1.2em;
}

*[iam-combobox__item *= -wgtCounries--vSpcMinimal-]{
	width: 200px;
}

/* iam-css 3.0 */
[iam-combobox-item *= "{iam-countries}{--spc-minimal}"]{
	width: 200px;
}

/* iam-css 3.1.1 */
[iam-combobox-item *= "iam-countries spc-minimal"]{
	width: 200px;
}

В iam-css v3.2.1 контекст возможно вынести в переменную (флаг контекста), например:

[iam-list] {
	--iam-list--: ; /* toggler родительского контекста */
}

[id = "countries"] {
	--iam-list-countries--: var(--iam-list--); /* toggler примиксованного контекста к другому родительскому */
}

[iam-list ~= "countries"] { /* или модификатором в iam-list */
	--iam-list-countries--: var(--iam-list--);
}

[iam-list ~= "spc-minimal"] {
	--iam-list-spc-minimal--: ;
}

/* рекомендуется использовать всё же не тэг, а предустановленный атрибут, например iam-li */
li {
	--iam-list-font-size-base: var(--iam-list--) 15px;
	--iam-list-countries-font-size: var(--iam-list-countries--) 20px;
	font-size: var(--iam-list-countries-font-size, var(--iam-list-font-size-base, 18px));

	--iam-list-li-line-height-base: var(--iam-list--) 1.6;
	--iam-list-li-line-height-min: var(--iam-list-spc-minimal--) .8;
	line-height: var(--iam-list-li-line-height-min, var(--iam-list-li-line-height-base));

	--iam-list-li-color: var(--iam-list--) orange;
	color: var(--iam-list-li-color, inherit);
}

Состояние

Состояние - динамически изменяющийся модификатор, отвечающий за отображение элемента и в целом блока (состояние блока задаёт контекст отображения элементов блока). Элемент/контейнер блока может находится сразу в нескольких состояниях.

Состояния, в отличии от модификаторов, могут иметь одни и те же названия в разных блоках (например миксах) и элементах.

Использование стандартного способа указания состояния при помощи стандартных атрибутов или WAI ARIA (мнение Pepelsbey), таких как disabled, checked, hidden, readonly, required, aria-busy, aria-expanded, aria-grabbed, aria-invalid, aria-pressed, aria-readonly, aria-required, aria-selected, приводит к усложнению и увеличению веса селектора. Дублировать disabled и checked в aria-disabled и aria-checked нет смысла.

Предпочтительней не ориентировать визуальное состояние на ARIA-атрибуты, и размещать наименования состояний в атрибут Элемента (block-elem) в качестве значений, возможно группировать view и state

  • 1.0 и 2.0: -viewName--stateName-,
  • 3.0: {v-name}{s-name}
  • 3.1.1 v-name s-name
  • 3.2.1 some-view-name is-named-state, с разделением слов через символ -

Селекторы состояний могут выглядеть следующим образом (tooltip реализован как подкомпонент компонента tree):

/* iam-css 1.0 */
.iTree[role = "treeitem"][aria-expanded = "true"] {}
.iTreeTooltip[x-hoverlabel]:hover {}
.iTreeTooltip[x-hoverlabel *= "-loading-"] {}

/* iam-css 1.1 "inverted" */
.leaf[x-mas-tree][aria-expanded = "true"] {}
.hoverlabel[x-mas-tree-tooltip]:hover {}
.hoverlabel[x-mas-tree-tooltip *= -loading-] {}

/* iam-css 2.0 */
*[data-my-tree__item][aria-expanded = "true"] {}
*[data-my-tree-tooltip__hoverlabel]:hover {}
*[data-my-tree-tooltip__hoverlabel *= -loading-] {}

/* iam-css 2.2 */
*[x-item *= "^my-tree^"][aria-expanded = "true"]{}
*[x-hoverlabel *= "^my-tree-tooltip^"]:hover {}
*[x-hoverlabel *= "^my-tree-tooltip^"][x-hoverlabel *= "-loading-"] {}
*[x-hoverlabel *= "^my-tree-tooltip^-loading-"] {} /* states идут строго за родительским контекстом блока */

/* iam-css 3.2 */
[aria-expanded = true] { --is-aria-expanded: ; }
[iam-tree-item] { /* использование флага var(--is-aria-expanded) */ }

[iam-tree-tooltip-hoverlabel]:hover {}
[iam-tree-tooltip-hoverlabel ~= loading]

Необходимо понимать, что составные селекторы наращивают специфичность, старайтесь придерживаться минимальной специфичности (либо какой-то базовой), чтобы при написании переопределяемого селектора не приходилось искуственно наращивать специфичность. v3.2 наименее специфичен.

Состояние по умолчанию / undefined

Даже в случае если у вас элемент имеет всего два состояния, рекомендуется прописывать оба, а не определять оформление элемента по умолчанию + переопределять эти же свойства в контексте одного из состояний. В качестве исключения можно назвать состояния с использованием стандартизированных атрибутов :disabled и :checked, управление которыми заключается в их добавлении/удалении. При наличии модификатора базового состояния, его toggle к новому состоянию возможно будет закрепить в предсказуемом месте среди множества значений атрибута. В противном случае модификаторы будут как на конвейере перепрописываться в конец атрибута. Разумеется любую рекомендацию вы можете игнорировать ;)

Динамические нативно поддерживаемые состояния

Если для управления состоянием блок-элемента не используется js, то в iam-css не считается зазорным использовать например :hover, :disable, :focus.

Вариация отображения (view)

Используется для внесения незначительных модификаций отображения, позволяет обойтись без миксов. Сюда же можно отнести и незначительные правки уникально-идентифицируемых элементов посредством [id = "countries"] или [iam-menu = "top"] {} [iam-menu = "bottom"] {}. Для корректировки отображения в v3.2 данные селекторы следует рассматривать как контекстные.

Неявно указанные view (цветовые темы и адаптивность)

В общей концепции iam-css прописано, что view - это тип модификатора, который отвечает за вариант визуального представления блок-элемента. Но не всегда view указывается явно в качестве значения атрибута на блок-элементе.

Такие view можно определить исходя из UX (полезное на weblind.ru):

Вышеобозначенные views, отсутствующие в спике модификаторов блок-элемента, могут изменяться средствами js (через установку пользовательских настроек), либо средствами html (например ширина окна браузера).

Изменение варианта таких view возможно как заменой url тэга <link>, так и через явную установку флагов в качестве значений атрибута data-themes или соответствующих классов на одном из корневых элементов страницы. Указываем значения одних и тех же css-переменных в контекстах одного типа, например [data-themes ~= "night"] {} и [data-themes ~= "day"] {} и используем эти переменные в селекторах блок-элементов.

Но использование data-themes не будет соответствовать стратегии mobile first, так как необходимо будет подключить все файлы стилей, а это траффик.

!!! Особое внимание стоит обратить на style queries.

Рассмотрим файлы-шкурки/темы (skins). Если раньше мы писали что-то типа .dark .myNumberInputLabel, то теперь, используя возможности css-variables (для ie11 polyfill, тест) пишем:

.sknLight { --t-primary-color: black; } /* Тема по умолчанию */ 
.sknDark { --t-primary-color: white; }

/* iam-css 1.0 */
.my-iInput[type = "number"] { color: var(--t-primary-color); }

/* iam-css 2.0 */
*[data-my-input][type = "number"] { color: var(--t-primary-color); }

/* iam-css 3.1.1 */
*[iam-input][type = number] { color: var(--t-primary-color); }

К "не шкуркам", но похожих на них, можно также отнести правила @supports - feture-ориентированность. Следует делать отдельные слои для мобильного, desktop'ного и print-представления, например выделив обработку touch-событий, отсутствие hover и прочих (проверяемых с помощью js, а не только через media-queries). В нынешние дни ширина экрана отошла на задний план, но следует заметить, что оформление, зависящее от ширины экрана таже не относится к модификаторам view, поскольку реализуется с помощью media queries.

Специфичность модификаторов доопределяющих модуль (custom styling)

Даже современные готовые решения, например такое как vuetify грешат селекторами, у которых специфичность сверх меры повышена при отсутствии возможности указать css-переменную соответствующего правила, например для состояния active. Переопределять такие селекторы повышая специфичность ещё сильнее, накручивая к тому же important - тупиковый путь, потому следует к тем правилам миксовать простейший БЭМ или iam-css, например так.

Миксы

iam-css по сравнению с БЭМ, использующим атрибут class, имеет синтаксическое преимущество: сокращение именований блок-элемента каждого модификатора до единственного блок-элемента. БЭМ-многоголосье особенно ярко проявляется с использованием миксов, в этом случае вообще, разметка HTML по БЭМу превращается в общую свалку (в одном html-атрибуте class=""), тогда как iam-css позволяет все модификаторы сгруппировать по отдельным custom-атрибутам.

Для декларации модификатора, которым следует пользоваться всем элементам микса, возможно использование атрибута "class" с одинаковыми именованием класса для всех примиксованных блоков - это будет "общак" ;) Не забывайте, что "class" скорей всего используется и другими фреймворками, потому добавляйте к наименованию модификатора namespace (префикс проекта/организации). Можно заморочиться и получать более специфичные селекторы с отсылкой к вёрстке АНБ, когда для отступов примиксовывались блоки-"holster", например [iam-p].margin{ var(--iam-p-margin) }, но большого смысла, при использовании контекстов css-переменных, такой подход не имеет и лишь усложняет код.

Механизм миксов схоэ с механизмом множественного наследования в ООП. Начиная с iam-css версии 2.0б миксы - это набор атрибутов на одной html-ноде.

Поскольку миксы рассматриваются как множественное наследование, то в идеале селекторы блок-элементов, участвующих в одном миксе не должны определять одинаковые css-свойства, либо эти блоки не должны быть замиксованы вместе на одной DOM-ноде. Потому в iam-css 3.2 для переопределения того же свойства примиксованный блок-элемент должен переопределять css-переменную используемую в базовом блок-элементе, а не перекрывать свойство.

Если возможно, лучше обойтись новым значением модификатора прежнего блок-элемента, добавляемым к имеющимся. Примиксованный блок-элемент, начиная с iam-css v2, это возможность сгруппировать в новом html-атрибуте дополнительный набор модификаторов, а не тот же самый, как в исходном блок-элементе.

ВАЖНО! В БЭМе миксы используют для того чтобы отделить общие layout-правила (отвечающие за различные размеры и отступы элементов внутри контейнера) от оформления контейнера которое зависит от того в каком родительском блоке/элементе он находится. Смотри уже устаревшие holder/layout блоки, которые претерпели множество изменений в рамках методологии БЭМ, и Родительский контекст.

Пример примиксовывания общей/глобальной стилистики заголовков в виде блока iam-h к элементу iam-popup-title:

<dl iam-popup id="countries">
	<dt iam-h="h3" iam-popup-title="spc-normal">Popup title</dt>
	<dd iam-popup-content>Some content</dd>
</dl>

Валидность префиксов

Если вы любитель кнопок-бэйджиков от W3C, то наверное знаете, что стандартом HTML5 допускаются помимо WAI-ARIA и стандартных атрибутов только data- атрибуты, остальные атрибуты считаются инвалидными ;)

Если использовать XHTML или XML, то у вас не будет этого ограничения, и вы не обязаны будете каждый свой атрибут (да ещё и с префиксом) предварять префиксом data-. Как вариант, можно вообще игнорировать тот факт, что код у вас не совсем валидный, и для удобочитаемости опускать префиксы data-. Только необходимо помнить о том, что вы работаете в глобальном пространстве имён, и, чтобы случайно не пересечься с каким-либо сторонним фреймворком, его необходимо сужать введением своих префиксов (например проекта/организации).

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

JS-хуки.

Для упрощения сопровождения css-селекторов, вместо getElementById вызывайте querySelector('[id = "countries"]'), с заранее известной DOM-ноды (не с корня html), чтобы сузить область срабатывания селектора. Соответственно в файлах css для атрибутивных селекторов не пренебрегайте кавычками, так проще потом будет искать.

Параметр для querySelector или querySelectorAll не должен быть динамически собираемым, чтобы необходимое js-влияние по селектору легко было обнаружено верстальщиком. Верстальщик при изменении селекторов в CSS-файлах обязан также проверять и JS код на наличие CSS-подобных селекторов в querySelectorAll, потому для JS не требуется заводить дублирующий data-атрибут, который в других методологиях призван отделить работу js от представления. Сам же JS-код не должен менять стили программно, он обязан только управлять сменой состояний элемента/блока.

Родительский контекст

Вложенные селекторы типа .class1 .class2 это плохое решение, из-за повышения специфичности. Возможно вы в этом уже убедились при работе со сторонними библиотеками, которые внезапно переопределяют оформление вашего элемента. При этом по прежнему не хочется на каждый li и td вешать развесистые атрибуты/классы вашего элемента/блока, потому предлагается воспользоваться современным способом "space toggle". Например, в случае со списком, вместо [iam-list] li код может выглядеть так:

:root {
	--list-item-clr: orange;
}

[iam-list] {
	--iam-list--: ; /* родительский контекст/переключатель */
}

/* Модификаторов у одного и того же списка может быть множество */
[iam-list ~= unordered] {
	--iam-list-unordered--: ;
}

li {
	--iam-list-item-clr: var(--iam-list--) plum;
	color: var(--iam-list-item-clr, var(--list-item-clr), inherit);
}

Примеры кода

  • Разные примеры, по iam-css 3.1 и 3.2, в том числе обновлённые из нижеперечисленных.
  • Тулбар с применением checkbox-hack в соседстве с ARIA-атрибутами, сложные селекторы по iam-css 2.0 (2017.10.09)
  • Микс кнопки и выпадающего списка, не столь актуальный с учётом предыдущих два примера, зато по iam-css 1.0 (2016.12.08)
  • Font Awesome чекбоксы/радио-кнопки с применением iam-css 2.0
  • Виджет vp-tree__list JSON по iam-css 2.0 и SVG-иконками разных размеров (2018.01.18)