Atomizer vs Minimalist Notation (MN)
- понедельник, 13 июля 2020 г. в 00:24:26
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
<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
под свои нужды, путем создания собственных обработчиков:
// для свойства "padding"
mn('p', (params) => {
return {
style: {
padding: (params.num || '0') + (params.unit || 'px'),
},
};
});
// для свойства "padding-left"
mn('pl', (params) => {
return {
style: {
paddingLeft: (params.num || '0') + (params.unit || 'px'),
},
};
});
// для свойства "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
<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>
.m {
margin: 0;
}
.p {
padding: 0;
}
.dBlock,.dBlock\>li li {
display: block;
}
.mb5\>li li {
margin-bottom: 5px;
}
.c0\>a a,.c0\>a\:hover a:hover {
color: #000;
}
.tdNone\>a a {
text-decoration: none;
}
.p5\>a a {
padding: 5px;
}
Это выглядит прикольно, но если свойств и элементов будет довольно много, или если селекторы дочерних элементов будут более длинные, то получится довольно громоздко и мало читаемо.
Для примера Мы добавим еще один уровень списка, который имеет отступ в 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
возможность использования псевдоклассов и псевдоэлементов:
<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;
}
Сравним кабинаторы.
"The underscore character ( _
)" в Atomizer
:
<div class="foo">
<div class="foo_D(n)"></div>
</div>
Аналог в MN
:
<div class="foo">
<div class="dN<.foo"></div>
</div>
.foo .dN\<\.foo {
display: none;
}
"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>
.foo>.dN\<1\.foo {
display: none;
}
"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>
.foo+ .dN\<\.foo\+ {
display: none;
}
В 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
есть функциональность, называемая 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)
;В 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
есть возможность использования вычисляемых значений.
Для некоторых свойств Мы можем использовать дроби:
<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
для указания цвета используются шестнадцатеричные цвета из 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, поэтому он экранируется слэшем (\
). altColor: 'off'
В 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%);
}
Имеется возможность устанавливать позиции переходов градиента:
<div class="bgF00-0F0p77%-00Fp90%">...</div>
.bgF00-0F0p77\%-00Fp90\% {
background: linear-gradient(180deg,#f00 0%,#0f0 77%,#00f 90%);
}
В 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
можно устанавливать брейкпоинт к правилу, путем добавления суффикса к записи. Брейкпоинт указывает, что это правило вступит в силу только в рамках медиа-запроса.
Значения имени и длины каждой точки останова определяются в объекте конфигурации:
{
// ...
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%;
}
}
В 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 комбинации класса с самим собой столько раз, сколько Мы указываем в нотации.
С 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 руками, ибо это будет также странно, как носить от ручья воду в бидонах, имея при этом под носом чистую воду из крана — хотя, конечно, подобные расточительные глупости вполне себе в норме для Нашего общества, но тем не менее...