Если вы когда-нибудь заглядывали за кулисы пользовательских веб-интерфейсов, то знаете для чего нужно свойство class. Оно ведь нужно для связи HTML с CSS, правда? Сейчас я расскажу о том, почему настало время отказаться от него. Имена классов — это архаичная система, используемая как неудачный посредник для примитивов UI; ещё хуже то, что они создают ужасные сочетания, приводящие к комбинаторному взрыву странных пограничных случаев. Давайте изучим этот вопрос, начав со скучного урока истории, который вы уже слышали миллион раз.

Классы очень старые, даже древние


HTML 2.0 (1996 год) стала первой опубликованной спецификацией HTML, в ней имелся фиксированный список имён тегов, и у каждого тега был фиксированный список разрешённых атрибутов. Документы HTML 2.0 нельзя было стилизовать, ведь какой в этом смысл? Экраны многих компьютеров в то время были чёрно-белыми! Самым близким к стилизации в HTML 2.0 был тег <pre>, имевший атрибут width. Для разработки HTML 3.0 потребовалось несколько лет, а Netscape и Microsoft тем временем добавляли всевозможные странные расширения, включая любимые нами теги <marquee> и <blink>. В конце концов, все разногласия были разрешены, и в 1997 году появился HTML 3.2, что позволило «стилизовать» тег <body> такими атрибутами, как bgcolor и text.


Тем временем изобрели CSS, ставший способом добавления в веб-страницы некой структуры и стилизации. Чтобы веб был повеселее. История HTML 3.2 оказалась короткой, потому что в том же 1997 году был опубликован HTML 4.0, в котором появились механизмы поддержки CSS, в том числе новые «идентификаторы элементов» — атрибуты id и class:

Чтобы повысить удобство управления элементами, в HTML был добавлен новый атрибут [2] CLASS. Всем элементам внутри элемента BODY можно добавлять классы, а настраивать их можно в таблице стилей CSS Level 1

Эти атрибуты позволили нам определять «классы» элементов, которые можно стилизовать при помощи ограниченного набора тегов. Например, <div class="panel"> может внешне сильно отличаться от <div class="card">, несмотря на одинаковое имя тега. Концептуально это можно воспринимать как классическое наследование (то есть класс Card расширяет Div) — наследование семантики и базовых стилей div с созданием стиля для класса Card с возможностью его многократного использования.

С 1997 года мы пережили более двадцати лет инноваций в вебе. Теперь существует множество способов структурирования CSS.

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

Масштабирование селекторов Class


Если продолжить рассуждать о свойстве class как об аналоге классов ООП, то можно отметить, что классы редко не получают параметров и не имеют состояния. Ценность классов в C заключается в том, что они имеют «режимы» благодаря параметрам, и мы можем менять их состояние при помощи методов. CSS имеет псевдоселекторы, описывающие ограниченные части состояния, например, :hover, но для описания индивидуального состояния или модальности внутри класса нужно использовать ещё больше классов. Проблема в том, что class получает только список строк…

Вернёмся к нашему примеру с Card. Допустим, нам нужно параметризировать Card так, чтобы он получал опцию size, имеющую одно из значений Big/Medium/Small, булеву опцию rounded и опцию align со значением Left/Right/Center. Пусть также наш Card может загружаться «лениво», так что нам нужно описать состояние Loading и Loaded. В нашем распоряжении имеется множество опций, но каждая из них обладает ограничениями:

  • Мы можем представить их в виде дополнительных классов, например, <div class="Card big">. Проблема такого подхода заключается в том, что ему не хватает пространств имён; могут появиться какие-то другие CSS и вобрать в себя значение big для своего компонента, что может привести к конфликту. Обойти эту проблему можно при помощи комбинирования селекторов в CSS: .Card.big {}, но это может поднять вопросы специфичности, что в дальнейшем способно привести к проблемам.
  • Мы можем представить их в виде отдельных «конкретных» классов, например, <div class="BigCard">. Проблема такого подхода в том, что потенциально мы создадим множество дублирующихся CSS, так как BigCard и SmallCard с большой вероятностью будут иметь много общего CSS. Такой подход создаёт и проблемы масштабируемости, приводя к проблеме комбинаторного взрыва; для одной лишь опции size нам нужно создать три класса, но если добавить rounded, то их уже нужно шесть, а если ввести ещё и align, то придётся создавать 18 классов.
  • Мы можем создать пространства имён для параметров классов, например, <div class="Card Card--big">. Это помогает смягчить конфликты и избежать проблемы комбинаторного взрыва, но такая система будет слишком многословной, с большим объёмом дублирующейся типизации; к тому же она страдает от ещё одной проблемы, связанной с неправильным использованием: что произойдёт, если я использую класс Card--big без Card?

Современный CSS может решить некоторые из этих проблем, например, функции псевдоклассов :is() и :where() могут манипулировать со специфичностью селектора (.Card:is(.big) имеет равную специфичность с .Card). Также при разработке таких систем можно использовать языки наподобие SASS, способным снизить неудобства дублирования благодаря вложенности и примесям. Это повышает удобство для разработчиков, но не решает фундаментальные проблемы.

А ещё у нас есть множество проблем, которые классы не могут решить по своей природе:

  • При работе с классами переходных состояний наподобие loading и loaded код может произвольно применять эти классы к элементу, даже когда элемент на самом деле не загружается. Противодействовать этому можно путём дисциплины разработки (плохо масштабируется на большие команды) или инструментарием (сложно поддерживать).
  • При работе со взаимоисключающими классами наподобие Big и Small элементы могут применять оба класса одновременно, и никакие системы именования классов не помогут в решении этой проблемы, если только вы не будете решать их целенаправленно добавлением инструментария или кода (например, .Card.big.small { border: 10px solid red }).

Кроме того, существует «кустарная промышленность» псевдоспецификаций CSS, пытающихся решить эти проблемы, но какого-то полного решения не существует:

▍ BEM — это не решение


BEM, или Block Element Modifier, обеспечивает достаточно надёжное и масштабируемое решение задачи параметризации классов. Он использует пространства имён, позволяющие предотвращать проблемы многократного использования, но ценой многословности. У него есть жёсткие правила именования, что немного упрощает анализ кода.

.Card { }
.Card--size-big { width: 100%; }
.Card--size-small { width: 25%; }
.Card--rounded { border-radius: 6px }
.Card--align-left { text-align: left }
.Card--align-right { text-align: right }
.Card--align-center { text-align: center }

.Card__Title { /* Подкомпоненты! */ }

BEM даёт нам небольшую степень согласованности, но не решает двух фундаментальных проблем классов (контроль за инвариантностью). Я могу применить class="Card--size-big Card--size-small" к одному элементу, а предоставляемый BEM фреймворк не в силах этому помешать. Аналогично, в BEM нет понятия защищённых свойств, так что я должен положиться на то, что вы не будете добавлять к элементу .Card--is-loading. Эти проблемы легче выявить благодаря фреймворку именования, но по уровню удобства они находятся на одном уровне с префиксами _ методов JavaScript. Всё работает, если вы соблюдаете правила, но вас никто не заставит это делать.

Ещё одна большая проблема BEM заключается в том, что для описания динамического состояния через JS требуется огромный объём бойлерплейта:

/* Это наименьшее, что я придумал без добавления вспомогательных функций */
function changeCardSize(card, newSize: 'big' | 'small' | 'medium') {
  card.classList.toggle('.Card--size-big', newSize === 'big')
  card.classList.toggle('.Card--size-medium', newSize === 'medium')
  card.classList.toggle('.Card--size-small', newSize === 'small')
}

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

▍ Атомарный CSS — это не решение


Атомарный CSS, или «utility-классы» отходят от концепции ООП описания компонентов систем дизайна наподобие Card, вместо этого используя классы как абстракцию от свойств CSS. Это хорошо подходит большинству систем дизайна, которые, по сути, являются подмножеством самого CSS (например, CSS позволяет использовать практически неограниченное количество цветов, в то время как в вашей палитре бренда может быть меньше сотни цветов). Наиболее примечательной реализацией атомарного CSS, вероятно, является популярная библиотека Tailwind, но это может выглядеть примерно так:

.w-big { width: 100% }
.w-small { width: 25% }
.h-big { height: 100% }
.al-l { text-align: left }
.al-r { text-align: right }
.br-r { border-radius: 6px }
/* и так далее... */

При этом, повторюсь: атомарный CSS не решает двух главных проблем классов. Я всё равно могу применить к своему элементу class="w-big w-small", и по-прежнему отсутствует возможность использования защищённых классов.

Кроме того, атомарный CSS обычно приводит к хаосу в разметке. Чтобы снизить многословность, в таких системах обычно предпочитают краткие имена классов из нескольких символов наподобие br вместо border-radius. Чтобы описать наш пример с Card в этой системе, требуется китайская грамота из непостижимых имён классов, а ведь это тривиальный пример:

<!-- Big Card -->
<div class="w-big h-big al-l br-r"></div>

Кроме того, при использовании атомарного CSS теряются многие преимущества CSS. Атомарный CSS заставляет всех изучать документацию; опытным дизайнерам с большим опытом в написании CSS приходится обращаться к таблице поиска: «мне нужно сделать flex-shrink: 0, это будет flex-shrink-0 или shrink-0»? Все utilities в общем случае представляют собой одно имя класса, то есть мы теряем все преимущества специфичности; если же мы добавляем специфичность при помощи смешения методологий или использования media query или встроенных стилей, то всё становится ещё хуже и начинает разваливаться на части. Обычно проблему специфичности решают добавлением ещё большей специфичности; в Primer CSS GitHub эту проблему обходят добавлением !important к каждому utility-классу, что в дальнейшем создаёт новые проблемы.

Если уж мы коснулись темы media query, то скажу, что считаю самой большой проблемой атомарного CSS то, что он оставляет адаптивный дизайн (responsive design) открытым к интерпретациям. Многие реализации предоставляют классы, которые применяются только в контрольной точке адаптивности, что лишь ещё больше загрязняет разметку и подвержено проблеме комбинаторного взрыва. Вот фрагмент со всего лишь двумя width в двух контрольных точках, определённый в CSS Tailwind:

.w-96 { width: 24rem }
.w-80 { width: 20rem }

@media (min-width: 640px) {
  .sm\:w-96 { width: 24rem; }
  .sm\:w-80 { width: 20rem; }
}
@media (min-width: 768px) {
  .md\:w-96 { width: 24rem; }
  .md\:w-80 { width: 20rem; }
}

<!-- Big Card на больших экранах, Small Card на маленьких экранах -->
<div class="w-96 sm:w-80 al-l br-r"></div>

На первый взгляд система utility-классов кажется благословением для системы дизайна, но если применить её в разметке, становятся заметны проблемы. Невозможность удобного представления компонентов в разметке заставляет систему дизайна искать другие решения, например, добавление разметки с прикреплёнными именами классов для описания компонента, что обычно приводит к тому, что система дизайна реализует компоненты во множестве фреймворков.

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

▍ CSS-модули — это не решение


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

.card { /* "Базовый" компонент */ }
.big { width: 100% }
.small { width: 25% }
/* ... и так далее ... */

после преобразования на этапе сборки превращается в

.card_166056 { /* ... */ }
.card_big_166056 { width: 100% }
.card_small_166056 { width: 25% }
/* ... и так далее ... */

Похоже, что это решает проблемы с BEM, потому что теперь не нужно везде прописывать пространства имён! Но вместо этого нужен инструментарий, который необходимо разработать и поддерживать во всём стеке, описывающем UI. Для этого нужно, чтобы фреймворк шаблонизации, среда исполнения JS (если она отличается) и компилятор CSS понимали и использовали одну систему CSS-модулей, что создаёт множество зависимостей в кодовой базе. Если вы работаете в большой организации с кучей обслуживаемых веб-сайтов, которые, вероятно, написаны на разных языках, то вам нужно разработать и поддерживать инструментарий для всего этого. Вашей команде разработки системы дизайна придётся заняться оснасткой этого инструментария (или переложить эту ношу на другие команды разработчиков).

Но у нас всё равно остаются две фундаментальные проблемы! Повторюсь, что проблема class="big small" по-прежнему не решена. Можно реализовать как бы защищённые классы, если добавить в кодовую базу ещё больше инструментария, чтобы гарантировать, что только один компонент использует один файл CSS-модуля, но это решение обладает всеми недостатками крупной технологии: нужна ещё куча инструментария.

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

▍ Проблема всех этих решений


Основная проблема всех этих решений заключается в том, что они ставят в центр свойство class как единственный способ описания состояния объекта. Будучи списком произвольных строк, классы не имеют ключей и значений, приватных состояний, сложных типов (это ещё и означает очень ограниченную поддержку IDE). И чтобы сделать их хотя быть чуть более удобными, приходится пользоваться специальными DSL наподобие BEM. Мы постоянно пытаемся реализовать параметры в виде Set<string>, хотя на самом деле нам нужно Map<string, T>.

Решение всех этих проблем


Я утверждаю, что современная веб-разработка предоставляет нам все средства, чтобы отказаться от имён классов и реализовать нечто гораздо более надёжное; для этого достаточно внести довольно простые изменения:

▍ Атрибуты


Атрибуты позволяют нам параметризировать компонент при помощи описания «ключ-значение», что очень похоже на Map<string, T>. У браузеров есть куча функций селекторов для парсинга значений атрибута. Если взять наш пример с Card, весь CSS можно выразить так:

.Card { /* ... */ }
.Card[data-size=big] { width: 100%; }
.Card[data-size=medium] { width: 50%; }
.Card[data-size=small] { width: 25%; }

.Card[data-align=left] { text-align: left; }
.Card[data-align=right] { text-align: right; }
.Card[data-align=center] { text-align: center; }

HTML-атрибуты можно выразить только один раз, то есть <div data-size="big" data-size="small"> будет соответствовать только data-size=big. Это решает проблему инвариантов, на что неспособны другие решения.

Это может показаться похожим на BEM и обладает многими его преимуществами. При создании CSS они определённо похожи, но моё предложение демонстрирует свои достоинства, когда дело доходит до создания HTML — ведь тогда становится гораздо проще дискретно различать каждое из состояний:

<div class="Card" data-size="big" data-align="center"></div>

Кроме того, становится гораздо проще сделать значения динамическими при помощи JS:

function changeCardSize(card, newSize: 'big' | 'small' | 'medium') {
  card.setAttribute('data-size', newSize)
}

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

.Card[my-align=left] { text-align: left; }

Это даёт и другие осязаемые преимущества. Селекторы атрибутов наподобие [attr~"val"] позволяют работать со значением так, как будто оно является списком. Это может быть полезным, если вам нужна гибкость в стилизации частей компонента, например, применение стиля к одной или нескольким сторонам границы:

.Card { border: 10px solid var(--brand-color) }
.Card[data-border-collapse~="top"] { border-top: 0 }
.Card[data-border-collapse~="right"] { border-right: 0 }
.Card[data-border-collapse~="bottom"] { border-bottom: 0 }
.Card[data-border-collapse~="left"] { border-left: 0 }

<div class="card" data-border-collapse="left right"></div>

Готовая к выпуску спецификация CSS Values 5 также позволяет атрибутам проникать в свойства CSS, подобно переменным CSS. В системах дизайна применяются различные уровни размеров, абстрагирующие значения в пикселях (например, pad-size может иметь значение от 1 до 6, где каждое число обозначает величину от 3px до 18px):

<div class="card" pad-size="2"></div>

.Card {
  /* Берём атрибут `pad-size` и приводим его к значению `px`. */
  /* Если оно отсутствует, откатываемся к 1px */
  --padding-size: attr(pad-size px, 1px)
  /* Делаем размер padding кратным 3px */
  --padding-px: calc(var(--padding-size) * 3px);
  padding: var(--padding-px);
}

Разумеется, при достаточном объёме типизации эту проблему можно решить уже сегодня, по крайней мере, для ограниченных значений (которые выражают большинство систем дизайна):

.Card {
  --padding-size: 1;
  --padding-px: calc(var(--padding-size) * 3px)
  padding: var(--padding-px);
}
.Card[pad-size=2] { --padding-size: 2 }
.Card[pad-size=3] { --padding-size: 3 }
.Card[pad-size=4] { --padding-size: 4 }
.Card[pad-size=5] { --padding-size: 5 }
.Card[pad-size=6] { --padding-size: 6 }

Согласен, это достаточно большой объём бойлерплейта, но как временное решение подойдёт.

▍ Собственные имена тегов


Если вы дочитали до этого места, то, вероятно, уже кричите в монитор: «Автор, ты клоун, ты ведь по-прежнему использует имена классов! .Card — это класс!» Ну, тут всё просто. HTML5 позволяет использовать собственные теги, любой тег, не распознанный парсером — это неизвестный элемент, который можно свободно стилизовать как угодно. Неизвестные теги не имеют стандартной стилизации user-agent: по умолчанию они ведут себя как <span>. Это полезно, потому что мы можем выразить компонент при помощи литерального имени тега вместо class:

<my-card data-size="big"></my-card>

my-card { /* ... */ }
my-card[data-size="big"] { width: 100% }

Эти элементы представляют собой абсолютно валидный синтаксис HTML5 и не требуют никаких дополнительных определений, никакого специального DTD или метатега, никакого JavaScript. Как и в случае с атрибутами, хорошей идеей будет добавление -, что соответствует спецификации и предотвращает случайное переписывание. Кроме того, использование - также позволит использовать ещё более мощные инструменты наподобие Custom Element Definitions, что обеспечивает возможность интерактивности на JavaScript. С Custom Elements можно использовать собственные состояния CSS, благодаря чему мы переходим на новый уровень возможностей:

▍ Custom State (собственные псевдоселекторы)


Если у ваших компонентов есть любой уровень интерактивности, то им может потребоваться изменение стиля из-за какого-нибудь изменения состояния. Возможно, вам знакомы элементы input[type=checkbox], имеющие псевдокласс :checked, позволяющий CSS связываться с их внутренним состоянием. В случае нашего примера с Card мы хотели добавить состояние loading, чтобы можно было декорировать его в CSS; дополнить его анимированными спиннерами, в то время как полностью загруженная card может отображаться с зелёной рамкой. Добавив немного JavaScript, можно определить тег как Custom Elements, взять объект внутреннего состояния и манипулировать им для представления этого как собственных псевдоселекторов для собственного тега:

customElements.define('my-card', class extends HTMLElement {
  #internal = this.attachInternals()
  
  async connectedCallback() {
    this.#internal.states.add('loading')

	await fetchData()
	
	this.#internal.states.delete('loading')
    this.#internal.states.add('loaded')
  }
})

my-card:state(loading) { background: url(./spinner.svg) }
my-card:state(loaded) { border: 2px solid green }

Custom states могут быть очень мощными, потому что они позволяют элементу представить себя как модальность с определёнными условиями без изменения его разметки, то есть элемент может сохранить полный контроль за своими состояниями, и их нельзя будет контролировать снаружи (если только элемент не разрешит это). Можно даже назвать это внутренним состоянием. Они поддерживаются всеми современными браузерами, а для старых или необычных браузеров создан полифил (хотя он имеет некоторые тонкости).

Заключение


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

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (10)


  1. TerrorDroid
    19.07.2024 13:27
    +6

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


  1. infectedtrauma
    19.07.2024 13:27
    +1

    Правильно, навалим еще на клиента нагрузку, чтобы точно тупило :D
    Зачем вот это нужно, если на бэкенде можно организовать любой какой тебе нужно промежуточный сервис, который из твоей придуманной восхитительной (заменим часть название на кей-значение, ага, стало сразу круче) системы стилей сгенерирует то что тебе нужно? Эта задача решается дополнительным слоем абстракции, причем только на билде
    P.S> комментарий про автора-клоуна - самый полезный.


    1. artptr86
      19.07.2024 13:27

      Какую же вы видите в данном случае дополнительную нагрузку на клиенте?


  1. gmtd
    19.07.2024 13:27
    +9

    При этом, повторюсь: атомарный CSS не решает двух главных проблем классов. Я всё равно могу применить к своему элементу class="w-big w-small", и по-прежнему отсутствует возможность использования защищённых классов.

    Галиматья какая-то...

    А топором по ноге своей автору кто мешает ударить? Никто? Нехай рубит.


  1. artptr86
    19.07.2024 13:27

    К сожалению, до сих пор custom elements требуют регистрации в глобальном реестре, а это значит, что в большом приложении может возникнуть коллизия имён элементов.


  1. ImagineTables
    19.07.2024 13:27
    +3

    По-моему, это очередное столкновение сторонников и противников компонентности в UI. Эта борьба, раз уж мы вспоминаем историю, пожалуй, ещё древнее, чем CSS и классы. Когда-то одни писали приложения в терминах представлений документов. Это требовало бОльших усилий для создания «фастфуда», но давало больше свободы и, в конечном итоге, оправдывало себя по мере роста сложности и юзерских хотелок. Другие наслаждались тем, что можно кинуть компонент(ы) на форму и получить сразу почти готовое приложение, но по мере роста сложности приложения упирались в ограничения, диктуемые архитектурой компонентов. Как в анекдоте: «как ни собирали детали, а всё выходит пулемёт». Я своими глазами видел, как аргумент «используемые нами компоненты это не поддерживают» возникал при обсуждении требований со стороны дизайнеров. Конечно же, я стал убеждённым противником сборки UI из компонентов.

    А вот автор явно хочет вернуться к компонентности. Он 13 раз использует слово «компонент» (я подсчитал, не поленился). В данном случае, ему нужен компонент карточки:

    Вернёмся к нашему примеру с Card. Допустим, нам нужно параметризировать Card так, чтобы он получал опцию size, имеющую одно из значений Big/Medium/Small, булеву опцию rounded и опцию align со значением Left/Right/Center. Пусть также наш Card может загружаться «лениво», так что нам нужно описать состояние Loading и Loaded.

    Но к счастью для тех, кто разделяет мой подход, современный HTML не про компоненты, современный HTML про стилизации. И в этом его сила. Например, rounded, который автор хочет видеть булевским свойством компонента карточки — на самом деле универсальная стилизация, равно подходящая и к карточке, и к кнопке, и к даже к некоторым из объектов UI, которые сегодня ещё никто не изобрёл. Нет никакого смысла ограничивать карточки (как объект UI) при создании приложения тем жалким набором свойств, которые предусмотрел автор карточного компонента. Так теряется универсальность. Гораздо лучше составить словарь стилизаций и уже на этом языке описывать UI.

    Конечно, от составителя этого словаря требуется ДУМОТЬ, чего многие не любят и не умеют. И часто получается TailWind дикая смесь всех стилей и направлений. Но это не значит, что плох сам подход.


  1. gybson_63
    19.07.2024 13:27
    +1

    Вы почти XML/XSLT придумали


  1. ILaeeeee
    19.07.2024 13:27

    Мне не нравится делать лишние движения, поэтому управление визуальным стилем только в CSS. Бегать туда и сюда, между HTML и CSS - нафиг этот геморой. Поэтому я отдельно посылаю лучи поноса тем верстальщикам, которые привязывают стилизацию к тегу. А потом приходят какие-нибудь SEOшники и говорят типа того: "нам тут тег A не нужен, давай H2 на H3 меняй" и аналогичное. Вот и бегаешь туда сюда, меняя и HTML и CSS.

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


  1. gun_dose
    19.07.2024 13:27
    +1

    У меня есть альтернативное решение проблемы class="big small". А что, если фронтендеры будут сперва немного думать, прежде чем совать все подряд классы в элемент?


  1. Sadler
    19.07.2024 13:27

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