minimalist-notation
Version:
Minimalist Notation
385 lines (275 loc) • 28.8 kB
Markdown
[English](https://github.com/mr-amirka/minimalist-notation/blob/master/from-author.md)
# From the author
* [Начало](#Начало)
* [Минимализм](#Минимализм)
## Начало борьбы за адаптивность
Используя многие СSS-фреймворки, например, такие как Twitter Bootstrap Мы пишем html код таким образом:
```html
<div class="text-center">...</div>
```
, вместо того, чтобы сделать тоже самое через inline style:
```html
<div style="text-align: center;">...</div>
```
Практически, такой подход похож на старый “преступный” для некоторых инлайновый способ установки стилей элементам. Отличие только в немного сокращенной форме записи и приоритете стилей.
В самом деле, в подобных примерах использование классов вместо инлайновых стилей почти не имеет смысла.
Главный недостаток инлайновых стилей в том, что они весьма громоздкие и делают разметку менее читаемой.
CSS позволяет нам назначать необходимые стили какому-либо классу, а затем указывать этот класс в целевых элементах, и это позволяет Нам сократить код в разметке и немного автоматизировать установку стилей, что соответственно ускоряет Нам работу.
Также в CSS Мы можем устанавливать поведение при изменении различных селекторов и медиа-запросов.
В CSS есть несколько проблем:
* разрыв контекста, который замедляет процесс внесения правок;
* наличие лишней абстракции в виде отдельного файла со стилями;
* необходимость следить за зависимостями и подчищать их;
* неочевиднось поведения классов в разметке и необходимость каждый раз навигироваться по коду, чтобы понять их назначение;
* избыточная когнитивная нагрузка из-за необходимости придумывать новые имена классов;
* побочные эффекты и грабли от перекрывающих правил CSS - хотя этот недостаток ныне решают различные компонентные подходы;
* Много CSS-кода. Чем больше CSS-кода, тем сильнее тормозит браузер. Это обусловлено тем, что под капотом браузера каскады стилей, грубо говоря, представляют из себя ранжированный список шаблонов, с каждым из которых сопоставляется каждый элемент разметки при каждом рендеринге.
Чем более насыщено DOM-дерево и чем длиннее список каскадных правил, тем более явно торможение. Торможение особенно явно, когда применяется анимация, т.к. рендеринг анимации может приводить к увеличению частоты сопоставлений.
Бывают кейсы, когда разработчики не имеют возможности использовать современные технологии или компонентные подходы.
Со временем CSS в их проектах разрастается, и в нем накапливается помойка из неиспользуемых стилей или множество граблей из
перекрывающих друг друга правил СSS.
Как и многие другие разработчики я начинал свою эпопею адаптивной верстки с Twitter Bootstrap.
Некоторые бравировали тем, что используют в своих проектах этот "фреймоворк", но на практике же,
я часто видел, что большинство разработчиков делали дикие перекрытия поверх стандартных стилей этой библиотеки.
Во всех случаях использование Twitter Bootstrap 3, как такого, не имело большого смысла.
В моих наблюдениях, из всей помойки правил, которые есть в этой балластной библиотеке большей частью используется только адаптивная сетка.
Я сам так использовал Bootstrap первые полгода знакомства с ним.
После знакомства с препроцессором SASS, я стал самостоятельно генерировать адаптивную сетку под свои нужды.
Адаптация Bootstrap 3, по сути, распространяется только на колонную сетку для разных разрешений экрана
и некоторые предустановленные и часто невостребованные элементы.
В адаптивной сетке мы используем суффиксы в нотации классов, соответствующие определенной ширине окна браузера, а точнее медиа-запросам, такие как: -xs, -sm, -md, -lg.
Например: col-sm-6, col-md-4, col-lg-6.
Часто мне хотелось иметь в Bootstrap возможность адаптивности других актуальных стилей - классов: text-left, text-center и т.п. по аналогии, например: text-center-xs, text-left-md.
Весьма странно, что этого не было в библиотеке.
По понятным причинам подобная нотация не предусмотрена для адаптивности размеров шрифтов,
например: font-size-sm-14, font-size-lg-20.
Хотя, для моих задач подобная особенность востребована,
я понимаю, что генерация классов для всех размеров шрифтов даст много бесполезного CSS-кода.
Мой подход к верстке - это хорошо зарекомендовавший себя общий оптимальный подход большинства CSS-фреймворков,
к которому мы приходим опытным путём.
Мной он частично позаимствован у многим известного Twitter Bootstrap.
Суть подхода в том, чтобы определять в каждом CSS классе минимальный набор стилей
для учета многих распространенных ситуаций, что позволяет нам:
* не добавлять новые классы для похожих случаев;
* избежать избыточные перекрытия стилей;
* не путаться в громоздком CSS коде, тщетно резервируя в своем головном мозге
“ячейки памяти” под все эти непрерывно размножающиеся селекторы и каскады стилей.
Идея создания технологии MN возникла в тот момент, когда в процессе профессиональной деятельности
мне вошло в привычку писать CSS примерно таким образом:
```css
.p10{
padding: 10px;
}
.pl10{
padding-left: 10px;
}
.mh5{
margin-left: 5px;
margin-right: 5px;
}
(max-width: 768px){
.p10-xs{
padding: 10px;
}
.pr10-xs{
padding-right: 10px;
}
}
```
В моём коде уже присутствовала определенная нотация сопоставимая со строго определенными атрибутами стилей, их значениями и медиа-запросами.
Сначала я старался автоматизировать генерацию классов препроцессором SASS, грубо говоря, примерно таким образом:
```scss
$grids: (
// suffix, media, container width
('', all ),
('-sm', (max-width: 992px), 750px),
('-xs', (max-width: 768px), 480px),
('-xm', (max-width: 480px) ),
('-xn', (max-width: 360px) ),
('-mm', (max-width: 320px) ),
('-md', (min-width: 992px), 970px ),
('-lg', (min-width: 1200px), 1170px ),
('-ll', (min-width: 1600px), 1570px )
//,('-print', print )
);
$prefixes: ( '-o-', '-ms-', '-moz-', '-webkit-' );
cross($name, $value: ''){
$prefix in $prefixes {
#{$prefix + $name}: $value;
}
#{$name}: $value;
}
sides($name, $valueName, $suffix, $prop, $value){
#{$name}#{$valueName}#{$suffix} {
#{$prop} : $value;
}
#{$name}v#{$valueName}#{$suffix} {
#{$prop}-top : $value;
#{$prop}-bottom : $value;
}
#{$name}h#{$valueName}#{$suffix} {
#{$prop}-left : $value;
#{$prop}-right : $value;
}
#{$name}t#{$valueName}#{$suffix} {
#{$prop}-top : $value;
}
#{$name}b#{$valueName}#{$suffix} {
#{$prop}-bottom : $value;
}
#{$name}l#{$valueName}#{$suffix} {
#{$prop}-left : $value;
}
#{$name}r#{$valueName}#{$suffix} {
#{$prop}-right : $value;
}
}
clearfix(){
&:before, &:after {
content: " ";
clear: both;
display: table;
}
}
keyframes($name) {
@-o-keyframes #{$name} { ; }
@-moz-keyframes #{$name} { ; }
@-webkit-keyframes #{$name} { ; }
#{$name} { ; }
}
keyframes(spinner-animate){
from { cross(transform, rotateZ(0deg)); }
to { cross(transform, rotateZ(360deg)); }
}
.spnr {
cross(animation, spinner-animate 3s infinite linear);
}
%col{min-height: 1px;}
$suffix, $media, $container in $grids {
$i: 12;
$i > 0 {
.col#{$i}#{$suffix}{ %col; }
$i: $i - 1;
}
#{$media} {
$i: 12;
$i > 0 {
.col#{$i}#{$suffix}{
.lt#{$suffix};
width: 100% * $i / 12;
}
$i: $i - 1;
}
.cfx#{$suffix} {
clearfix();
}
$size in 5, 10, 15, 20, 25, 30 {
#{'.mh-' + $size + $suffix} {
margin-left: -$size + px;
margin-right: -$size + px;
}
}
// text-align
.tl#{$suffix}{text-align:left;}
.tc#{$suffix}{text-align:center;}
.tr#{$suffix}{text-align:right;}
// float
.lt#{$suffix}{float:left;}
.ct#{$suffix}{float:none;}
.rt#{$suffix}{float:right;}
// display
.dNone#{$suffix}{display:none;}
.dBlock#{$suffix}{display:block;}
.dInlineBlock#{$suffix}{display:inline-block;}
.dInline#{$suffix}{display:inline;}
// ...
// font-size
$size in 12, 14, 16, 18, 20, 24, 30, 32 {
#{'.f' + $size + $suffix} { font-size: $size + px; }
}
// padding
$size in 0, 5, 10, 15, 20, 25, 30 {
sides('.p', $size, $suffix, padding, $size + px)
}
// margin
$size in 0, 5, 10, 15, 20, 30 {
sides('.m', $size, $suffix, margin, $size + px);
}
// ...
}
//...
}
```
Несмотря на частичную автоматизацию, таким способом может генерироваться
много лишнего, бывает утомительно добавлять новые классы и следить за их
актуальностью.
Я задумался над разработкой своей технологии, предполагающей генерацию стилей
на основании нотации имен классов в самой разметке, которая позволила бы
значительно автоматизировать процесс разработки и улучшить качество кода.
Главная идея: генерация стилей происходит автоматически на основе атрибутов
в самой разметке - ничего лишнего!
Таким образом, у Нас не будет неиспользуемого CSS-кода, и в большинстве случаев
Мы можем вообще не писать CSS-код.
При желании в рантайм версии Мы можем даже не обращаться за CSS-файлами на
сервер - CSS распаковывается, подобно rar-архиву, из самой разметки.
За основу взят принцип формирования имени класса от атрибута стиля – это,
по сути, аналог инлайновых стилей, только более минималистичный,
т.к. я старался, сократить названия классов до аббревиатур.
Часть имени класса отвечает за атрибут стиля, часть – за его значение.
Однако этого недостаточно для адаптивности, поэтому я сделал ещё больше,
и Мы можем увидеть это из примеров приведенных в настоящей документации.
Дабы проработать все нюансы я пытался детальнее изучить известные мне существующие
на момент 2017 года подходы в разработке интерфейсов, среди которых и "методология BEM".
Мой подход позволяет уменьшить написание кода, систематизировать именование классов
и улучшить понимание их назначения в разметке.
Согласитесь, Нам в любом случае приходится писать в разметке имена классов,
и если MN с чем-то сравнивать, то например:
1. Суммарное значение атрибута ``` class ``` в Вашей разметке может быть даже
меньше, чем длинные имена классов BEM.
2. Не нужно писать CSS.
3. Имена классов почти буквально сами говорят за себя, что конкретно они делают.
4. Извечная проблема программиста: "Как назвать переменную?". В большинстве случаев Мы экономим время и умственную энергию, избавляя себя от необходимости лишний раз думать о том, как лучше назвать класс для какого-либо элемента, так как Мы руководствуемся заранее определенным шаблоном.
5. Однажды определённые эссенции и правила генерации эссенций в перспективе формируют легко расширяемую, гибкую и конечную фундаментальную базу, которая может выступать в роли общепринятого стандарта для большинства разработчиков, снижающего порог вхождения.
6. Благодаря гибкости и реактивной природе технологии, на основе базовых эссенций и правил их генерации создаётся многократно переиспользуемый код.
Методология BEM в аспектах своей громоздкости и неадаптивности несколько противоположна методологии, которой я придерживаюсь, но при необходимости её вполне можно применять совместно.
В моей нотации вместо понятия "Элемент" присутствует понятие "Эссенция стиля".
Альтернативой понятиям "Блок" и "Модификатор состояния" является "Контекст эссенции", в который входят:
* полноценная улучшенная альтернатива CSS-селекторов;
* синонимы или шаблоны медиа-запросов.
Посредством продуманного синтаксиса можно лаконично указать поведение эссенций стилей при изменении всевозможных селекторов и медиа-запросов.
Когда я создавал MN, моей целью было:
1. Автоматизировать генерацию CSS, ибо мой ручной способ написания CSS стал механическим. Если я и так добавляю новые классы для каждого отдельного значения атрибута стиля, именуя их соответствующими аббревиатурами - в чём наблюдается прямая зависимость, то почему бы мне не сделать их генерацию автоматической прямо из самой разметки, дабы больше не открывать CSS-файл? Это гениально! Это похоже на своеобразную реализацию парадигмы реактивности в CSS!
Как известно, разметка имеет смысл без CSS, но сам по себе CSS не имеет смысла без разметки. В нашем же случае всё нужное в одном месте. Разметка автономна и вся информация о её внешнем виде находится в ней самой.
В этом подходе есть нечто общее с директивным подходом Angular. Разница между чистым CSS и MN, примерно такая же как между VanillaJS и Angular, или как между jQuery и Angular. В первом случае Вы манипулируете DOM-элементами вручную, вручную прописываете в JS селекторы элементов, для которых нужно инициализировать какой-либо плагин или повесить событие. Во втором случае Вы просто пишите на нужном элементе в разметке директиву, которая инициализирует компонент или устанавливает выполняемое выражение по наступлению какого-либо события.
2. Избежать коллизий с именами классов. Предусмотрена генерация стилей для отдельных кастомных атрибутов в разметке. В нотации "нахальным образом" эксплуатируется использование служебных символов, которые, вряд ли, кто-то станет использовать добровольно, так как их ручное экранирование в CSS достаточно накладно и "напряжно" для чтения. MN позволяет Нам минимально ограничиваться в параметризации эссенций нотации. Также имеется удобная возможность при необходимости экранировать служебные символы, если они должны находится в параметрах эссенции.
3. Разработать максимально адаптивную и лаконичную систему нотации, учитывающую мутабельность стилей в зависимости от изменения состояния, атрибутов и классов как на самом элементе, так и на его родительских элементах, и мутабельность в зависимости от изменения медиа-запросов.
С технологией MN у Вас может возникнуть ощущение, что каскады стилей переместились непосредственно в саму разметку. MN выглядит как более лаконичный аналог и альтернатива инлайновых стилей со всеми возможностями обычного CSS.
Все последующие фичи добавлялись уже в процессе применения MN в моих проектах. Я добавлял то, что позволяло сократить рутинные действия с написанием классов в самой разметке:
1. Возможность указывать в нотации текущего элемента какой стиль будут иметь его дочерние элементы - это очень удобная фича, которая многократно меня выручала.
2. Сокращение записи нескольких имен классов со схожими подстроками за счет разработанной мной нотации группировки подстрок и алгоритма их парсинга;
3. Установка приоритета стилей в нотации.
Некоторые фичи были добавлены чисто экспериментально в силу того, что нечто подобное есть в других препроцессорах, а кое-что представлялось возможным и легко достижимым:
1. Наследование от эссенций стилей (расширение). Это прикольная фича. Иногда она нужна, но я не рекомендую её использование без необходимости, в связи с тем, что:
- во-первых, множественное наследование делает код сложнее для понимания;
- во-вторых, когда эссенция ``` A ``` наследует свойства эссенции ``` B ```,
селекторы ассоциированные с эссенцией ``` A ```,
также ассоциируются с эссенцией ``` B ```.
Это в большинстве случаев приводит к тому, что сгенерированный конечный CSS-код за счёт дублирования селекторов по объему значительно может превышать CSS-код, получаемый с помощью примесей.
2. Примеси эссенций. Примеси эссенций можно использовать как альтернативу наследованию эссенций, в зависимости от того, насколько это будет оптимально для текущего проекта с Вашей точки зрения. Примеси отличаются от наследования тем, что копируют непосредственно атрибуты донорских эссенций на шаге прекомпиляции эссенции, что более оптимально.
3. Ассоциация селекторов с эссенциями стилей - редко используемая, но очень удобная возможность.
4. Программная манипуляция глобальными стилями CSS в рантайме. Манипуляция глобальными стилями CSS в рантайме используется в основном как способ динамической кастомизации и альтернативного задания стилей, когда мы хотим совсем не видеть CSS-файлы в своем проекте.
Позднее я выяснил, что методология, которой я придерживаюсь, именуется как Functional/Atomic CSS.
## Минимализм!
В моем случае, минимализм подразумевает минимальную трудоемкость решения задач, в частности, следующее:
* минимальное количество символов кода;
* минимальное выделение памяти в Нашем головном мозге на запоминание классов и соответствующих им стилей;
* исключение лишних абстракций.
Мы запоминаем только базовые правила нотации и правила интерпретации для параметризованных имен, которые устанавливаем Мы сами, а также опции на Наше усмотрение: синонимы псевдоклассов, контексты родительских/дочерних селекторов.
Мы можем добавить свой обработчик, генерирующий ессенции стилей.
Примеры находятся в репозитории https://github.com/mr-amirka/mn-presets .
Для рантайм версии библиотеки я старался сделать парсинг и генерацию CSS как можно более оптимальной:
* если имя класса уже зарегистрировано однажды, то повторный вызов метода check для этого имени будет проигнорирован.
* если было зарегистрировано новое имя класса, процедура рендеринга не происходит сразу, а откладывается в конец итерации цикла событий и отрабатывает единожды после всех предшествующих вызовов метода check.
* во внутренних струкутрах данных препроцессора селекторы группируются по контекстам медиа-запросов и эссенциям стилей, для каждой из которых хранится отдельно прекомпилированный текст атрибутов CSS и текст селекторов CSS. При регистрации новых имен классов происходит минимум операций с конкатенациями строк.
* обработчики-генераторы CSS срабатывают единожды для каждой уникальной эссенции.