Comments 113
Интересно как это отразится на производительности всего этого добра, ибо может оказаться что наличие этих атрибутов и кастомные псевдоселекторы в итоге вызовую резкий рост нагрузки или ещё чего-то, ибо никто не предусматривал, что их будут настолько агрессивно использовать. Было бы классно увидеть тесты какие-то, сверстав единтичные макеты разными способами.
Явно потери будут. Вопрос на сколько сильны. Из опыта использования переменных в css думаю 99% современных пользователей и не заметят разницы. Также вопрос производительности отнимает много ресурсов, т. е. денег. Если заказчик готов тратить на это ресурсы, то это одно дело, если нет, то оно надо париться за 0.5 сек первичной отрисовки). Как по мне лучше выбрать более комфортные механизмы разработки. Классы, по 1000 раз переопределяющие свойства элемента, тоже могут снижать производительность
а как это может затронуть производительность? движки html и css явно же не идиоты пишут
Правильно, навалим еще на клиента нагрузку, чтобы точно тупило :D
Зачем вот это нужно, если на бэкенде можно организовать любой какой тебе нужно промежуточный сервис, который из твоей придуманной восхитительной (заменим часть название на кей-значение, ага, стало сразу круче) системы стилей сгенерирует то что тебе нужно? Эта задача решается дополнительным слоем абстракции, причем только на билде
P.S> комментарий про автора-клоуна - самый полезный.
При этом, повторюсь: атомарный CSS не решает двух главных проблем классов. Я всё равно могу применить к своему элементу
class="w-big w-small"
, и по-прежнему отсутствует возможность использования защищённых классов.
Галиматья какая-то...
А топором по ноге своей автору кто мешает ударить? Никто? Нехай рубит.
И аналогично с подходом автора <div data-size="small" data-size="big">
можно написать
Автор пишет, что первый атрибут затирается вторым, а вот в случае class="first second"
— применятся оба.
В целом подход автора — весьма интересен. Я всë ждал когда же он уже напишет про CSSinJS, но закончилось всë неожиданно новой фичей и как-бы не CSSinJS, но
— не без JS.
Это не повод прямо сейчас бросить React, но приятно, что отступные пути есть и всë новые появляются — как часть стандарта.
Затрется, а если затрется как раз правильный атрибут?
А зачем бросать реакт, если он немного про другое, чем просто стилизация?
а вот в случае
class="first second"
— применятся оба.
В случае атомарных фреймворков типа tailwind в конструкции class="w-big w-small" будет применен именно последний. Ибо переопределение свойств и есть часть CSS
Будет применён тот, что ниже по списку в итоговом css. Или там как-то хитро сделано и такие случаи учитываются?
На уровне "компиляции" tailwind применит только последний в списке
Тейлвинд же не компилирует шаблоны. Максимум purgecss уберёт неиспользуемые классы. И действительно свойства нижележащего в css класса затирают предыдущие, несмотря на порядок их перечисления в атрибуте `class`.
Я про сами шаблоны ничего не говорил, он прочитает все указанные в разделе content конфига и будет строить цепочки по тем классам, которые там указаны + входящй css c apply.
Неверно. Будет применен тот класс, который прописан последним в CSS файле. Порядок указания классов в атрибуте `class` ни на что влияет на это.
Еще раз: в случае tailwind и его "атомарных" классов "компилятор" в результирующий css сам сгенерит такую последовательность, чтобы именно последний, указанный (из взаимно-исключающих атомов) в верстке, применился.
Если, конечно, вы будете "компилировать" шаблоны и директивы, а не выкатите полный набор tw как есть
После компиляции tail становится худым донельзя. И это мы еще директивы apply не обсудили
А про отсутствием головной боли с условными тегам типа dark:darkclass. Или вовсе можно что то свое сделать в роде isWindows:bg-blue-100
В одном элементе я пропишу `class="w-big w-small"`, в другом `class="w-small w-big"`. Что мне tailwind в этом случае сгенерирует?
проверьте, даже интересно, я в целом могу предпололжить как сделал бы я на месте tw, но что будет у них, я хз.
Подозреваю что сгенерится два класса
.w-big.w-small {}
.w-small.w-big {}
Понятно, что в обычном варианте они будут взаимоисключающие. Но я бы еще взял вышестоящие элементы и попробовал наследоваться от них если они разные
https://play.tailwindcss.com/rc8Bihs9Lx
![](https://habrastorage.org/getpro/habr/upload_files/d66/d82/53a/d66d8253ab0241b94ab9cf4e34bf0b0f.png)
В обоих случаях применился height: 5rem
, потому что .h-20
описан ниже, чем .h-10
, что вам тут неоднократно говорили. Tailwind чудес не делает, он тупо генерирует набор классов как это сделал бы, например, цикл @for
в SASS, а затем сторонняя тулза PurgeCSS вычищает неиспользуемые классы. А браузер уже применяет атрибуты в соответствии с порядком появления их в CSS-файлах.
Значит он тупее, чем я думал, либо через кастом классы с apply оно работает не так (в apply взаимоисключающие свойства перекрываются последним для ЭТОГО блока, что логично)
На их месте, очевидно, что в двух данных блоках есть различия в bg-* и можно было бы сгенерить цепочки, которые работали бы правильнее
.bg-red-200.h-20.h-10 {
--tw-bg-opacity: 1;
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
height: 2.5rem;
}
.bg-green-200.h-10.h-20 {
--tw-bg-opacity: 1;
background-color: rgb(187 247 208 / var(--tw-bg-opacity));
height: 5rem;
}
В apply атрибуты применяются, очевидно, по правилам тейлвинда, а в атрибутах HTML — по спецификациям HTML и CSS.
можно было бы сгенерить цепочки
Тейлвинд сам не анализирует ни используемые классы, ни тем более списки. Более того, в общем случае сгенерировать такие цепочки невозможно. Здесь нам «повезло», что есть разные классы, по которым можно различить эти элементы и построить специфичные селекторы.
В таком случае ваша схема не сработает:
<div class="bg-red-200 border border-black h-20 h-10">123</div>
<div class="bg-red-200 border border-black h-10 h-20">456</div>
Это потому, что в селекторах никакого различия в порядке классов нет, и такие селекторы будут означать одно и то же:
.bg-red-200.border.border-black.h-20.h-10 {}
.bg-red-200.border.border-black.h-20.h-10 {}
Ну не знаю. Я помню ещк в 17 году что-то подобное пробовал сделать через атрибуты на небольшом проекте. Результат мне не очень понравился. Визуально разметка загрязняется. А если говорить в контексте фреймворка все ещё становится гораздо хуже. Например с vue кассетные элементы это компоненты фреймворка, дальше всякие пропсы, и директивы, и будет в итоге раздутые теги с 10+ атрибутами. Ну и аргумент с переопределентем я все жк не понял. Нафига писать 2 класса которве будут переопределять одно и тоже свойство, и разницу между подходом автора тоже. В случае классом применится тот у кого будет выше специфичность, а с случае с атрибутом по ввти тоже самое только тот который задан первым? В общем даже не знаю, пока за 7 лет использования БЕМ, альтернативы так и не нашёл. Разве что комбинация scoped/ccsmodules+BEM+tailwind(через @aply) мне зашла. Кстати Antony Fu вроде в своих проектах продвигает UnoCss по сути аналог tw, только как раз построенная на атрибутах.
ВНЕЗАПНО в CSS-файле второе по ходу файла правило тоже перетрёт первое (именно поэтому он и каскадный), но при этом если в первом классе есть какие-то правила которые хочется использовать, никто не мешает их не переписывать во втором. Поэтому если очень уж хочется обязательно применить два класса (не очень понимаю кому в здравом уме это надо) можно сделать и "w-big w-small", просто надо учесть это в правилах.
То что автор не врубился как пользоваться стилями не значит что надо городить в стандарт какое-то шизовое дополнительное говно, которое он в своей писанине предлагает.
Не совсем так. В случае атрибутов правила будут целиком перезатёрты, а в случае классов переопределяются только повторы:
Пример:
.red { color: red; }
.big { font-size: 2em; }
[data-lol=red] { color: red; }
[data-lol=big] { font-size: 2em; }
<div class="red">class="red"</div>
<div class="big">class="big"</div>
<div class="red big">class="red big"</div>
<div class="big red">class="big red"</div>
<div data-lol="red">data-lol="red"</div>
<div data-lol="big">data-lol="big"</div>
<div data-lol="red big">data-lol="red big"</div>
<div data-lol="big red">data-lol="big red"</div>
<div data-lol="big" data-lol="red">data-lol="big" data-lol="red"</div>
<div data-lol="red" data-lol="big">data-lol="red" data-lol="big"</div>
![](https://habrastorage.org/getpro/habr/upload_files/450/3a8/532/4503a8532be30a40c1c3296f7ce979a2.png)
В примере могут применяться либо красный цвет либо х2 шрифт. Свойства классов применяются оба вне зависимости от порядка следование имён классов в атрибуте class, а вот с селектором по атрибуту применяются стили из одного блока – либо двойной шрифт либо красный цвет.
Притом следует обратить внимание, что используется первый атрибут. Последующие атрибуты с одинаковым именем игнорируются, хотя интуитивно кажется, что – должен затирать предыдущие, идущий последним – как в каскаде.
Зачем – это может понадобиться IRL – загадка, но приятно было узнать новые мелочи. Как минимум IDE подскажет, что вы используете один атрибут дважды, чего она не сделает с именами классов.
Да, но применится только один - data-size="small"
К сожалению, до сих пор custom elements требуют регистрации в глобальном реестре, а это значит, что в большом приложении может возникнуть коллизия имён элементов.
По-моему, это очередное столкновение сторонников и противников компонентности в 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 дикая смесь всех стилей и направлений. Но это не значит, что плох сам подход.
Конечно же, я стал убеждённым противником сборки UI из компонентов.
А как вы следите за консистентностью и соблюдаете DRY — если не используете в том или ином виде компоненты? Вы пишите один раз и больше не занимаетесь поддержкой? Это лендосы или PWA?
Цвет автомобиля может быть любым, при условии, что он черный © Генри Форд
Это я к тому, что либо вы выпускаете штучный товар за дорого с кастомизацией, либо живëте с ограничениями и делаете много, но не так дорого за штуку.
Мне кажется, есть смысл отдельно рассматривать DRY на двух разных уровнях — 1) на уровне проекта и 2) между проектами.
Начнём с уровня проекта. Вот эта вот фраза: «соблюдаете DRY — если не используете в том или ином виде компоненты» намекает, что любую технику избегания копипасты на проекте можно объявить компонентом — не в том виде, так в ином. Но я так не считаю. Компонент это изолированный кусок интерфейса. Вся фишка в этой изолированности. Автор статьи, например, хочет поведение «скруглённый» зафиксировать и сделать свойством карточки. Чем больше карточка будет накапливать разных поведений, инкапсулированных как свойства, тем больше она будет похожа на компонент. Но карточки можно описывать шаблонами, а стилизовать — той самой привязкой стилей к классам, против которой направлена статья. Это и есть некомпонентный способ поддерживать DRY.
Популярная техника — писать в разметке что-то типа (я специально взял очень простой пример, но она масштабируется):
<template class="link">
<a href="{url}" target="_blank" class="text-nowrap {classes}">
{text}
<svg width="16" height="16" class="inline"><use href="#bi-box-arrow-up-right"></use></svg>
</a>
</template>
…
<span data-template="template.link" data-template-params='{ "text": "Habr", "url": "https://en.wikipedia.org/wiki/Habr" }'></span>
Это не компонент, потому что его внешний вид будет сильно зависеть от того, что из стилей вы позже привяжете к шаблону при помощи классов. А его поведение будет сильно зависеть от того, что из обработчиков вы позже привяжете к шаблону при помощи, опять же, классов.
Теперь об отсутствии DRY на межпроектном уровне. Это уже, скорее, философский вопрос — по крайней мере, тут на примерах кода не обсудить.
Начнём с того, что прогресс в HTML идёт так быстро, что привязываться к компонентам зачастую просто неоправданно. Во-первых, то, что вчера требовало прорвы скриптов, завтра становится можно описать одним CSS-свойством. Например, после того, как появилась тригонометрия, кучу 3D-компонентов я переписал со скриптов на простой CSS. Во-вторых, меняется подход, иногда радикально. Сколько на вашей памяти сменилось моделей лэйаутинга? Я вот ещё таблицы застал. Поэтому, не надо привязываться к легаси, окаменевшей в форме компонентов. Надо просто отпустить легаси, как ту сахарницу.
А закончим тем, что компонентный подход… как бы это назвать… самоподдерживающийся. Если начинать проектировать с компонентов, становится непонятно, как не нарушать DRY без компонентов. Но можно ведь и не начинать. Грубо говоря, Microsoft мог оформить представление вордовского документа в виде VRTC (VERY Rich Text Control). Но они почему-то пошли по пути технологии OLE Automation. Тогда в MS вообще хорошие архитекторы работали.
Я похоже не очень понял, что вы написали. Возможно потому, что пишу на React и под компонентами подразумеваю React-компонены или styled-компоненты.
В целом компоненты – это удобная абстракция знакомая мне по React, но в целом может полагаться на разные способы.
Неважно, что написал я. Важно, что написал автор. Процитирую его ещё раз:
Вернёмся к нашему примеру с Card. Допустим, нам нужно параметризировать Card так, чтобы он получал опцию size, имеющую одно из значений Big/Medium/Small, булеву опцию rounded и опцию align со значением Left/Right/Center. Пусть также наш Card может загружаться «лениво», так что нам нужно описать состояние Loading и Loaded.
Человек спроектировал вот такой вот компонент. С булевым свойством rounded
. Потом вы показываете клиенту приложение на его основе, а клиент говорит: сделайте скругление поменьше, пожалуйста. Если бы речь шла о компоненте Java.FX, VCL (это Delphi/Builder) или ActiveX (.ocx), вы бы ответили клиенту: извините, нельзя. Но поскольку HTML это некомпонентная платформа, вы просто жамкаете F12, находите в коде, что карточка представлена экземплярами div.card
и прикручиваете к нему через этот класс уменьшенный border-radius
с повышенным приоритетом. Но именно это и не нравится автору статьи. Он считает такую возможность вредной (см. заголовок). Он хочет, чтобы было как в Java.FX, VCL и ActiveX, когда в таких ситуациях больше ничего нельзя сделать.
А если не очень понятно про описанную мной технику, я дополню. Но вообще, вы просто вставляете шаблон, а в других местах ссылаетесь на него с параметрами.
Проблема не в компонентности. Не знаю как там javafx, но в vcl возможности контролов зависят от платформы. Т.е. не очень корректное сравнение с HTML. Однако, есть другой пример - fmx, там те же компоненты, только вот их вид зависит только от разработчика. Хочешь скругление - делаешь как в css, берешь и указываешь скругление. Компонент сам по себе не имеет этой возможности, а вот его представление не ограничено ни чем. В qt, кстати, примерно так же и даже ближе к HTML.
не очень корректное сравнение с HTML
Что же некорректного в этом сравнении? В Delphi/Builder'е, насколько я в курсе, нельзя в Object Inspector'е добавить своё свойство, в то время как в DevTools'е можно. Поэтому VCL компонентный, а HTML нет. И хорошо, что это так, иначе я бы вместо него использовал что-то другое.
В qt, кстати, примерно так же и даже ближе к HTML.
У нас, юайщиков, есть такая шутка. Платформы делятся на те, которые погрязли в компонентности, императивщине и отсутствии DSL'ей, и те, которые делают свой HTML, только ни с чем не совместимый и плохо документированный. (Впрочем, это переделка старой шутки про Лисп).
Честное слово, это не я только что придумал. Тут статья была, потом её стёрли, а так бы ссылку дал.
Своё свойство добавить нельзя, только это и не нужно, управлять отображением можно не только через свойства (нет, не в рантайме, а в дизайнере).
Есть подход, где есть "компонент", а есть его "представление". Компонент обладает свойствами, конкретного элемента, например, кнопка, там важен текст, размеры, положение и т.д. А вид можно задать через специальный дизайнер, где ты управляешь тем, как кнопка будет выглядеть. Ты конструируешь вид кнопки из примитивов, у которых уже есть и скругления и прочее.
Равно как HTML+CSS. HTML код - основной каркас, css - представление.
В итоге, система компонентная, а вот ограничений нет.
Понятно. Видимо, такое появилось уже после того, как я последний раз смотрел на эти RAD'ости.
Но это всё равно мало на что влияет. Дело в том, что при работе с карточками в приложении нам обычно нужно видеть много карточек. То есть, оформлять каждую карточку компонентом это очень большое упрощение, на практике понадобится компонент «картотека». И как вы будете добавлять скругление к отдельным карточкам внутри «картотеки» в этом специальном дизайнере?
Но, допустим, вы берёте компонент именно для карточки, донастраиваете его в специальном дизайнере, а для управления несколькими карточками ваше приложение создаёт много инстансов этого компонента. Далее, вам поступает требование в карточке заменить статичное фото ссылкой на редактор фотографий. В HTML-то понятно, что делать: модифицировать DOM, опираясь на классы. Для простых вещей есть before
/after
, и, я надеюсь, HTML будет развиваться в эту сторону. А что вы сделаете с готовым компонентом? Если вы начнёте править исходники, он перестанет быть компонентом. Но даже если вы согласны, исходники ещё надо раздобыть. А если их нет, начинается особая, хендловая магия ))) Я много занимался такими задачами, пришивая без исходников одним приложениям куски других компонентов по методу доктора Франкенштейна так, что авторы бы в обморок упали. Это было весело, но лучше уж писать на HTML более традиционным способом.
Зачем мне править исходники? Для твой карточки можно использовать любой компонент. Базовый. Всё равно в карточке много полей нужно показать и картинку и т.д. Такого компонента точно не будет. В нем нет смысла.
Уже давным давно всё это не так работает, как раньше в VCL. Я на VCL уже лет 5 ничего не писал. А использую кроссплатформенный фреймворк в том же Delphi. Там парадигма схожая с html. Как я уже говорил, там те же компоненты, но на любой компонент можно использовать какое угодно представление (и на любой инстанс компонента тоже).
Для твоей картотеки нужно: просто контейнер с прокруткой а внутрь вставляем карточки. Карточка может быть основана на чем угодно, хоть на пустом компоненте без каких-либо особых свойств. Каждой такой карточке мы можем применить любое представление в любой момент. Представление создается отдельно с нужным набором полей.
Вот пример
![](https://habrastorage.org/getpro/habr/upload_files/1b1/29c/2b9/1b129c2b914dd397b3c8a3801a141d9e.png)
Слева список представлений из примитивов. Мы можем его сделать из чего угодно, в том числе из компонентов со своими представлениями.
Справа список с карточками. При создании мы указываем содержимое карточки, в том числе картинку откуда хочешь. Или вообще указать карточке другое представление и она будет выглядеть иначе.
Т.к. представление из примитивов, мы можем в любой момент обратиться к представлению, либо конкретно этой карточки, либо к базовому представлению и изменить что угодно в любой момент. В том числе, изменить скругление.
И сейчас речь даже не о переделке программы, по требованию клиента, а рантайме. Я могу это сделать без пересборки программы, во время её работы.
Всё равно не понял, как решается без исходников задача «в карточке заменить статичное фото ссылкой на редактор фотографий».
Тут, возможно, дело уже в другом. Судя по «Там парадигма схожая с html», Delphi просто попадает во вторую категорию: «свой HTML, только ни с чем не совместимый».
Например,
rounded
, который автор хочет видеть булевским свойством компонента карточки — на самом деле универсальная стилизация, равно подходящая и к карточке, и к кнопке, и к даже к некоторым из объектов UI, которые сегодня ещё никто не изобрёл.
А зачем? Ну, т.е. в моем мире это или стандартные штуки типа скругления углов, и они применимы к чему угодно, либо это уже мета-параметры типа loading, про которые никто кроме меня не знает, как они должны выглядеть на карточке/кнопке/неизбретенном еще обьекте UI. Ну, т.е., loading не будет подходить к чему угодно, потому что на карточке я хочу спиннер, а на кнопке — котика. Поэтому мне все равно придется описать как будет выглядеть loading кнопки. А коль я это описываю — то мне несложно и даже удобнее не пытаться впихнуть это в стандартное “loading”(если оно существует), а придумать loading-kitty, и именно его-то и описать, и оперировать именно loading-kitty=true, или loading-dog=true.
Вы почти XML/XSLT придумали
Мне не нравится делать лишние движения, поэтому управление визуальным стилем только в CSS. Бегать туда и сюда, между HTML и CSS - нафиг этот геморой. Поэтому я отдельно посылаю лучи поноса тем верстальщикам, которые привязывают стилизацию к тегу. А потом приходят какие-нибудь SEOшники и говорят типа того: "нам тут тег A не нужен, давай H2 на H3 меняй" и аналогичное. Вот и бегаешь туда сюда, меняя и HTML и CSS.
Хотя есть и противоположный пример, когда нужно привязывать только к тегам, это места, где клиенты в админке WYSYWIG редактором что-то меняют. Нафиг никому не упало в админке ещё какие то классы выставлять. Вот просто там жирным, цитатку или ещё что-то одним кликом тыц, и визуально это все видно. А на стороне сайта это как-нибудь стилизуется.
У меня есть альтернативное решение проблемы class="big small". А что, если фронтендеры будут сперва немного думать, прежде чем совать все подряд классы в элемент?
Какправило этим грешат бэкендеры тимлиды, которые быстро делают таску чуть поковыряв палкой код в редакторе и найдя подходящие классы..
Да, конечно, бэкендеры и тимлиды дураки, потому что не понимают все те 800 слоёв абстракций, которые придумали, чтобы вёрстку гордо называть фронтендом.
Зато фронтендеры умные и городят селекторы пятого уровня вложенности, да ещё и через ">", чтобы когда глупый тимлид ради a11y обернёт радиобаттоны в филдсет, то вся форма пренепременнейше разлетится.
Блин, если читать статью внимательнее, то автор даёт объяснение, что дело не только в банальной невнимательности. Если нужно программно поменять модификатор, то нельзя просто установить ему новое значение - нужно ещё сбросить все другие возможные значения. И лишние строки кода это полбеды, главное здесь то, что нужно знать исчерпывающий список возможных значений. Но что если было три размера, а потом добавили четвертый?
Либо придётся парсить список классов, чтобы убрать лишние по какому-то префиксу.
Либо использовать фреймворк, который не будет менять дом точечно, а просто перерендерит компонент с нуля :) Но автор, очевидно, ищет более общее решение.
Я прекрасно понимаю, о чём пишет автор. Но вот эти все вещи, вроде исчерпывающего списка значений и т.д. - это всё абсолютно легко решается с помощью всё той же внимательности. А решение автора обязывает вместо классов учить все возможные имена атрибутов. Кто-нибудь всё так же ошибётся, только вместо class="small big" будет сочетание атрибутов data-text-size="small" и data-font-size="big". То есть предложенное решение вообще ничего не решает.
Мы тут вплотную подходим к тезису о том, что HTML - это не язык программирования, со всеми вытекающими, а именно с тем, что валидация всего написанного на таких языках - это головная боль пишущего.
Но вот эти все вещи, вроде исчерпывающего списка значений и т.д. - это всё абсолютно легко решается с помощью всё той же внимательности.
На длинной дистанции плохо решается.
Но что если было три размера, а потом добавили четвертый?
И случилось это спустя время, когда старый сотрудник уже уволился, и задачу передали другому.
На длинной дистанции неизбежно появятся новые атрибуты для одних и тех же вещей. В итоге всё равно придётся читать документацию, которой скорее всего не будет, и быть внимательным.
Внимательность - штука хорошая и нужная, но её недостаточно.
Одно дело, если нужно добавить новый модификатор в стили, которые вот здесь, перед глазами - внимательно разобрался и добавил. Возможно, этот код уже легаси, возможно с наслоениями говна, но важно что он известен и локализован.
Но если новый модификатор ещё неявно влияет на некий другой код, который возможно есть (но может и нет) где-то в другом месте (и одном ли?), тут придётся быть ещё и немного телепатом.
Вот автор пытается, помимо прочего, снижать вероятность таких ситуаций.
Я же привёл наглядный пример, почему подход автора абсолютно не рабочий. Почему вы упорно продолжаете это игнорировать? Или вам не хватает той самой внимательности?
Кто-нибудь всё так же ошибётся, только вместо class="small big" будет сочетание атрибутов data-text-size="small" и data-font-size="big". То есть предложенное решение вообще ничего не решает.
Это ведь тот самый пример, да?
Так я на протяжении всей ветки пытаюсь донести мысль, что это меньшая часть проблемы, потому что такая ошибка легко обнаруживается и фиксится.
А вот разница между element.classList.add('big')
и element.dataset.size = "big"
веселее, потому что такой код может находиться вообще в другом месте, а ошибки в нём могут иметь малопредсказуемые проявления (сложение стилей вместо подмены). Ключевая идея тут - уйти от сложения групп стилей (add) к их прямому присваиванию (set).
То есть кое-что всё-таки решает. Хотя и не серебряная пуля.
Это не решает опять же практически ничего, потому что 99% атрибутов задаётся в шаблонах, где программист оперирует HTML-строками, а не DOM-элементами.
То, что вы привели, хорошо работает для смены состояния уже отрисованного элемента. Но во время реализации смены состояния программист прекрасно отдаёт себе отчёт в том, что если нужно сменить цвет, то надо старый удалить, а новый добавить
А давайте сразу весь код писать без багов и проблем и так, чтобы он никогда не становился legacy. А то чего как дураки, попишут багов, потом сидят их чинят, и кому это нужно было — непонятно.
А что плохого в том, чтобы писать код без багов?
Ничего, это прекрасно, дёшево и хорошо для пользователей, только в среднем — невозможно.
Буквально парой комментариев ниже, в ветке на похожее замечание, я более подробно раскрыл, что имею в виду, если интересно, можете ознакомиться там, не буду уж тут повторяться.
А я вот тут же, чуть выше описал, почему метод, предложенный автором вообще никак не решает описанную проблему:
Кто-нибудь всё так же ошибётся, только вместо class="small big" будет сочетание атрибутов data-text-size="small" и data-font-size="big"
Только в случае классов это приведет к произвольной каше из частей двух стилей, а во втором это культурно свалится либо в small, либо в big.
В обоих случаях результат будет абсолютно одинаковый, это же селекторы одинаковой специфичности. Или вы не увидели, что там названия data-атрибутов разные? В этом же вся суть, захочет кто-то поменять размер текста, придумает свой атрибут, и никто не будет проверять весь список уже существующих, будут добавлять дубли с похожими названиями
О боже, придется читать документацию, да?
Проблема не в том, что документацию придётся читать, а в том, что её придётся писать.
А теперь вопрос: стал бы автор вообще писать эту статью, если бы наличие документации к каждому проекту подразумевалось само собой, а её знание всеми разработчиками было обязательным?
А, и у вас наверное есть решение, которое обеспечит наличие и знание документации?
Использую, в зависимости от размера проекта, и БЭМ, и CSS Modules, и кастомные атрибуты, и голый CSS без всяких методологий и обвязок (главное, чтобы это подчинялось какой-то логике, а не всё вместе). Я слишком часто переусложнял, теперь стараюсь использовать технологию минимальной сложности, достаточную для решения задачи. Потому что с кодом разбираться придётся не только мне: чем проще вкатиться другим программистам, тем лучше.
Если HTML5 может любой произвольный тег считать чем-то валидным, то Angular, например, будет пытается распарсить это тег как компонент, не сможет его найти и сломается на этапе билда.
Автор сам придумал проблему и сам героически её решает. Если недопустимо сочетать названия классов big и small для одного элемента - не сочетайте.
Если нельзя вызывать методы у null — не вызывайте, если переполнение переменной приводит к ошибке — не переполняйте, если неочистка ссылки приводит к утечке памяти — очищайте, если ваш код со временем станет тяжело поддерживаемым legacy — пишите так, чтобы не стал. И самое главное, если после реализации бизнес придёт к вам с доработками и правками - сделайте их ещё при первой интерации, зачем ждать.
Это я к чему — CSS написал один человек, в голове которого left и right несочетаемые понятия, использовал в разметке страницы другой, для которого это «левый» и «правильный» и они сочетаются, рефакторил третий, который ничего не понял и переименовал right в correct. А потом четвёртый внёс взаимоисключающие свойства на этапе редизайна, и вёрстка в хорошо спрятанных местах сползла в штаны. Все очевидно на короткой дистанции но на длинной далеко не всегда.
HTML-атрибуты можно выразить только один раз, то есть
<div data-size="big" data-size="small">
будет соответствовать толькоdata-size=big
. Это решает проблему инвариантов, на что неспособны другие решения.
То есть будет использовано первое значение?
А если прописать два класса class="big small"
, то тоже будет использовано одно значение, только объявленное последним в css. Точнее оно перебьет стили предыдущего, но результат тот же.
Те же яйца, только в профиль =)
Нет, яйца далеко не те же.
Во-первых, главная идея тут в том, что порядок атрибутов в html однозначно определяет, кто кого побеждает (что вижу, то и получаю), а порядок классов нет. Порядок стилей задаётся не в коде перед глазами, а где-то в другом месте, причем часто даже не в одном, а в нескольких разных (проведи расследование, чтобы выяснить это).
Во-вторых, стили классов не обязательно заменят друг друга, они могут сложиться.
Не согласен. Если мы говорим о модификаторах, а в статье упор как раз на этот кейс, то каждый такой селектор будет определять один и тот же набор свойств, и уж складываться они точно не будут. И определяются такие вещи как правило рядом друг с другом, а вовсе не разбросаны по проекту.
Соглашусь лишь с тем, что глядя только на разметку, действительно невозможно определить какой стиль будет применен, для этого нужно знать порядок объявления классов в css.
Модификаторы тоже не застрахованы. Потому что big/small это слишком базовый пример, а жизнь разнообразнее. Вот прямо сходу что пришло на ум:
Тултип с модификатором, определяющим положение стрелочки. Там будут комбинации свойств left/right/top/bottom. Они могут сложиться.
Многочисленные модификаторы кнопок (primary/secondary/success..., hover/disabled/active...) — там разница не ограничивается значением цвета. Там могут быть отличия в обводках, тенях, прозрачности и пр. И это тоже может сложиться. Теоретически можно прописать все свойства для всех и менять только значения переменных, но это техника со своими минусами.
Модификаторы могут зависеть от медиа-запросов, причем опять же не только по значениям, но и наличию/отсутствию свойств. Например, в светлой/темной теме кнопки часто отличаются не только цветом, но и присутствием той же теньки или обводки. Ну потому что особенности человеческого восприятия так работают. И я сомневаюсь, что все ваши медиа-запросы описаны прямо рядом.
Да, пожалуй, вы правы.
И я сомневаюсь, что все ваши медиа-запросы описаны прямо рядом.
У меня медиазапросы всегда пишутся рядом с тем классом, который они переопределяют.
Да, я тоже пишу медиа-запросы в том же модуле, к которому они относятся. Но с учетом всех модификаторов, состояний, переменных и прочего добра суммарно блок/компонент может занимать несколько экранов кода. И если какие-то переопределения разнесены на пару экранов - уже можно считать, что это не совсем рядом.
Плюс ещё бывает, что используются какие-то общие миксины, которые описаны отдельно. Ну то есть стремиться к компактности стилей конечно нужно, но это не достижимо на 100%.
Я размышлял на эту тему и тоже приходил к похожим мыслям - модификаторы в атрибутах объективно имеют некоторые преимущества. Но, конечно, выглядит всё это странно, непривычно, в прод такое тащить страшно, потому что народ не одобрит... Хотя немного экспериментировал в верстке для себя.
В целом соглашусь, что штука спорная, но здравое зерно в рассуждениях автора точно есть.
Например, можно изобрести своё собственное пространство имён для работы с параметрами CSS, что повышает читаемость
Вообще по стандарту HTML нельзя. У элемента могут быть указаны глобальные атрибуты, специфические для этого элемента атрибуты и любые data-*
атрибуты. Любые другие атрибуты с точки зрения стандарта считаются невалидными. Из-за лояльности HTML как-бы и ладно, ничего не будет, а селекторы по атрибуту и JS сработают. Но всё-таки так нельзя делать.
любой тег, не распознанный парсером — это неизвестный элемент, который можно свободно стилизовать как угодно
Не совсем так. Элемент считается неизвестным HTMLUnknownElement
, если он не является одним из стандартных или не содержит в названии как минимум одного дефиса. Если дефис есть, то парсер рассматривает элемент как потенциальный пользовательский элемент, который пока ещё не зарегистрирован. В таком случае он будет HTMLElement
.
Автор упорно пытается решить проблему с одновременным big и small, но эта какая-то надуманная высланная из пальца проблема. Если классы рендерит бэкенд или шаблонизатор на фронте, то такой ситуации не возникает. Если классы захардкожены, и по ошибке написаны оба, это просто будет заметно в браузере и сразу исправлено. Если это незаметно и применялись нужные в этом месте свойства, да и бог с ним.
Какой ужас я сейчас прочитала...Чувство, что писал джун-бэкендер...
Это чего вдруг в 1996 году многие мониторы были чёрно белыми? В то время VGA уже считался устаревшим, а в ходу был SVGA 1024*768 256 цветов
Ну насчет "многих" можно спорить, но ч/б действительно встречались. Да, считались уже устаревшими, но это не мешало им быть и использоваться.
В наших условиях это могда быть разве что ЕС-ка в каком-то совсем Богом забытом НИИ, и то сомнительно. На Западе вообще сложно себе представить, что во время 200 Мгц Pentium и Windows 95 кто-то мог работать на каком-нить IBM PC XT образца 80-х годов, да еще и использовать его для разработки новых стандартов в новейшем на тот момент направлении - интернете.
Особенно смешно, что дальше он пишет (преувеличиваю) «а вот уже в 1997 все стали цветными и выпустили новый стандарт»
Очень интересный подход! Возьму себе на вооружение. Это это выглядит более удобно, кратко и семантически корректно и с нативной производительностью.
Мне тоже уже порядком надоели всевозможные штучки типа (BEM,SASS,TAILWIND,CssInJs), которые привносят дополнительную когнитивную нагрузку и бьют по производительности. Когда нативный HTML/CSS уже научился делать все вещи для которых они создавались изначально. Пользуюсь только CssModules.
ПС: Также разрабатываю сейчас замену переусложенному React/Solid https://github.com/fusorjs/dom
Вы хотя бы не тупо переводите статьи, а делайте их редактуру/ревью. Например,
.Card:is(.big) имеет равную специфичность с .Card
Это не так.
<div class="card" data-border-collapse="left right"></div>
Мне показалось, или вы тут классы переизобрели?
Частично проблема class="big small"
решается линтерами. Да, если это прямо пользовательские классы, то не поможет, но если что-то из разряда tailwind, то IDE укажет на лишний класс.
Кажется, 70% текста из начала статьи можно было б скипнуть, если просто понять (и сказать), что классы в CSS – это не классы из ООП, а скорей классы (или множества) из математики.
Тогда и проблемы вроде конфликта small и big стали бы более понятными (и их можно было бы записать в виде ограничения small ∩ big = ∅), и его предложения было бы проще сформулировать и проанализировать.
Так как я разрабатываю чаще всего под Vue, то с такой проблемой давно уже не сталкивался. Самое приятное это делать в компоненте все аттрибуты через пропсы, условно, кнопка это <UiButton /> с классом по умолчанию <button class="btn">, а все остальные классы прокидываются в типизированные пропсы, например type="primary|stroke|white", size="sm|md|lg" и каждый тип имеет ограниченное количество вариантов которые можно прокинуть, благодаря чему решается проблема описанная в статье. (а тайпскрипт сильно в этом помогает, подчеркивая неверный тип пропса). Но способ делать это нативно через аттрибуты - интересный. Страшно, но интересно)) Страшно потому что такого ранее не видел и непонятно как это внедрить так чтобы не побили за это
Префикс data- может быть немного неуправляемым.
На этом тема клоз
Если использовать Tailwind в IDE, допустим VSCode, то там есть официальное расширение Tailwind Intellisence, которая предупреждает об использовании взаимоисключающих атомарных классах
Мое мнение по вопросу построения селекторов для UI-компонентов.
Взаимоисключающие селекторы не используются совместно по договоренности внутри команды и по здравому смыслу.
Компонент имеет уникальный для себя в рамках текущей компонентной системы класс, желательно с префиксом, например, ".ui-": .ui-button, .ui-card, .ui-slider, .ui-popup. Префикс ui-системы позволяет однозначно понять, что увиденный в коде класс относится к ui-системе, а не является проектным, созданным для использования в конкретном проекте.
Различные вариации компонента могут быть выражены классами или data-атрибутами. Выбираем то, что удобно команде и лучше подходит для конкретного случая. Опять же желателен короткий префикс (по сути namespace). Например, .ui-xl, .ui-sm и т.п., .ui-primary, .ui-secondary и т.п. Для свойств состояния (булевых свойств) используются префиксы "-is" и "-has". Например, .ui-is-disabled, .ui-is-active, .ui-is-loading и т.п.
Вопрос специфичности селекторов решается с помощью :where. Таким образом будут подобные селекторы: .ui-button:where(.ui-primary.ui-disabled), .ui-card:where(.ui-xl) и т.п. Это удобно, так как :where не меняет специфичность селектора и поддерживается всеми современными браузерами (Safari 14+).
В UI системе удобно создать набор shortcut классов (tailwind классов) для некоторых (именно части) свойств. Например, для отступов .ui-margin-top-8, .ui-margin-top-12 и т.п., для обложки .ui-border-top, ui-border-y и т.п.
Учитывая сохранение специфичности через :where можно будет легко переопределить (или дополнить) стили в нужных местах проекта. Например, добавить отступы к карточке снизу добавив класс .ui-margin-bottom-24.
Если вашей команде нравится использовать data-атрибуты вместо классов, то совмещая это с :where ничего не меняется, например, .ui-button:where([data-variant="primary"]), .ui-button:where([data-is-disabled]). Но лично я считаю этот вариант более громоздким визуально и придерживаюсь той позиции, что data-атрибуты - это про данные, а не про стилизацию.
<my-card data-size="big"></my-card>
Вы не задумывались о том, как поисковая машина Google проиндексирует контент, размеченный вашими собственными HTML-тегами? Возможно, такой контент будет либо вообще проигнорирован, либо обработан некорректно?
Кастомные теги лишены должной семантики изначально, по сравнению со стандартными тегами, если их создавать с нуля, поэтому за такое спасибо не скажут и различные плагины, и скринридеры, и приложения (например преобразующие страницу в удобно-читаемый вид без лишнего контента). Семантику, конечно, можно будет прикрутить, но это придется править для каждого кастомного тега ручками. НО лучше всего просто создавать теги за счет расширения существующих экземпляров класса (например, HTMLButtonElement) и тогда семантика подтянется соответствующая. Здесь вопрос скорее в другом, зачем плодить такие элементы для решения заданного автором статьи вопроса? Мало того, что создаются какие-то дубликаты существующего инструментария, так эти кастомные теги еще не так просты, как кажутся на первый взгляд, и требуют должного внимания к ним. На мой взгляд автор предлагает решить одну маленькую проблему путем, на котором встретятся множество других проблем, которые могут оказаться посерьезнее.
При прочтении заголовка статьи я ожидал увидеть CSSinJS подход. При прочтении статьи на середине я ожидал увидеть упоминания css переменных. Вместо этого я увидел хардкор-код с бойлерплейтами и жалобой, дескать не хочу добавлять лишнего инструментария (но, например, полифилл для кастом стейт в конце добавить почему-то предложил), поэтому давайте плодить множество data-атрибутов и кастомных тегов. О возможных подводных камнях, которые имеются, не написал ничего. При том решил ли автор поставленного им же вопроса? Большая часть опции в виде close/open, loading/loaded и прочие обычно управляются переменными состояния и, если они неправильно перетирают друг-друга, то решать вопрос нужно совершенно иначе и в самом коде логики. Остальная малая часть опции, если правится руками, требует банальной проверки результата (в идеале скриншоты тесты, конечно), причем это потребуется даже в случае с предложенным автором решениями, поскольку и в атрибутах, и в тегах, можно также легко накосячить.
Попробуйте tailwind и вам больше не придет в голову городить этот огород 😂
Автор поднял интересную тему. Я бы название статьи изменил на «Кому необходимо знать имена CSS-классов?»
И в самом деле кому?
Пользователю? Нет!
Браузеру? Нет! Он отрисует ровно так, как описано в стилях и как классы зовутся ему по барабану.
Остались программисты. А теперь ответьте себе на этот вопрос: зачем программисту знать имя CSS-класса?
Я для себя на этот вопрос давно ответил и проблему решил. Через свою реализацию концепции Web-компонентов. Написал компилятор и вуаля! Не знаю ни одного имени класса. А состоянием управляю через data-аттрибуты.
по моему "решение", создает гораздо больше проблем чем решает.
На первый поверхностный взгляд, автор исходной статьи сначала пытается пришить к рассматриваемому вопросу грубыми нитками концепцию ООП, а потом удивляется, что она не очень хорошо пришивается.
CSS это ж не только классы, а как допустим я как UI дизайнер буду анализировать и вносить правки в готовый продукт? Сейчас всё просто - вижу кривой блок, открываю веб-инспектор, прописываю или исправляю то что там есть в CSS и вношу правку. Как при этом подходе мне фиксить визуальные баги? Да и ради чего оно надо если как говорят выше это повлияет на производительность? Ну да вроде незначительно, но как правило эти "незначительно" потом выстраиваются в длинную цепочки и всё начинает глючить и тормозить.
не надо ломать то что работает. class в css вполне удобный инструмент, он работает, он выполняет свою функцию. есть вещи которые менять не надо. по моему мнению у css как раз проблема в том что в него за последние годы чего только не добавили, начинали с правил оформления, а теперь его чуть ли не в язык программирования превращают.
Автор не вывез разобраться как работают стили, а уже лезет что-то править в стандарт. Это очень плохо, очень.
Лучше бы ему бросить web-разработку иначе ведь найдутся и последователи у этой шизы и испортят жизнь всему сообществу.
CSS-классы вредны