[CSS, JavaScript] Atomizer vs Minimalist Notation (MN)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Minimalist Notation (MN) (минималистическая нотация) — гибкая адаптивная технология генерации стилей.
Она генерирует стили только для существующих классов разметки html, jsx, и т.п. — благодаря чему отпадает необходимость заботиться о компонентом подходе в CSS, мёртвом CSS коде, и отпадает необходимость писать CSS код вообще.
В ней поддерживаются брейкпонты (медиа-запросы), селекторы, приоритеты, группировки, необходимые автопрефиксы и полифилы.
Применение этой технологии похоже на использование инлайновых стилей, только с гораздо более выразительным синтаксисом и множеством дополнительных возможностей, поэтому MN можно даже назвать технологией inline styles 2.0.
MN подразумевает не только правила нотации как таковые, но и, по-сути, даже усовершенствованный альтернативный синтаксис каскадного языка установки стилей, который ориентирован на инлайновое применение.
Я разработал MN в 2017 году, и с того момента успел во многом усовершенствовать эту технологию, добавить достаточно пресетов и удобных инструментов для её изучения.
Если кто-то желает узнать о том, как мне пришло в голову разработать собственную технологию, я оставляю ссылку на свой репозиторий, дабы не плагиатить у самого себя.
Технология MN имеет обширную функциональность.
Для многих она является новой, и почти не имеет других первоисточников для изучения, кроме этой статьи, поэтому предупреждаю, что ниже будет МНОГО БУКВ и примеров кода.
Начнём с более менее общеизвестных функциональностей, которые имеются и у ближайшего аналога этой технологии, параллельно проводя сравнение.
Лучший способ что-то объяснить — это показать наглядные примеры.
Начнём с сравнения Minimalist-Notation с более менее известными функциональностями его первого аналога: Atomizer.
Актуальные на момент написания статьи версии пакетов:
- minimalist-notation 1.5.17 ;
- atomizer 3.6.2 .
Проверить примеры ниже с Atomizer можно будет здесь:
https://pankajparashar-zz.github.io/atomizer-web/
Проверить примеры ниже с MN можно можно будет здесь:
https://viewer.minimalist-notation.org/
Документация по всем предустановленным пресетам MN здесь:
https://styles.minimalist-notation.org/
Atomizer vs MN. Простые примеры##
Atomizer
<div class="D(f) Jc(c) Ai(c) H(100%) Fz(20vh) C(#0f0) C(#F00) Bgc(#f00) M(-10px) P(10px) Pstart(10%) Pos(a)">...</div>
.D\(f\) {
display: flex;
}
.Jc\(c\) {
justify-content: center;
}
.Ai\(c\) {
align-items: center;
}
.H\(100\%\) {
height: 100%;
}
.Fz\(20vh\) {
font-size: 20vh;
}
.C\(\#0f0\) {
color: #0f0;
}
.Bgc\(\#f00\) {
background-color: #f00;
}
.M\(-10px\) {
margin: -10px;
}
.P\(10px\) {
padding: 10px;
}
.Pstart\(10\%\) {
padding-left: 10%;
}
.Pos\(a\) {
position: absolute;
}
На что я обратил внимание в этом примере с Atomizer:
- аббревиатуры аналогичны Emmet;
- имена свойств в нотации представлены как функции, которые всегда именуются аббревиатурами, начинающимися с верхнего регистра;
- значения свойств стилей в нотации обязательно заключаются в скобки;
- аббревиатура Pstart для свойства padding-left для меня неожиданна;
- единицы измерения в нотации нельзя пропускать;
- некоторые значения можно задавать только предустановленными аббревиатурами, такими как, например: f - flex, c - center, однако, например, такая запись работать не будет: D(flex);
- когда я пытаюсь задавать цвет символами верхнего регистра, например, #F00 — вместо, #f00, то стили не генерируются, либо генерируется какая-то петрушка…
Minimalist-Notation
<div class="dF jcC aiC h f20vh c0F0 bgcF00 m-10 p10 pl10% abs">...</div>
.dF {
display: flex;
}
.jcC {
justify-content: center;
}
.aiC {
align-items: center;
}
.h {
height: 100%;
}
.f20vh {
font-size: 20vh;
}
.c0F0 {
color: #0f0;
}
.bgcF00 {
background-color: #f00;
}
.m-10 {
margin: -10px;
}
.p10 {
padding: 10px;
}
.pl10\% {
padding-left: 10%;
}
.abs {
position: absolute;
}
Отличия:
- для большинства дефолтных обработчиков аббревиатуры аналогичны Emmet, за некоторыми исключениями, которые связаны исторически с моими личными практиками, частотой использования различных свойств стилей.
Это связано ещё с тем, что лично я узнал о существовании Emmet и Atomizer только после того, как создал первую версию MN.
Примеры:
f — вместо fz для свойства font-size — ибо размер шрифта Мы задаем очень часто,
font — вместо f для свойства font — ибо Мы редко используем его непосредственно,
abs — вместо posA для свойства и значения position: absolute, хотя posA тоже можно использовать;
- имена свойств в нотации именуются аббревиатурами в нижнем регистре;
- значения свойств стилей в нотации начинаются с любого символа отличного от латинских букв в нижнем регистре.
Кончено и здесь в нотации значения также можно заключать в скобки: d(F) — но скобки
в MN предназначены не для параметризации, а для группировки подстрок нотации, например,
эти записи аналогичны: p(l10|r15) === pl10 pr15.
Кроме того, нет смысла использовать скобки без нужды, ибо это увеличивает конечный размер кода.
- единицы измерения не являются частью базовой нотации.
Необходимость указания единиц измерения зависит от обработчика заданного для соответствующего имени в нотации.
Для большинства дефолтных обработчиков указание единиц измерения являются опциональным, например, возможны вариации:
f14px == f14,
h100% == h,
h10px == h10.
- в MN также имеются предустановленные аббревиатуры для некоторых значений, такие как, например,
F - flex, C - center — хотя Мы можем использовать полные именования.
Вы имеете возможность использовать вместо, например, этих аббревиатур:
dF dIB jcC aiC — различные вариации именований:
dFlex dInlineBlock jcCenter aiCenter,
d_flex d_inline-block jc_center ai_center .
В большинстве дефолтных обработчиков запись для значений из формата camelCase
просто трансформируется в формат kebabCase, а нижнии прочерки заменяются на пробелы;
- для большинства дефолтных обработчиков предусмотрены значения по умолчанию, например, как Мы наблюдаем, для свойства height (h) значение по умолчанию равно 100% .
Если Мы используем значение: h40 — то получим следующее:
.h40 {height: 40px}
- Для указания цвета не требуется решетка (#).
Более того, решетка (#) является служебным символом, необходимым для полноценных селекторов, о которых Вы узнаете ниже.
Если Вы добавите решетку, то получите не то, чего, вероятно, ожидаете:
<div class="c#0F0">...</div>
.c\#0F0#0F0 {color: #000}
В некоторых случаях решетка всё же нужна, например, для свойства outline.
Тогда служебные символы в нотации нужно экранировать:
<div class="ol_thick_double_\#32a1ce">...</div>
.ol_thick_double_\\\#32a1ce {outline: thick double #32a1ce}
В MN для дефолтных обработчиков (c, bc, olc, fill, stroke, bg, bgc, temc, tdc) цвета можно задавать разными вариациями символов — об этом подробнее будет ниже.
- обработчики для аббревиатур свойств являются кастомизируемыми и подключаются по умолчанию из библиотеки minimalist-notation/presets/styles.
Вы имеете возможность полностью кастомизировать MN под свои нужды, путем создания собственных обработчиков:
// для свойcтва "padding"
mn('p', (params) => {
return {
style: {
padding: (params.num || '0') + (params.unit || 'px'),
},
};
});
// для свойcтва "padding-left"
mn('pl', (params) => {
return {
style: {
paddingLeft: (params.num || '0') + (params.unit || 'px'),
},
};
});
// для свойcтва "color"
mn('c', (params) => {
return !params.negative && {
style: {
color: mn.utils.color(params.value || '0'),
},
};
}, '^(([A-Z][a-z][A-Za-z]+):camel|([A-F0-9]+):color):value(.*)?$');
Примеры обработчиков MN можно подсмотреть в репозитории:
https://github.com/mr-amirka/minimalist-notation/presets
Как за себя говорит само название технологии, минимализм — здесь главное,
поэтому нотация устроена таким образом, чтобы Нам не требовалось писать много кода
и осуществлять прочие избыточные телодвижения.
Примеры немного сложнее
Atomizer vs MN. Флаг !important
Atomizer
<div class="D(f)! C(#0f0)!">...</div>
.D\(f\)\! {
display: flex !important;
}
.C\(\#0f0\)\! {
color: #0f0 !important;
}
Minimalist-Notation
<div class="dF-i c0F0-i">...</div>
.dF-i {
display: flex!important;
}
.c0F0-i {
color: #0f0!important;
}
В MN для установки флага !important не используется символ !, потому-что этот служебный символ уже применяется в нотации для реверсирования подстрок.
Группировки и реверсирование подстрок (!)
Возьмем такую запись:
<div class="cF:hover>.item">...</div>
.cF\:hover\>\.item:hover .item {
color: #fff;
}
Если Мы добавим символ ! после :hover, то получим следующее:
<div class="cF:hover!>.item">...</div>
.cF\:hover\!\>\.item .item:hover {
color: #fff;
}
Здесь псевдоселектор hover переместился от родительского элемента к дочернему.
Этот приведенный для наглядности способ использования реверсирования в данном случае бесполезен, ибо такого же эффекта Мы можем добиться и другим способом:
<div class="cF>.item:hover">...</div>
.cF\>\.item\:hover .item:hover {
color: #fff;
}
Однако реверсирование может оказаться весьма полезным для сокращения записи в комбинации с группировками.
Допустим, что Мы верстаем какой-то простой сайт без шаблонизаторов и без React компонентов — возможно, это лендинг.
Мы имеем некоторый список, в котором располагаются одинаковые дочерние элементы:
<ul class="m p dBlock">
<li class="mb5 dBlock">
<a class="c0 c0:hover tdNone p5">...</a>
</li>
<li class="mb5 dBlock">
<a class="c0 c0:hover tdNone p5">...</a>
</li>
<li class="mb5 dBlock">
<a class="c0 c0:hover tdNone p5">...</a>
</li>
...
</ul>
.m {
margin: 0;
}
.p {
padding: 0;
}
.dBlock {
display: block;
}
.mb5 {
margin-bottom: 5px;
}
.c0,.c0\:hover:hover {
color: #000;
}
.tdNone {
text-decoration: none;
}
.p5 {
padding: 5px;
}
Мы понимаем, что указывать классы для каждого элемента может быть довольно утомительно, особенно если в процессе Мы будем что-то изменять.
Мы могли бы написать код гораздо короче, и таким образом, чтобы в последующем при необходимости Нам приходилось вносить меньше правок.
Рассмотрим по шагам различные способы.
Вынесем всю нотацию в родительский элемент:
<ul class="m p dBlock mb5>li dBlock>li c0>a c0>a:hover tdNone>a p5>a">
<li>
<a>...</a>
</li>
<li>
<a>...</a>
</li>
<li>
<a>...</a>
</li>
...
</ul>
Это выглядит прикольно, но если свойств и элементов будет довольно много, или если селекторы дочерних элементов будут более длинные, то получится довольно громоздко и мало читаемо.
Для примера Мы добавим ещё один уровень списка, который имеет отступ в 10 пикселей слева, а ссылки внутри него имеют подчеркивание и красный цвет.
При наведении курсора на ссылку, она становится зеленой, а при нажатии — становится синей.
<ul class="
m p dBlock
mb5>li dBlock>li
p5>a tdNone>1li>1a c0>1li>1a c0>1li>1a:hover
m>ul p>ul dBlock>ul pl10>1li>1ul
tdUnderline>1li>1ul>1li>1a
cRed>1li>1ul>1li>1a
cGreen>1li>1ul>1li>1a:hover
cBlue>1li>1ul>1li>1a:active
">
<li>
<a>...</a>
</li>
<li>
<a>...</a>
</li>
<li>
<a>...</a>
<ul>
<li>
<a>...</a>
</li>
...
</ul>
</li>
...
</ul>
Число перед селектором (1) просто указывавет на строго определенную глубину вложенности элемента.
Кстати, обратите внимание: селекторы с одинаковыми значениями свойств в сгенерированном CSS коде группируются вместе — что позволяет минимизировать его объём на выходе.
Думаю, этого для наглядности Нам пока хватит.
Теперь попробуем группировки:
<ul class="
(m|p|dBlock)(|>ul) (mb5|dBlock)>li
p5>a tdNone>1li>1a c0>1li>1a(|:hover)
pl10>1li>1ul
(tdUnderline|cRed)>1li>1ul>1li>1a
cGreen>1li>1ul>1li>1a:hover
cBlue>1li>1ul>1li>1a:active
">
<li>
<a>...</a>
</li>
<li>
<a>...</a>
</li>
<li>
<a>...</a>
<ul>
<li>
<a>...</a>
</li>
...
</ul>
</li>
...
</ul>
Здесь группировки Нам не сильно помогли…
Вот теперь попробуем ещё и реверсирование:
<ul class="
(m|p|dBlock)(|>ul)
(mb5|dBlock)>li p5>a
(tdNone|c0(|:hover!))>1li>1a
pl10>1li>1ul
(tdUnderline|c(Red|Green:hover!|Blue:active!))>1li>1ul>1li>1a
">
<li>
<a>...</a>
</li>
<li>
<a>...</a>
</li>
<li>
<a>...</a>
<ul>
<li>
<a>...</a>
</li>
...
</ul>
</li>
...
</ul>
Забавно, не правда ли?
Хорошо. Слегка поиграем с вложенностями и заменим имена псевдоклассов и значений на краткие синонимы:
<ul class="
(m|p|dB)(|>ul)
(mb5|dB)>li
p5>a
(tdN|c0(|:h!))>2a
pl10>2ul
(tdU|c(Red|Green:h!|Blue:a!))>4a
">
<li>
<a>...</a>
</li>
<li>
<a>...</a>
</li>
<li>
<a>...</a>
<ul>
<li>
<a>...</a>
</li>
...
</ul>
</li>
...
</ul>
Всё-таки, для пущей наглядности заменю имена тегов на классы:
<ul class="
ListA
(m|p|dB)(|.ListA)
pl10>2.ListA
(mb5|dB)>.ListA__Item
(p5>|(tdN|c0(|:h!))>2|(tdU|c(Red|Green:h!|Blue:a!))>4).ListA__Link
">
<li class="ListA__Item">
<a class="ListA__Link">...</a>
</li>
<li class="ListA__Item">
<a class="ListA__Link">...</a>
</li>
<li class="ListA__Item">
<a class="ListA__Link">...</a>
</li>
<li>
<a class="ListA__Link">...</a>
<ul class="ListA">
<li class="ListA__Item">
<a class="ListA__Link">...</a>
</li>
...
</ul>
</li>
...
</ul>
Извратились Мы знатно!
Однако, рекомендую, друзья мои, этими группировками сильно не увлекаться!
Здесь у некоторых может возникнуть вопрос, как же Нам решить проблему с
неодноранговастью сгенерированных селекторов? — ибо, если Мы захотим кастомизировать
отдельные элементы из этого списка, нам могут помешать грабли перекрывающих стилей.
Ответ:
Мы, например, можем просто именовать классы дочерних уровней немного иначе и не указывать в нотации глубину их вложенности, дабы селекторы всех уровней имели равный приоритет:
<ul class="
ListA
(m|p|dB)(|.ListA)
(mb5|dB)>.ListA__Item
p5>.ListA__Link
(tdN|c0(|:h!))>.ListA__Link_level1
pl10>.ListA_level2
(tdU|c(Red|Green:h!|Blue:a!))>.ListA__Link_level2
bgF88>.ListA__Item_substyle
">
<li class="ListA__Item ListA__Item_level1 ListA__Item_substyle">
<a class="ListA__Link ListA__Link_level1">...</a>
</li>
<li class="ListA__Item ListA__Item_level1">
<a class="ListA__Link ListA__Link_level1">...</a>
</li>
<li class="ListA__Item ListA__Item_level1">
<a class="ListA__Link ListA__Link_level1">...</a>
</li>
<li>
<a class="ListA__Link">...</a>
<ul class="ListA ListA_level2">
<li class="ListA__Item ListA__Item_level2">
<a class="ListA__Link ListA__Link_level2">...</a>
</li>
<li class="ListA__Item ListA__Item_level2 ListA__Item_substyle">
<a class="ListA__Link ListA__Link_level2">...</a>
</li>
...
...
</ul>
</li>
...
</ul>
Многие могут заметить, что здесь Мы имеем возможность использовать сложные селекторы, которые потенциально порождают нежелательные сайд-эффекты с перекрытиями стилей.
Да. Это так. И это, отчасти, противоречит самой методологии Atomic CSS.
Тем не менее, есть несколько нюансов:
- Нас никто не принуждает использовать именно такие конструкции;
- Нас никто не ограничивает, и при необходимости Мы можем воспользоваться этими конструкциями;
- Благодаря тому, что весь код как правило вмещается у Нас на одном экране и не разрывается контекст между разметкой и стилями, Нам гораздо проще обнаружить, "где зарыта собака" и исключить возможные грабли с перекрытиями стилей.
Atomizer vs MN. Псевдоклассы и псевдоэлементы
Чисто ради сравнения, рассмотрим доступную в Atomizer возможность использования псевдоклассов и псевдоэлементов:
<div class="Bgc(#fff):h C(blue):a C(#0f0):hover D(n)::b">...</div>
.Bgc\(\#fff\)\:h:hover {
background-color: #fff;
}
.C\(blue\)\:a:active {
color: blue;
}
.C\(\#0f0\)\:hover:hover {
color: #0f0;
}
.D\(n\) {
display: none;
}
Мы видим, что Atomizer позволяет юзать как некоторые полные имена псевдоклассов, так и их короткие синонимы.
В документации написано, что можно испозовать псевдоэлементы.
Например: b для ::before
Но на практике, возможно, я как-то сильно тупил, и как ни пытался пробовать разные варианции, ни один пример не сработал.
Для D(n)::b я ожидал получить следующее:
.D\(n\)\:\:::before {
display: none;
}
В MN же Мы можем всё тоже самое:
<div class="bgcF:h cBlue:a c0F0:hover dN::before">...</div>
.bgcF\:h:hover {
background-color: #fff;
}
.cBlue\:a:active {
color: blue;
}
.c0F0\:hover:hover {
color: #0f0;
}
.dN\:\:before::before {
display: none;
}
Помимо этого, Мы имеем возможность задавать вообще любые псевдоклассы и севдоэлементы известные и неизвестные в CSS:
<div class="bgcF:hz cBlue::placeholder c0F0::-webkit-input-placeholder bgE:odd bt:first c:i">...</div>
.bgcF\:hz:hz {
background-color: #fff;
}
.cBlue\:\:placeholder::placeholder {
color: blue;
}
.c0F0\:\:-webkit-input-placeholder::-webkit-input-placeholder {
color: #0f0;
}
.bgE\:odd:nth-child(2n+1) {
background: #eee;
}
.bt\:first:first-child {
border-top-width: 0;
}
.c\:i::placeholder {
color: #000;
}
.c\:i:-ms-input-placeholder {
color: #000;
}
.c\:i::-moz-placeholder {
color: #000;
}
.c\:i::-webkit-input-placeholder {
color: #000;
}
В MN Мы можем параметризовать псевдоклассы:
<div class="c:not[.anyClass] bg0A:not[tag[attr=value].class\:pseudo] c88F4:n[13] c01:n[3n+1]">...</div>
.c\:not\[\.anyClass\]:not(.anyClass) {
color: #000;
}
.bg0A\:not\[tag\[attr\=value\]\.class\\\:pseudo\]:not(tag[attr=value].class:pseudo) {
background: #000;
background: rgba(0,0,0,.67);
}
.c88F4\:n\[13\]:nth-child(13) {
color: #88f;
color: rgba(136,136,255,.27);
}
.c01\:n\[3n\+1\]:nth-child(3n+1) {
color: #000;
color: rgba(0,0,0,.07);
}
В MN Мы также можем указать несколько псевдоклассов подряд:
<input
type="checkbox"
class="mh20:not[.anyClass]:n[5n+2]:c:h"
/>
.mh20\:not\[\.anyClass\]\:n\[5n\+2\]\:c\:h:not(.anyClass):nth-child(5n+2):checked:hover {
margin-left: 20px;
margin-right: 20px;
}
В MN можно задать собственный набор синонимов для псевдоклассов.
Пример:
mn.utils.extend(mn.states, {
foo: [':active'],
bar: ['.Bar_active', '.otherSelector'],
vasya: ['[data-name=vasya]'],
});
<div class="cRed:foo cGreen:bar cBlue:vasya">...</div>
.cRed\:foo:active {
color: red;
}
.cGreen\:bar.otherSelector,.cGreen\:bar.Bar_active {
color: green;
}
.cBlue\:vasya[data-name=vasya] {
color: blue;
}
Atomizer vs MN. Комбинаторы
Сравним кабинаторы.
"The underscore character ( _ )" в Atomizer:
<div class="foo">
<div class="foo_D(n)"></div>
</div>
Аналог в MN:
<div class="foo">
<div class="dN<.foo"></div>
</div>
"The right angle bracket character ( > )" в Atomizer:
<div class="foo">
<div class="foo>D(n)"></div>
</div>
Аналог в MN:
<div class="foo">
<div class="dN<1.foo"></div>
</div>
"The plus sign ( + )" в Atomizer:
<div class="foo"></div>
<div class="foo+D(n)"></div>
Аналог в MN:
<div class="foo"></div>
<div class="dN<.foo+"></div>
Помимо этого в MN комбинатор "соседний брат" ( + ) можно использовать в обратном направлении:
<div class="dN+.foo"></div>
<div class="foo"></div>
В MN можно использовать комбинатор "общий брат" ( ~ ):
<div class="foo"></div>
<div class="dN<.foo~"></div>
<div class="dN<.foo~"></div>
.foo~ .dN\<\.foo\~ {
display: none;
}
И в обратном направлении:
<div class="dN~.foo"></div>
<div class="foo"></div>
<div class="foo"></div>
.dN\~\.foo~.foo {
display: none;
}
Atomizer vs MN. Контекст
В Atomizer есть функциональность, называемая Context class, которая используется в случаях, когда необходимо установить некоторое поведение стилей при изменении селектора на родительском элементе.
Пример:
<div class="foo bar any">
<div class="double">
<div class="foo_D(n) bar:h_D(n) any_D(n):h any_double_D(n)">...</div>
</div>
</div>
.foo .foo_D\(n\), .any_double .any_double_D\(n\) {
display: none;
}
.bar:hover .bar\:h_D\(n\) {
display: none;
}
.any .any_D\(n\)\:h:hover {
display: none;
}
На что я обратил внимание в этом примере:
- нельзя использовать последовательность из нескольких родительских классов, что я и попытался сделать в этом примере таким образом any_double_D(n);
- оказывается, стили в конечном CSS всё же частично группируются по какому-то принципу.
В MN, как, вероятно, некоторые могли догадаться из примеров выше, можно юзать полноценные селекторы, но для начала давайте рассмотрим, каким образом можно сделать тоже самое:
<div class="foo bar any">
<div class="double">
<div class="dN<.foo dN<.bar:h dN:h<.any dN<.double<.any">...</div>
</div>
</div>
.foo .dN\<\.foo,.bar:hover .dN\<\.bar\:h,.any .dN\:h\<\.any:hover,.any .double .dN\<\.double\<\.any {
display: none;
}
При необходимости можно задать строгую глубину вложенности:
<div class="any">
<div class="double">
<div class="dN<1.double<1.any dN<2.any">...</div>
</div>
</div>
.any>.double>.dN\<1\.double\<1\.any,.any>*>.dN\<2\.any {
display: none;
}
Аналогичным образом, используя стрелочки направленные в противоположную сторону, можно задавать стили дочерним элементам:
<div class="dN>2.double">
<div class="any">
<div class="double">...</div>
</div>
</div>
.dN\>2\.double>*>.double {
display: none;
}
Можно комбинировать родительские селекторы с дочерними:
<div class="parent">
<div class="dN<.parent>2.double">
<div class="any">
<div class="double">...</div>
</div>
</div>
</div>
.parent .dN\<\.parent\>2\.double>*>.double {
display: none;
}
<div class="dN>.double<.any">
<div class="any">
<div class="double">...</div>
</div>
</div>
.dN\>\.double\<\.any .any .double {
display: none;
}
Можно обозначить поведение стиля при наличии произвольного селектора на текущем элементе:
<div class="bgF2.active[data-name=Gena]:h active" data-name="Gena">...</div>
<div class="dN#feedback" id="feedback">...</div>
<div class="o50.disable disable">...</div>
.bgF2\.active\[data-name\=Gena\]\:h.active[data-name=Gena]:hover {
background: #fff;
background: rgba(255,255,255,.13);
}
.dN\#feedback#feedback {
display: none;
}
.o50\.disable.disable {
opacity: .5;
}
Отличия:
- в нотации MN строгий порядок.
Фрагмент строки отвечающий за значение стиля, как основная суть нотации, всегда следует вначале, и только затем начиная с какого-нибудь служебного символа следует контекст, который уточняет к чему стили имеют отношение.
Если существует такое понятие как "Венгерская нотация", то мне чисто ради наблюдения, хочется здесь назвать такой способ записи "Тюркской нотацией", ибо в тюркских языках суть слова всегда находится вналале и только затем следует множество уточняющих суффиксов и окончаний.
В глобальном смысле это даже предельно технически правильный способ подачи почти любой информации, заключающийся в последовательном уменьшении неопределенности.
Пример со временем:
2020-02-02 22:22:22 — каждое следующее значение в этой последовательности практически будет бесполезно без предшествующих;
Аналогичный пример с адресом: Germany, 14193 Berlin, Kronberger Str. 12;
- в MN можно использовать произвольные слелекторы.
Atomizer vs MN. Вычисляемые значения
В Atomizer есть возможность использования вычисляемых значений.
Для некоторых свойств Мы можем использовать дроби:
<div class="W(1/2) P(1/3) M(1/4) Start(1/5) T(1/6) Pstart(1/7) Miw(1/8)">...</div>
.W\(1\/2\) {
width: 50%;
}
.P\(1\/3\) {
padding: 33.3333%;
}
.M\(1\/4\) {
margin: 25%;
}
.Start\(1\/5\) {
left: 20%;
}
.T\(1\/6\) {
top: 16.6667%;
}
.Pstart\(1\/7\) {
padding-left: 14.2857%;
}
.Miw\(1\/8\) {
min-width: 12.5%;
}
В MN Мы также можем использовать вычисляемые значения, в том числе и дроби:
<div class="w1/2 p1/3 m1/4 sl1/5 st1/6 pl1/7 wmin1/8">...</div>
.w1\/2 {
width: 50%;
}
.p1\/3 {
padding: 33.33%;
}
.m1\/4 {
margin: 25%;
}
.sl1\/5 {
left: 20%;
}
.st1\/6 {
top: 16.66%;
}
.pl1\/7 {
padding-left: 14.28%;
}
.wmin1\/8 {
min-width: 12.5%;
}
Однако в Atomizer почему-то нельзя вычесть или сложить значение с дробью.
Я проверил:
<div class="W(1/2-10) P(1/3+5)">...</div>
.W\(1\/2-10\) {
width: 50%;
}
В MN Мы можем вычитать и складывать значения с дробью, и это для некоторых случаяев весьма полезная возможность.
Пример:
<div class="w1/2-10 p1/3\+5">...</div>
.w1\/2-10 {
width: calc(50% - 10px);
}
.p1\/3\\\+5 {
padding: calc(33.33% + 5px);
}
Замечания:
- здесь " + " — это с служебный символ-комбинатор, как и в обычном CSS, поэтому он экранируется слэшем (\).
Atomizer vs MN. Установка цветов
В Atomizer для указания цвета используются шестнадцатеричные цвета из 3-х или 6-ти символов с префиксом # в качестве идентификатора значения.
Шестнадцатеричные значения для цветов должны быть написаны в нижнем регистре (т.е., #ccc, а не #CCC).
Для установки коэффициента непрозрачности к шестнадцатеричному значению необходимо добавлять точку(.) с десятичным значением.
Например:
<div class="C(#fff) Bdc(#ff0000) Bgc(#00ff00.5)">...</div>
.C\(\#fff\) {
color: #fff;
}
.Bdc\(\#ff0000\) {
border-color: #ff0000;
}
.Bgc\(\#00ff00\.5\) {
background-color: rgba(0,255,0,.5);
}
В MN для указания цвета используются шестнадцатеричные цвета от 0 до 8-ми символов с коэффициентом непрозрачности включительно, но для коэффициента непрозрачности дополнительно имеется альтернативный способ: использовать точку(.) с десятичным значением.
Например:
<div class="c cFFF bcFF0000 bgc00FF00\.5 сFF00008 сFF000080 cF bgc08 bgc0\.5 bgc1234 bgc12348">...</div>
.c {
color: #000;
}
.cFFF {
color: #fff;
}
.bcFF0000 {
border-color: #f00;
}
.bgc00FF00\\\.5 {
background-color: #0f0;
background-color: rgba(0,255,0,.5);
}
.cFF00008 {
color: #f00;
color: rgba(255,0,0,.53);
}
.cFF000080 {
color: #f00;
color: rgba(255,0,0,.5);
}
.cF {
color: #fff;
}
.bgc08 {
background-color: #000;
background-color: rgba(0,0,0,.53);
}
.bgc0\\\.5 {
background-color: #000;
background-color: rgba(0,0,0,.5);
}
.bgc1234 {
background-color: #123;
background-color: rgba(17,34,51,.27);
}
.bgc12348 {
background-color: #123;
background-color: rgba(17,34,51,.28);
}
Замечания:
- здесь " . " — это с служебный символ, с помощью которого указываются классы, как и в обычном CSS, поэтому он экранируется слэшем (\).
- Вы могли обратить внимание на то, что ко всем генерируемым цветам с коэффициентом непрозрачности дополнительно добавляется значения без альфа-канала — это полифил для браузеров, которые не поддерживают коэффициент непрозрачности.
Это поведение можно отключить в настройках MN при помощи установки опции altColor: 'off'
Градиентная цветовая заливка в MN
В MN Мы имеем два похожих пресета, которые, как известно, на практике часто могут быть взаимозаменяемыми: bgc и bg.
Пример:
<div class="bg48A">...</div>
<div class="bgc48A">...</div>
.bg48A {
background: #48a;
}
.bgc48A {
background-color: #48a;
}
Однако с bg Мы также можем легко и лакончино делать градиенты, путем указания последовательности цветов через знак минуса (-):
<div class="bg0-F">...</div>
<div class="bgF00-0F0-00F">...</div>
.bg0-F {
background: #000;
background: linear-gradient(180deg,#000 0%,#fff 100%);
}
.bgF00-0F0-00F {
background: #f00;
background: linear-gradient(180deg,#f00 0%,#0f0 50%,#00f 100%);
}
Имеется возможность устанвавливать направление градиента:
<div class="bg0-F_g45">...</div>
<div class="bg0-F_g90">...</div>
.bg0-F_g45 {
background: #000;
background: linear-gradient(225deg,#000 0%,#fff 100%);
}
.bg0-F_g90 {
background: #000;
background: linear-gradient(270deg,#000 0%,#fff 100%);
}
Помимо линейных градиентов, Мы можем использовать радиальные градиенты:
<div class="bg0-F_r">...</div>
<div class="bg0-F_r_closestSide">...</div>
<div class="bg0-F_r_ellipse_at_top">...</div>
.bg0-F_r {
background: #000;
background: radial-gradient(circle,#000 0%,#fff 100%);
}
.bg0-F_r_closestSide {
background: #000;
background: radial-gradient(closest-side,#000 0%,#fff 100%);
}
.bg0-F_r_ellipse_at_top {
background: #000;
background: radial-gradient(ellipse at top,#000 0%,#fff 100%);
}
Atomizer vs MN. Несколько значений
В Atomizer есть возможность указывать несколько значений, разделенных запятыми, когда это поддерживается соответствующими свойствами, например:
<div class="Bgp(20px,50px)">...</div>
.Bgp\(20px\,50px\) {
background-position: 20px 50px;
}
С MN получить аналогичный результат можно похожим образом:
<div class="bgp20px_50px">...</div>
.bgp20px_50px {
background-position: 20px 50px;
}
Atomizer vs MN. Идентификатор брейкпоинта (breakpoint identifier). Медиа-запросы
В Atomizer можно устанавливать брейкпоинт к правилу, путем добавления суффикса к записи. Брейкпоинт указывает, что это правило вступит в силу только в рамках медиа-запроса.
Значения имени и длины каждой точки останова определяются в объекте конфигурации:
{
// ...
breakPoints: {
'sm': '@media(min-width:750px)', // breakpoint 1
'md': '@media(min-width:1000px)', // breakpoint 2
'lg': '@media(min-width:1200px)', // breakpoint 3
// ...
},
// ...
}
Используются эти брейкпоинты следующим образом:
<div class="W(50%)--sm W(33%)--md W(25%)--lg">...</div>
@media(min-width:750px) {
.W\(50\%\)--sm {
width: 50%;
}
}
@media(min-width:1000px) {
.W\(33\%\)--md {
width: 33%;
}
}
@media(min-width:1200px) {
.W\(25\%\)--lg {
width: 25%;
}
}
В MN также можно прекрасно устанавливать брейкпоинты, путем добавления суффикса к записи:
<div class="w50%@m w33%@d w25%@d2 w1/5@ie w1/6@android cr@mouse">...</div>
@media (max-width: 992px) {
.w50\%\@m {
width: 50%;
}
}
@media (min-width: 992px) {
.w33\%\@d {
width: 33%;
}
}
@media (min-width: 1200px) {
.w25\%\@d2 {
width: 25%;
}
}
.ie .w1\/5\@ie {
width: 20%;
}
.android .w1\/6\@android {
width: 16.66%;
}
@media (pointer: fine) and (hover: hover) {
.cr\@mouse {
cursor: pointer;
}
}
Здесь Мы имеем возможность для брейкпоинтов использовать как медиа-запросы, так и родительские селекторы.
Такая возможность может быть очень удобна в случаях, когда Нам нужно учесть какие-либо функциональности браузера, для которых пока не предусмотрены соответсвующие медиа-запросы, либо быстро сделать полифилы для браузеров, которые вовсе не поддерживают медиа-запросы.
Также возможность использования родительских селекторов в качестве брейкпоинтов, вероятно, могла бы пригодиться Нам для организации каких-нибудь контекстов или пространств имен.
Пример конфигурирования брейкпоинтов:
module.exports = (mn) => {
const {media} = mn;
// media-queries
media.m = {
query: '(max-width: 992px)',
priority: 0,
};
media.m2 = {
query: '(max-width: 768px)',
priority: 1,
};
media.d = {
query: '(min-width: 992px)',
priority: 2,
};
media.d2 = {
query: '(min-width: 1200px)',
priority: 3,
};
media.mouse = {
query: '(pointer: fine) and (hover: hover)',
priority: 4,
};
// ...
// user agents
media.mozilla = {
selector: '.mozilla'
};
media.webkit = {
selector: '.webkit'
};
media.ie = {
selector: '.ie'
};
media.iphone = {
selector: '.iphone'
};
media.android = {
selector: '.android'
};
// ...
};
Если Мы используем в нотации брейкпонт, который нигде не предустановлен, то в CSS получим значение медиа-запроса, которое непосредственно указали в записи:
<div class="w50%@print w50%@any">...</div>
@media print {
.w50\%\@print {
width: 50%;
}
}
@media any {
.w50\%\@any {
width: 50%;
}
}
Также помимо предустанавливаемых брейкпоинтов в нотации MN можно использовать
выражения:
<div class="w50%@768 w50%@768- w50%@768-992">...</div>
<div class="w50%@768-992x100-200 w50%@x100-200">...</div>
<div class="w50%@x100 w50%@x100-">...</div>
@media (max-width: 768px) {
.w50\%\@768 {
width: 50%;
}
}
@media (min-width: 768px) {
.w50\%\@768- {
width: 50%;
}
}
@media (min-width: 768px) and (max-width: 992px) {
.w50\%\@768-992 {
width: 50%;
}
}
@media (min-width: 768px) and (max-width: 992px) and (min-height: 100px) and (max-height: 200px) {
.w50\%\@768-992x100-200 {
width: 50%;
}
}
@media (min-height: 100px) {
.w50\%\@x100- {
width: 50%;
}
}
@media (min-height: 100px) and (max-height: 200px) {
.w50\%\@x100-200 {
width: 50%;
}
}
@media (max-height: 100px) {
.w50\%\@x100 {
width: 50%;
}
}
В выражениях брейкпонтов Мы можем указывать приоритет медиа-запроса, по которому каскадные блоки отсортировываются в конечном CSS:
<div class="w50%@768-992^5 w50%@768^1 w50%@992^3">...</div>
@media (max-width: 768px) {
.w50\%\@768\^1 {
width: 50%;
}
}
@media (max-width: 992px) {
.w50\%\@992\^3 {
width: 50%;
}
}
@media (min-width: 768px) and (max-width: 992px) {
.w50\%\@768-992\^5 {
width: 50%;
}
}
Minimalist Notation. Управление приоритетами стилей
В MN имеется специальная удобная возможность управления приоритетами стилей. Мы можем повысить приоритет стилей путем добавления суффикса * со значением приоритета в конец записи нотации, например:
<div class="cF*2 c0*3 cF00.active*2">...</div>
.cF\*2.cF\*2 {
color: #fff;
}
.c0\*3.c0\*3.c0\*3 {
color: #000;
}
.cF00\.active\*2.cF00\.active\*2.active {
color: #f00;
}
Таким образом, приоритет или, другими словами, специфичность стилей увлеличивается за счёт повторения в каскаде генерируемого CSS комбинации класса с самим собой столько раз, сколько Мы указываем в нотации.
Minimalist Notation. Селектор подстроки
С MN можно позволить себе сокращать имена классов в нотации.
Иногда достаточно только фрагмента имени:
<div class="SomeBlock">
<div class="bgF00>1.*_active">
<div class="SomeBlock__SomeElement SomeBlock__SomeElement_active">
...
</div>
<div class="SomeBlock__SomeElement">
...
</div>
<div class="SomeBlock__SomeElement SomeBlock__SomeElement_active">
...
</div>
</div>
</div>
.bgF00\>1\.\*_active>[class*=_active] {
background: #f00;
}
Например, в приложении на React Мы используем компоненты библиотеки Material-UI, которые легально кастомизируются через JSS так:
const React = require('react');
const {render} = require('react-dom');
const {withStyles} = require('@material-ui/core/styles');
const TextField = require('@material-ui/core/TextField').default;
const TextFieldGreen = withStyles({
root: {
'& label.Mui-focused': {
color: 'green',
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'red',
},
'&:hover fieldset': {
borderColor: 'yellow',
},
'&.Mui-focused fieldset': {
borderColor: 'green',
},
},
},
})(TextField);
function App() {
return (
<TextFieldGreen
label="Label"
required
defaultValue="Value"
/>
);
}
С помощью MN ради кастомизации элементов Нам придется заморачиваться меньше:
const React = require('react');
const {render} = require('react-dom');
const TextField = require('@material-ui/core/TextField').default;
function TextFieldGreen(props) {
return (
<TextField
{...props}
className={`
cGreen>label.*-focused
bcRed>.*OutlinedInput-root>fieldset
bcYellow>.*OutlinedInput-root:h>fieldset
bcGreen>.*OutlinedInput-root.*-focused>fieldset
` + (props.className || '')}
/>
);
}
function App() {
return (
<TextFieldGreen
label="Label"
required
defaultValue="Value"
/>
);
}
Если Нам понадобится кастомизировать значительно большее количество атрибутов, то подход JSS будет разительно более громоздким, чем подход MN.
Так в Нашем случае выглядит сгенерированный CSS:
.cGreen\>label\.\*-focused label[class*=-focused] {
color: green;
}
.bcRed\>\.\*OutlinedInput-root\>fieldset [class*=OutlinedInput-root] fieldset {
border-color: red;
}
.bcYellow\>\.\*OutlinedInput-root\:h\>fieldset [class*=OutlinedInput-root]:hover fieldset {
border-color: yellow;
}
.bcGreen\>\.\*OutlinedInput-root\.\*-focused\>fieldset [class*=OutlinedInput-root][class*=-focused] fieldset {
border-color: green;
}
Если возникает необходимость, селекторы подстрок можно использовать в странных и диковинных случаях:
<ul class="cRed>#*menu-item-">
<li id="menu-item-1">...</li>
<li id="menu-item-2">...</li>
<li id="menu-item-3">...</li>
<li id="menu-item-4">...</li>
...
</ul>
.cRed\>\#\*menu-item- [id*=menu-item-] {
color: red;
}
Заключение
Как можно видеть, MN больше ориентирован на методологию Atomic / Functional CSS.
Сейчас в ходу Scoped styles, JSS, PostCSS с BEM. Возможно и MN станет популярным где-то в свои 2050-е годы.
После MN Вам уже не захочется возвращаться к прежнему, и использование каких-либо иных классических способов покажется довольно утомительным и нелепым занятием. Ваши плечи будут тяжелеть от просьб написать CSS руками, ибо это будет также странно, как носить от ручья воду в бидонах, имея при этом под носом чистую воду из крана — хотя, конечно, подобные расточительные глупости вполне себе в норме для Нашего общества, но тем не менее...
Ссылки
Библиотека MN
MN get started example
===========
Источник:
habr.com
===========
Похожие новости:
- [Open source, PHP, JavaScript] readable — еще один линтер для PHP
- [CSS, HTML, Веб-дизайн, Разработка веб-сайтов] Numl – Альтернативный язык разметки и стилизации для веб
- [JavaScript, Разработка веб-сайтов] Интеграция ЭЦП НУЦ РК в информационные системы на базе веб технологий
- [Разработка веб-сайтов, JavaScript, Node.JS] Управление зависимостями JavaScript
- [Firefox, JavaScript, Python, Реверс-инжиниринг, Системы обмена сообщениями] Магия WebPush в Mozilla Firefox. Взгляд изнутри
- [JavaScript, Программирование, Учебный процесс в IT] Двоичное кодирование вместо JSON (перевод)
- [Разработка веб-сайтов, Программирование, HTML] Что делать, если брать фронтенд-фреймворк – это излишество
- [Canvas, JavaScript, WebGL, Математика, Работа с 3D-графикой] Canvas и геометрия. Это почти просто
- [Разработка веб-сайтов, JavaScript] Самый sexy framework для веб-приложений
- [JavaScript, Игры и игровые приставки, Разработка веб-сайтов, Социальные сети и сообщества] Программист создал аналог Club Penguin для взрослых, где можно одновременно общаться как в Zoom и играть
Теги для поиска: #_css, #_javascript, #_css, #_atomic_css, #_functional_css, #_inline_styles_2.0, #_mn, #_preprocessor, #_css_zero, #_responsive, #_adaptive, #_css, #_javascript
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:06
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Minimalist Notation (MN) (минималистическая нотация) — гибкая адаптивная технология генерации стилей. Она генерирует стили только для существующих классов разметки html, jsx, и т.п. — благодаря чему отпадает необходимость заботиться о компонентом подходе в CSS, мёртвом CSS коде, и отпадает необходимость писать CSS код вообще. В ней поддерживаются брейкпонты (медиа-запросы), селекторы, приоритеты, группировки, необходимые автопрефиксы и полифилы. Применение этой технологии похоже на использование инлайновых стилей, только с гораздо более выразительным синтаксисом и множеством дополнительных возможностей, поэтому MN можно даже назвать технологией inline styles 2.0. MN подразумевает не только правила нотации как таковые, но и, по-сути, даже усовершенствованный альтернативный синтаксис каскадного языка установки стилей, который ориентирован на инлайновое применение. Я разработал MN в 2017 году, и с того момента успел во многом усовершенствовать эту технологию, добавить достаточно пресетов и удобных инструментов для её изучения. Если кто-то желает узнать о том, как мне пришло в голову разработать собственную технологию, я оставляю ссылку на свой репозиторий, дабы не плагиатить у самого себя. Технология MN имеет обширную функциональность. Для многих она является новой, и почти не имеет других первоисточников для изучения, кроме этой статьи, поэтому предупреждаю, что ниже будет МНОГО БУКВ и примеров кода. Начнём с более менее общеизвестных функциональностей, которые имеются и у ближайшего аналога этой технологии, параллельно проводя сравнение. Лучший способ что-то объяснить — это показать наглядные примеры. Начнём с сравнения Minimalist-Notation с более менее известными функциональностями его первого аналога: Atomizer. Актуальные на момент написания статьи версии пакетов:
Проверить примеры ниже с Atomizer можно будет здесь: https://pankajparashar-zz.github.io/atomizer-web/ Проверить примеры ниже с MN можно можно будет здесь: https://viewer.minimalist-notation.org/ Документация по всем предустановленным пресетам MN здесь: https://styles.minimalist-notation.org/ Atomizer vs MN. Простые примеры## Atomizer <div class="D(f) Jc(c) Ai(c) H(100%) Fz(20vh) C(#0f0) C(#F00) Bgc(#f00) M(-10px) P(10px) Pstart(10%) Pos(a)">...</div>
.D\(f\) {
display: flex; } .Jc\(c\) { justify-content: center; } .Ai\(c\) { align-items: center; } .H\(100\%\) { height: 100%; } .Fz\(20vh\) { font-size: 20vh; } .C\(\#0f0\) { color: #0f0; } .Bgc\(\#f00\) { background-color: #f00; } .M\(-10px\) { margin: -10px; } .P\(10px\) { padding: 10px; } .Pstart\(10\%\) { padding-left: 10%; } .Pos\(a\) { position: absolute; } На что я обратил внимание в этом примере с Atomizer:
Minimalist-Notation <div class="dF jcC aiC h f20vh c0F0 bgcF00 m-10 p10 pl10% abs">...</div>
.dF {
display: flex; } .jcC { justify-content: center; } .aiC { align-items: center; } .h { height: 100%; } .f20vh { font-size: 20vh; } .c0F0 { color: #0f0; } .bgcF00 { background-color: #f00; } .m-10 { margin: -10px; } .p10 { padding: 10px; } .pl10\% { padding-left: 10%; } .abs { position: absolute; } Отличия:
Более того, решетка (#) является служебным символом, необходимым для полноценных селекторов, о которых Вы узнаете ниже. Если Вы добавите решетку, то получите не то, чего, вероятно, ожидаете: <div class="c#0F0">...</div>
.c\#0F0#0F0 {color: #000}
В некоторых случаях решетка всё же нужна, например, для свойства outline. Тогда служебные символы в нотации нужно экранировать: <div class="ol_thick_double_\#32a1ce">...</div>
.ol_thick_double_\\\#32a1ce {outline: thick double #32a1ce}
В MN для дефолтных обработчиков (c, bc, olc, fill, stroke, bg, bgc, temc, tdc) цвета можно задавать разными вариациями символов — об этом подробнее будет ниже.
Вы имеете возможность полностью кастомизировать MN под свои нужды, путем создания собственных обработчиков: // для свойcтва "padding"
mn('p', (params) => { return { style: { padding: (params.num || '0') + (params.unit || 'px'), }, }; }); // для свойcтва "padding-left" mn('pl', (params) => { return { style: { paddingLeft: (params.num || '0') + (params.unit || 'px'), }, }; }); // для свойcтва "color" mn('c', (params) => { return !params.negative && { style: { color: mn.utils.color(params.value || '0'), }, }; }, '^(([A-Z][a-z][A-Za-z]+):camel|([A-F0-9]+):color):value(.*)?$'); Примеры обработчиков MN можно подсмотреть в репозитории: https://github.com/mr-amirka/minimalist-notation/presets Как за себя говорит само название технологии, минимализм — здесь главное, поэтому нотация устроена таким образом, чтобы Нам не требовалось писать много кода и осуществлять прочие избыточные телодвижения. Примеры немного сложнее Atomizer vs MN. Флаг !important Atomizer <div class="D(f)! C(#0f0)!">...</div>
.D\(f\)\! {
display: flex !important; } .C\(\#0f0\)\! { color: #0f0 !important; } Minimalist-Notation <div class="dF-i c0F0-i">...</div>
.dF-i {
display: flex!important; } .c0F0-i { color: #0f0!important; } В MN для установки флага !important не используется символ !, потому-что этот служебный символ уже применяется в нотации для реверсирования подстрок. Группировки и реверсирование подстрок (!) Возьмем такую запись: <div class="cF:hover>.item">...</div>
.cF\:hover\>\.item:hover .item {
color: #fff; } Если Мы добавим символ ! после :hover, то получим следующее: <div class="cF:hover!>.item">...</div>
.cF\:hover\!\>\.item .item:hover {
color: #fff; } Здесь псевдоселектор hover переместился от родительского элемента к дочернему. Этот приведенный для наглядности способ использования реверсирования в данном случае бесполезен, ибо такого же эффекта Мы можем добиться и другим способом: <div class="cF>.item:hover">...</div>
.cF\>\.item\:hover .item:hover {
color: #fff; } Однако реверсирование может оказаться весьма полезным для сокращения записи в комбинации с группировками. Допустим, что Мы верстаем какой-то простой сайт без шаблонизаторов и без React компонентов — возможно, это лендинг. Мы имеем некоторый список, в котором располагаются одинаковые дочерние элементы: <ul class="m p dBlock">
<li class="mb5 dBlock"> <a class="c0 c0:hover tdNone p5">...</a> </li> <li class="mb5 dBlock"> <a class="c0 c0:hover tdNone p5">...</a> </li> <li class="mb5 dBlock"> <a class="c0 c0:hover tdNone p5">...</a> </li> ... </ul> .m {
margin: 0; } .p { padding: 0; } .dBlock { display: block; } .mb5 { margin-bottom: 5px; } .c0,.c0\:hover:hover { color: #000; } .tdNone { text-decoration: none; } .p5 { padding: 5px; } Мы понимаем, что указывать классы для каждого элемента может быть довольно утомительно, особенно если в процессе Мы будем что-то изменять. Мы могли бы написать код гораздо короче, и таким образом, чтобы в последующем при необходимости Нам приходилось вносить меньше правок. Рассмотрим по шагам различные способы. Вынесем всю нотацию в родительский элемент: <ul class="m p dBlock mb5>li dBlock>li c0>a c0>a:hover tdNone>a p5>a">
<li> <a>...</a> </li> <li> <a>...</a> </li> <li> <a>...</a> </li> ... </ul> Это выглядит прикольно, но если свойств и элементов будет довольно много, или если селекторы дочерних элементов будут более длинные, то получится довольно громоздко и мало читаемо. Для примера Мы добавим ещё один уровень списка, который имеет отступ в 10 пикселей слева, а ссылки внутри него имеют подчеркивание и красный цвет. При наведении курсора на ссылку, она становится зеленой, а при нажатии — становится синей. <ul class="
m p dBlock mb5>li dBlock>li p5>a tdNone>1li>1a c0>1li>1a c0>1li>1a:hover m>ul p>ul dBlock>ul pl10>1li>1ul tdUnderline>1li>1ul>1li>1a cRed>1li>1ul>1li>1a cGreen>1li>1ul>1li>1a:hover cBlue>1li>1ul>1li>1a:active "> <li> <a>...</a> </li> <li> <a>...</a> </li> <li> <a>...</a> <ul> <li> <a>...</a> </li> ... </ul> </li> ... </ul> Число перед селектором (1) просто указывавет на строго определенную глубину вложенности элемента. Кстати, обратите внимание: селекторы с одинаковыми значениями свойств в сгенерированном CSS коде группируются вместе — что позволяет минимизировать его объём на выходе. Думаю, этого для наглядности Нам пока хватит. Теперь попробуем группировки: <ul class="
(m|p|dBlock)(|>ul) (mb5|dBlock)>li p5>a tdNone>1li>1a c0>1li>1a(|:hover) pl10>1li>1ul (tdUnderline|cRed)>1li>1ul>1li>1a cGreen>1li>1ul>1li>1a:hover cBlue>1li>1ul>1li>1a:active "> <li> <a>...</a> </li> <li> <a>...</a> </li> <li> <a>...</a> <ul> <li> <a>...</a> </li> ... </ul> </li> ... </ul> Здесь группировки Нам не сильно помогли… Вот теперь попробуем ещё и реверсирование: <ul class="
(m|p|dBlock)(|>ul) (mb5|dBlock)>li p5>a (tdNone|c0(|:hover!))>1li>1a pl10>1li>1ul (tdUnderline|c(Red|Green:hover!|Blue:active!))>1li>1ul>1li>1a "> <li> <a>...</a> </li> <li> <a>...</a> </li> <li> <a>...</a> <ul> <li> <a>...</a> </li> ... </ul> </li> ... </ul> Забавно, не правда ли? Хорошо. Слегка поиграем с вложенностями и заменим имена псевдоклассов и значений на краткие синонимы: <ul class="
(m|p|dB)(|>ul) (mb5|dB)>li p5>a (tdN|c0(|:h!))>2a pl10>2ul (tdU|c(Red|Green:h!|Blue:a!))>4a "> <li> <a>...</a> </li> <li> <a>...</a> </li> <li> <a>...</a> <ul> <li> <a>...</a> </li> ... </ul> </li> ... </ul> Всё-таки, для пущей наглядности заменю имена тегов на классы: <ul class="
ListA (m|p|dB)(|.ListA) pl10>2.ListA (mb5|dB)>.ListA__Item (p5>|(tdN|c0(|:h!))>2|(tdU|c(Red|Green:h!|Blue:a!))>4).ListA__Link "> <li class="ListA__Item"> <a class="ListA__Link">...</a> </li> <li class="ListA__Item"> <a class="ListA__Link">...</a> </li> <li class="ListA__Item"> <a class="ListA__Link">...</a> </li> <li> <a class="ListA__Link">...</a> <ul class="ListA"> <li class="ListA__Item"> <a class="ListA__Link">...</a> </li> ... </ul> </li> ... </ul> Извратились Мы знатно! Однако, рекомендую, друзья мои, этими группировками сильно не увлекаться! Здесь у некоторых может возникнуть вопрос, как же Нам решить проблему с неодноранговастью сгенерированных селекторов? — ибо, если Мы захотим кастомизировать отдельные элементы из этого списка, нам могут помешать грабли перекрывающих стилей. Ответ: Мы, например, можем просто именовать классы дочерних уровней немного иначе и не указывать в нотации глубину их вложенности, дабы селекторы всех уровней имели равный приоритет: <ul class="
ListA (m|p|dB)(|.ListA) (mb5|dB)>.ListA__Item p5>.ListA__Link (tdN|c0(|:h!))>.ListA__Link_level1 pl10>.ListA_level2 (tdU|c(Red|Green:h!|Blue:a!))>.ListA__Link_level2 bgF88>.ListA__Item_substyle "> <li class="ListA__Item ListA__Item_level1 ListA__Item_substyle"> <a class="ListA__Link ListA__Link_level1">...</a> </li> <li class="ListA__Item ListA__Item_level1"> <a class="ListA__Link ListA__Link_level1">...</a> </li> <li class="ListA__Item ListA__Item_level1"> <a class="ListA__Link ListA__Link_level1">...</a> </li> <li> <a class="ListA__Link">...</a> <ul class="ListA ListA_level2"> <li class="ListA__Item ListA__Item_level2"> <a class="ListA__Link ListA__Link_level2">...</a> </li> <li class="ListA__Item ListA__Item_level2 ListA__Item_substyle"> <a class="ListA__Link ListA__Link_level2">...</a> </li> ... ... </ul> </li> ... </ul> Многие могут заметить, что здесь Мы имеем возможность использовать сложные селекторы, которые потенциально порождают нежелательные сайд-эффекты с перекрытиями стилей. Да. Это так. И это, отчасти, противоречит самой методологии Atomic CSS. Тем не менее, есть несколько нюансов:
Atomizer vs MN. Псевдоклассы и псевдоэлементы Чисто ради сравнения, рассмотрим доступную в Atomizer возможность использования псевдоклассов и псевдоэлементов: <div class="Bgc(#fff):h C(blue):a C(#0f0):hover D(n)::b">...</div>
.Bgc\(\#fff\)\:h:hover {
background-color: #fff; } .C\(blue\)\:a:active { color: blue; } .C\(\#0f0\)\:hover:hover { color: #0f0; } .D\(n\) { display: none; } Мы видим, что Atomizer позволяет юзать как некоторые полные имена псевдоклассов, так и их короткие синонимы. В документации написано, что можно испозовать псевдоэлементы. Например: b для ::before Но на практике, возможно, я как-то сильно тупил, и как ни пытался пробовать разные варианции, ни один пример не сработал. Для D(n)::b я ожидал получить следующее: .D\(n\)\:\:::before {
display: none; } В MN же Мы можем всё тоже самое: <div class="bgcF:h cBlue:a c0F0:hover dN::before">...</div>
.bgcF\:h:hover {
background-color: #fff; } .cBlue\:a:active { color: blue; } .c0F0\:hover:hover { color: #0f0; } .dN\:\:before::before { display: none; } Помимо этого, Мы имеем возможность задавать вообще любые псевдоклассы и севдоэлементы известные и неизвестные в CSS: <div class="bgcF:hz cBlue::placeholder c0F0::-webkit-input-placeholder bgE:odd bt:first c:i">...</div>
.bgcF\:hz:hz {
background-color: #fff; } .cBlue\:\:placeholder::placeholder { color: blue; } .c0F0\:\:-webkit-input-placeholder::-webkit-input-placeholder { color: #0f0; } .bgE\:odd:nth-child(2n+1) { background: #eee; } .bt\:first:first-child { border-top-width: 0; } .c\:i::placeholder { color: #000; } .c\:i:-ms-input-placeholder { color: #000; } .c\:i::-moz-placeholder { color: #000; } .c\:i::-webkit-input-placeholder { color: #000; } В MN Мы можем параметризовать псевдоклассы: <div class="c:not[.anyClass] bg0A:not[tag[attr=value].class\:pseudo] c88F4:n[13] c01:n[3n+1]">...</div>
.c\:not\[\.anyClass\]:not(.anyClass) {
color: #000; } .bg0A\:not\[tag\[attr\=value\]\.class\\\:pseudo\]:not(tag[attr=value].class:pseudo) { background: #000; background: rgba(0,0,0,.67); } .c88F4\:n\[13\]:nth-child(13) { color: #88f; color: rgba(136,136,255,.27); } .c01\:n\[3n\+1\]:nth-child(3n+1) { color: #000; color: rgba(0,0,0,.07); } В MN Мы также можем указать несколько псевдоклассов подряд: <input
type="checkbox" class="mh20:not[.anyClass]:n[5n+2]:c:h" /> .mh20\:not\[\.anyClass\]\:n\[5n\+2\]\:c\:h:not(.anyClass):nth-child(5n+2):checked:hover {
margin-left: 20px; margin-right: 20px; } В MN можно задать собственный набор синонимов для псевдоклассов. Пример: mn.utils.extend(mn.states, {
foo: [':active'], bar: ['.Bar_active', '.otherSelector'], vasya: ['[data-name=vasya]'], }); <div class="cRed:foo cGreen:bar cBlue:vasya">...</div>
.cRed\:foo:active {
color: red; } .cGreen\:bar.otherSelector,.cGreen\:bar.Bar_active { color: green; } .cBlue\:vasya[data-name=vasya] { color: blue; } Atomizer vs MN. Комбинаторы Сравним кабинаторы. "The underscore character ( _ )" в Atomizer: <div class="foo">
<div class="foo_D(n)"></div> </div> Аналог в MN: <div class="foo">
<div class="dN<.foo"></div> </div> "The right angle bracket character ( > )" в Atomizer: <div class="foo">
<div class="foo>D(n)"></div> </div> Аналог в MN: <div class="foo">
<div class="dN<1.foo"></div> </div> "The plus sign ( + )" в Atomizer: <div class="foo"></div>
<div class="foo+D(n)"></div> Аналог в MN: <div class="foo"></div>
<div class="dN<.foo+"></div> Помимо этого в MN комбинатор "соседний брат" ( + ) можно использовать в обратном направлении: <div class="dN+.foo"></div>
<div class="foo"></div> В MN можно использовать комбинатор "общий брат" ( ~ ): <div class="foo"></div>
<div class="dN<.foo~"></div> <div class="dN<.foo~"></div> .foo~ .dN\<\.foo\~ {
display: none; } И в обратном направлении: <div class="dN~.foo"></div>
<div class="foo"></div> <div class="foo"></div> .dN\~\.foo~.foo {
display: none; } Atomizer vs MN. Контекст В Atomizer есть функциональность, называемая Context class, которая используется в случаях, когда необходимо установить некоторое поведение стилей при изменении селектора на родительском элементе. Пример: <div class="foo bar any">
<div class="double"> <div class="foo_D(n) bar:h_D(n) any_D(n):h any_double_D(n)">...</div> </div> </div> .foo .foo_D\(n\), .any_double .any_double_D\(n\) {
display: none; } .bar:hover .bar\:h_D\(n\) { display: none; } .any .any_D\(n\)\:h:hover { display: none; } На что я обратил внимание в этом примере:
В MN, как, вероятно, некоторые могли догадаться из примеров выше, можно юзать полноценные селекторы, но для начала давайте рассмотрим, каким образом можно сделать тоже самое: <div class="foo bar any">
<div class="double"> <div class="dN<.foo dN<.bar:h dN:h<.any dN<.double<.any">...</div> </div> </div> .foo .dN\<\.foo,.bar:hover .dN\<\.bar\:h,.any .dN\:h\<\.any:hover,.any .double .dN\<\.double\<\.any {
display: none; } При необходимости можно задать строгую глубину вложенности: <div class="any">
<div class="double"> <div class="dN<1.double<1.any dN<2.any">...</div> </div> </div> .any>.double>.dN\<1\.double\<1\.any,.any>*>.dN\<2\.any {
display: none; } Аналогичным образом, используя стрелочки направленные в противоположную сторону, можно задавать стили дочерним элементам: <div class="dN>2.double">
<div class="any"> <div class="double">...</div> </div> </div> .dN\>2\.double>*>.double {
display: none; } Можно комбинировать родительские селекторы с дочерними: <div class="parent">
<div class="dN<.parent>2.double"> <div class="any"> <div class="double">...</div> </div> </div> </div> .parent .dN\<\.parent\>2\.double>*>.double {
display: none; } <div class="dN>.double<.any">
<div class="any"> <div class="double">...</div> </div> </div> .dN\>\.double\<\.any .any .double {
display: none; } Можно обозначить поведение стиля при наличии произвольного селектора на текущем элементе: <div class="bgF2.active[data-name=Gena]:h active" data-name="Gena">...</div>
<div class="dN#feedback" id="feedback">...</div> <div class="o50.disable disable">...</div> .bgF2\.active\[data-name\=Gena\]\:h.active[data-name=Gena]:hover {
background: #fff; background: rgba(255,255,255,.13); } .dN\#feedback#feedback { display: none; } .o50\.disable.disable { opacity: .5; } Отличия:
Atomizer vs MN. Вычисляемые значения В Atomizer есть возможность использования вычисляемых значений. Для некоторых свойств Мы можем использовать дроби: <div class="W(1/2) P(1/3) M(1/4) Start(1/5) T(1/6) Pstart(1/7) Miw(1/8)">...</div>
.W\(1\/2\) {
width: 50%; } .P\(1\/3\) { padding: 33.3333%; } .M\(1\/4\) { margin: 25%; } .Start\(1\/5\) { left: 20%; } .T\(1\/6\) { top: 16.6667%; } .Pstart\(1\/7\) { padding-left: 14.2857%; } .Miw\(1\/8\) { min-width: 12.5%; } В MN Мы также можем использовать вычисляемые значения, в том числе и дроби: <div class="w1/2 p1/3 m1/4 sl1/5 st1/6 pl1/7 wmin1/8">...</div>
.w1\/2 {
width: 50%; } .p1\/3 { padding: 33.33%; } .m1\/4 { margin: 25%; } .sl1\/5 { left: 20%; } .st1\/6 { top: 16.66%; } .pl1\/7 { padding-left: 14.28%; } .wmin1\/8 { min-width: 12.5%; } Однако в Atomizer почему-то нельзя вычесть или сложить значение с дробью. Я проверил: <div class="W(1/2-10) P(1/3+5)">...</div>
.W\(1\/2-10\) {
width: 50%; } В MN Мы можем вычитать и складывать значения с дробью, и это для некоторых случаяев весьма полезная возможность. Пример: <div class="w1/2-10 p1/3\+5">...</div>
.w1\/2-10 {
width: calc(50% - 10px); } .p1\/3\\\+5 { padding: calc(33.33% + 5px); } Замечания:
Atomizer vs MN. Установка цветов В Atomizer для указания цвета используются шестнадцатеричные цвета из 3-х или 6-ти символов с префиксом # в качестве идентификатора значения. Шестнадцатеричные значения для цветов должны быть написаны в нижнем регистре (т.е., #ccc, а не #CCC). Для установки коэффициента непрозрачности к шестнадцатеричному значению необходимо добавлять точку(.) с десятичным значением. Например: <div class="C(#fff) Bdc(#ff0000) Bgc(#00ff00.5)">...</div>
.C\(\#fff\) {
color: #fff; } .Bdc\(\#ff0000\) { border-color: #ff0000; } .Bgc\(\#00ff00\.5\) { background-color: rgba(0,255,0,.5); } В MN для указания цвета используются шестнадцатеричные цвета от 0 до 8-ми символов с коэффициентом непрозрачности включительно, но для коэффициента непрозрачности дополнительно имеется альтернативный способ: использовать точку(.) с десятичным значением. Например: <div class="c cFFF bcFF0000 bgc00FF00\.5 сFF00008 сFF000080 cF bgc08 bgc0\.5 bgc1234 bgc12348">...</div>
.c {
color: #000; } .cFFF { color: #fff; } .bcFF0000 { border-color: #f00; } .bgc00FF00\\\.5 { background-color: #0f0; background-color: rgba(0,255,0,.5); } .cFF00008 { color: #f00; color: rgba(255,0,0,.53); } .cFF000080 { color: #f00; color: rgba(255,0,0,.5); } .cF { color: #fff; } .bgc08 { background-color: #000; background-color: rgba(0,0,0,.53); } .bgc0\\\.5 { background-color: #000; background-color: rgba(0,0,0,.5); } .bgc1234 { background-color: #123; background-color: rgba(17,34,51,.27); } .bgc12348 { background-color: #123; background-color: rgba(17,34,51,.28); } Замечания:
Градиентная цветовая заливка в MN В MN Мы имеем два похожих пресета, которые, как известно, на практике часто могут быть взаимозаменяемыми: bgc и bg. Пример: <div class="bg48A">...</div>
<div class="bgc48A">...</div> .bg48A {
background: #48a; } .bgc48A { background-color: #48a; } Однако с bg Мы также можем легко и лакончино делать градиенты, путем указания последовательности цветов через знак минуса (-): <div class="bg0-F">...</div>
<div class="bgF00-0F0-00F">...</div> .bg0-F {
background: #000; background: linear-gradient(180deg,#000 0%,#fff 100%); } .bgF00-0F0-00F { background: #f00; background: linear-gradient(180deg,#f00 0%,#0f0 50%,#00f 100%); } Имеется возможность устанвавливать направление градиента: <div class="bg0-F_g45">...</div>
<div class="bg0-F_g90">...</div> .bg0-F_g45 {
background: #000; background: linear-gradient(225deg,#000 0%,#fff 100%); } .bg0-F_g90 { background: #000; background: linear-gradient(270deg,#000 0%,#fff 100%); } Помимо линейных градиентов, Мы можем использовать радиальные градиенты: <div class="bg0-F_r">...</div>
<div class="bg0-F_r_closestSide">...</div> <div class="bg0-F_r_ellipse_at_top">...</div> .bg0-F_r {
background: #000; background: radial-gradient(circle,#000 0%,#fff 100%); } .bg0-F_r_closestSide { background: #000; background: radial-gradient(closest-side,#000 0%,#fff 100%); } .bg0-F_r_ellipse_at_top { background: #000; background: radial-gradient(ellipse at top,#000 0%,#fff 100%); } Atomizer vs MN. Несколько значений В Atomizer есть возможность указывать несколько значений, разделенных запятыми, когда это поддерживается соответствующими свойствами, например: <div class="Bgp(20px,50px)">...</div>
.Bgp\(20px\,50px\) {
background-position: 20px 50px; } С MN получить аналогичный результат можно похожим образом: <div class="bgp20px_50px">...</div>
.bgp20px_50px {
background-position: 20px 50px; } Atomizer vs MN. Идентификатор брейкпоинта (breakpoint identifier). Медиа-запросы В Atomizer можно устанавливать брейкпоинт к правилу, путем добавления суффикса к записи. Брейкпоинт указывает, что это правило вступит в силу только в рамках медиа-запроса. Значения имени и длины каждой точки останова определяются в объекте конфигурации: {
// ... breakPoints: { 'sm': '@media(min-width:750px)', // breakpoint 1 'md': '@media(min-width:1000px)', // breakpoint 2 'lg': '@media(min-width:1200px)', // breakpoint 3 // ... }, // ... } Используются эти брейкпоинты следующим образом: <div class="W(50%)--sm W(33%)--md W(25%)--lg">...</div>
@media(min-width:750px) {
.W\(50\%\)--sm { width: 50%; } } @media(min-width:1000px) { .W\(33\%\)--md { width: 33%; } } @media(min-width:1200px) { .W\(25\%\)--lg { width: 25%; } } В MN также можно прекрасно устанавливать брейкпоинты, путем добавления суффикса к записи: <div class="w50%@m w33%@d w25%@d2 w1/5@ie w1/6@android cr@mouse">...</div>
@media (max-width: 992px) {
.w50\%\@m { width: 50%; } } @media (min-width: 992px) { .w33\%\@d { width: 33%; } } @media (min-width: 1200px) { .w25\%\@d2 { width: 25%; } } .ie .w1\/5\@ie { width: 20%; } .android .w1\/6\@android { width: 16.66%; } @media (pointer: fine) and (hover: hover) { .cr\@mouse { cursor: pointer; } } Здесь Мы имеем возможность для брейкпоинтов использовать как медиа-запросы, так и родительские селекторы. Такая возможность может быть очень удобна в случаях, когда Нам нужно учесть какие-либо функциональности браузера, для которых пока не предусмотрены соответсвующие медиа-запросы, либо быстро сделать полифилы для браузеров, которые вовсе не поддерживают медиа-запросы. Также возможность использования родительских селекторов в качестве брейкпоинтов, вероятно, могла бы пригодиться Нам для организации каких-нибудь контекстов или пространств имен. Пример конфигурирования брейкпоинтов: module.exports = (mn) => {
const {media} = mn; // media-queries media.m = { query: '(max-width: 992px)', priority: 0, }; media.m2 = { query: '(max-width: 768px)', priority: 1, }; media.d = { query: '(min-width: 992px)', priority: 2, }; media.d2 = { query: '(min-width: 1200px)', priority: 3, }; media.mouse = { query: '(pointer: fine) and (hover: hover)', priority: 4, }; // ... // user agents media.mozilla = { selector: '.mozilla' }; media.webkit = { selector: '.webkit' }; media.ie = { selector: '.ie' }; media.iphone = { selector: '.iphone' }; media.android = { selector: '.android' }; // ... }; Если Мы используем в нотации брейкпонт, который нигде не предустановлен, то в CSS получим значение медиа-запроса, которое непосредственно указали в записи: <div class="w50%@print w50%@any">...</div>
@media print {
.w50\%\@print { width: 50%; } } @media any { .w50\%\@any { width: 50%; } } Также помимо предустанавливаемых брейкпоинтов в нотации MN можно использовать выражения: <div class="w50%@768 w50%@768- w50%@768-992">...</div>
<div class="w50%@768-992x100-200 w50%@x100-200">...</div> <div class="w50%@x100 w50%@x100-">...</div> @media (max-width: 768px) {
.w50\%\@768 { width: 50%; } } @media (min-width: 768px) { .w50\%\@768- { width: 50%; } } @media (min-width: 768px) and (max-width: 992px) { .w50\%\@768-992 { width: 50%; } } @media (min-width: 768px) and (max-width: 992px) and (min-height: 100px) and (max-height: 200px) { .w50\%\@768-992x100-200 { width: 50%; } } @media (min-height: 100px) { .w50\%\@x100- { width: 50%; } } @media (min-height: 100px) and (max-height: 200px) { .w50\%\@x100-200 { width: 50%; } } @media (max-height: 100px) { .w50\%\@x100 { width: 50%; } } В выражениях брейкпонтов Мы можем указывать приоритет медиа-запроса, по которому каскадные блоки отсортировываются в конечном CSS: <div class="w50%@768-992^5 w50%@768^1 w50%@992^3">...</div>
@media (max-width: 768px) {
.w50\%\@768\^1 { width: 50%; } } @media (max-width: 992px) { .w50\%\@992\^3 { width: 50%; } } @media (min-width: 768px) and (max-width: 992px) { .w50\%\@768-992\^5 { width: 50%; } } Minimalist Notation. Управление приоритетами стилей В MN имеется специальная удобная возможность управления приоритетами стилей. Мы можем повысить приоритет стилей путем добавления суффикса * со значением приоритета в конец записи нотации, например: <div class="cF*2 c0*3 cF00.active*2">...</div>
.cF\*2.cF\*2 {
color: #fff; } .c0\*3.c0\*3.c0\*3 { color: #000; } .cF00\.active\*2.cF00\.active\*2.active { color: #f00; } Таким образом, приоритет или, другими словами, специфичность стилей увлеличивается за счёт повторения в каскаде генерируемого CSS комбинации класса с самим собой столько раз, сколько Мы указываем в нотации. Minimalist Notation. Селектор подстроки С MN можно позволить себе сокращать имена классов в нотации. Иногда достаточно только фрагмента имени: <div class="SomeBlock">
<div class="bgF00>1.*_active"> <div class="SomeBlock__SomeElement SomeBlock__SomeElement_active"> ... </div> <div class="SomeBlock__SomeElement"> ... </div> <div class="SomeBlock__SomeElement SomeBlock__SomeElement_active"> ... </div> </div> </div> .bgF00\>1\.\*_active>[class*=_active] {
background: #f00; } Например, в приложении на React Мы используем компоненты библиотеки Material-UI, которые легально кастомизируются через JSS так: const React = require('react');
const {render} = require('react-dom'); const {withStyles} = require('@material-ui/core/styles'); const TextField = require('@material-ui/core/TextField').default; const TextFieldGreen = withStyles({ root: { '& label.Mui-focused': { color: 'green', }, '& .MuiOutlinedInput-root': { '& fieldset': { borderColor: 'red', }, '&:hover fieldset': { borderColor: 'yellow', }, '&.Mui-focused fieldset': { borderColor: 'green', }, }, }, })(TextField); function App() { return ( <TextFieldGreen label="Label" required defaultValue="Value" /> ); } С помощью MN ради кастомизации элементов Нам придется заморачиваться меньше: const React = require('react');
const {render} = require('react-dom'); const TextField = require('@material-ui/core/TextField').default; function TextFieldGreen(props) { return ( <TextField {...props} className={` cGreen>label.*-focused bcRed>.*OutlinedInput-root>fieldset bcYellow>.*OutlinedInput-root:h>fieldset bcGreen>.*OutlinedInput-root.*-focused>fieldset ` + (props.className || '')} /> ); } function App() { return ( <TextFieldGreen label="Label" required defaultValue="Value" /> ); } Если Нам понадобится кастомизировать значительно большее количество атрибутов, то подход JSS будет разительно более громоздким, чем подход MN. Так в Нашем случае выглядит сгенерированный CSS: .cGreen\>label\.\*-focused label[class*=-focused] {
color: green; } .bcRed\>\.\*OutlinedInput-root\>fieldset [class*=OutlinedInput-root] fieldset { border-color: red; } .bcYellow\>\.\*OutlinedInput-root\:h\>fieldset [class*=OutlinedInput-root]:hover fieldset { border-color: yellow; } .bcGreen\>\.\*OutlinedInput-root\.\*-focused\>fieldset [class*=OutlinedInput-root][class*=-focused] fieldset { border-color: green; } Если возникает необходимость, селекторы подстрок можно использовать в странных и диковинных случаях: <ul class="cRed>#*menu-item-">
<li id="menu-item-1">...</li> <li id="menu-item-2">...</li> <li id="menu-item-3">...</li> <li id="menu-item-4">...</li> ... </ul> .cRed\>\#\*menu-item- [id*=menu-item-] {
color: red; } Заключение Как можно видеть, MN больше ориентирован на методологию Atomic / Functional CSS. Сейчас в ходу Scoped styles, JSS, PostCSS с BEM. Возможно и MN станет популярным где-то в свои 2050-е годы. После MN Вам уже не захочется возвращаться к прежнему, и использование каких-либо иных классических способов покажется довольно утомительным и нелепым занятием. Ваши плечи будут тяжелеть от просьб написать CSS руками, ибо это будет также странно, как носить от ручья воду в бидонах, имея при этом под носом чистую воду из крана — хотя, конечно, подобные расточительные глупости вполне себе в норме для Нашего общества, но тем не менее... Ссылки Библиотека MN MN get started example =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:06
Часовой пояс: UTC + 5