Post & Pre Processing CSS +13


Привет, читатель. На пути изучения верстки ты постиг CSS и хочешь продвинуться дальше? Тогда тебе под кат. Осторожно, много кода.

В данной статье я сделаю обзор на препроцессоры и постпроцессор(ы?).

Я не буду вдаваться в подробности насчет CSS, подразумевая, что вы его уже знаете. Классы буду именовать в BEM нотации. Также я не буду углубляться в установку и настройку всего о чем напишу, но тем не менее буду оставлять ссылки, по которым можно пройти и самостоятельно узнать как это сделать.

Начнем с препроцессоров.

Препроцессоры


Что такое препроцессор вне контекста CSS? Вики знает ответ.

Что же такое препроцессор в контексте CSS? В нашем случае препроцессор — это программа, которой на вход дается код написанный на языке препроцессора, а на выходе мы получаем CSS, который мы можем дать на вход нашему браузеру.

Какие же есть препроцессоры? Существует несколько представителей, например: Sass(.sass, .scss), Less(.less) и Stylys(.stylus).
Также среди препроцессоров можно отдельно выделить PostCSS(а точнее его парсер SugarSS и плагин PreCSS). Забегая далеко вперед, скажу что да, PostCSS — не только постпроцессор.

Я буду делать обзор на примере Sass. А точнее на его новом синтаксисе — SCSS, так как он наиболее приближен к CSS, чем старый синтаксис. Начнем с возможностей, которые добавляют препроцессоры и которых нет в CSS, а закончим решаемыми проблемами.

Возможности


Переменные


//style.scss

$color: #fff;
span {
    color: $color;
    background: $color;
}

//style.css

span {
    color: #fff;
    background: #fff;
}

Полезность переменных трудно переоценить. Теперь можно давать цветам осмысленные названия($tomato: rgb(255,99,71)), высчитывать значения не через константы, а через переменные(height: $body_height — $footer_height) и много чего еще. Многие могут возразить, что переменные в CSS есть. Но Can I Use говорит, что для IE поддержки нет(и по понятным причинам она не предвидится).

Вложенность


//style.scss
.chat-area {
    width: 40%;
    &__button { // & - указатель на текущий селектор(в данном случае & = .chat-area)
       display: inline-block;
       height:36px;
       width: 10px; 
    }

    a {
        color: red;
    }
}

//style.css
.chat-area {
    width: 40%;
}
.chat-area__button {
    display: inline-block;
    height:36px;
    width: 10px; 
}
.chat-area a {
    color: red;
}

В начале статьи я ссылался на BEM. В данном примере элемент с классом chat-area — блок. В случае, если появилась внезапная потребность его переименовать, то теперь это будет возможно сделать в одном месте, а это становится рутиной если в одном файле набирается несколько десятков селекторов, которые содержат в себе имя блока. Также хочу подметить, что это своеобразная защита от опечаток, ведь имя блока написано единожды.

Миксины


//style.scss
@mixin border-radius($radius) {
     -webkit-border-radius: $radius;
        -moz-border-radius: $radius;
         -ms-border-radius: $radius;
             border-radius: $radius;
}
.box {
    @include border-radius(10px);
}

//style.css
.box {
     -webkit-border-radius: 10px;
        -moz-border-radius: 10px;
         -ms-border-radius: 10px;
             border-radius: 10px;
}

Миксины одна из самых сложных тем для понимания. Грубо говоря миксин — это функция, которая принимает аргументы и применяет правила, зависящие от этих аргументов, к данному селектору. В данном примере миксин border-radius был применен к селектору .box

Дополнительные функции


//style.scss
$color: #202020;
h1, h2 {
    color: lighten($color, 40%);
}

//style.css
h1, h2 {
    color: #868686;
}

В основном новые функции облегчают работу с цветом. Например функция lighten — осветляет цвет на заданное кол-во процентов(противоположная функция darken).

Решаемые проблемы


Модульность


Проблема стандартного import в том, что он создает дополнительный запрос к серверу, а это дорогая операция. Было бы неплохо если бы import сразу вставлял в исходный файл весь текст импортируемого, не так ли?

Так или иначе раньше ведь не было никаких препроцессоров, а проблему надо было как-то решать. Например можно писать весь код в одном файле.

Как это выглядит
    /* сброс умолчаний */
    /* общие стили */
    /* шапка */
    /* основная часть */
    /* футер */
    /* страница печати */
    /* мобильная версия */

Как это выглядит на самом деле
    /* сброс умолчаний */
    /* общие стили */
    /* шапка */
    /* основная часть */
    /* футер */
    /* страница печати */
    /* мобильная версия */
    /* какие-то правки */
    /* новая страница */
    /* ещё какие-то правки */
    /* стили новой шапки */
    /* скопированные стили  */
    /* хотфиксы */
    /* пасхалка от разработчика */
    /* наработки от стажера */
    /* стили нового футера */



Однако у нас есть препроцессоры и они решат эту проблему за нас. Препроцессор переопределяет стандартный import и теперь он вместо запроса на сервер вставляет в исходный файл импортируемый, прям как мне и хотелось.

//style.scss
@import "selector1";
@import "selector2";

//selector1.scss
span {
  color: white;
}

//selector2.scss
div {
  color: gold;
}

//style.css
div {
 color: gold;
}

span {
 color: white;
}

Прошу заметить, что исходные файлы преобразовались в один. Один запрос на сервер за статикой — один ответ.

Наследование


<sarcasm>У нас есть классы, но нет наследования, как же так?</sarcasm>. Теперь появилась возможность выделять так называемые «шаблонные селекторы» и расширять ими другие селекторы.

// style.scss
%equal-heights { // шаблонный селектор
    height: 100%;
}

%message { // шаблонный селектор
    padding: 10px;
}

.success {
  @extend %message; color: green;
}

.error {
  @extend %message; color: red;
}

// style.css
.success, .error {
    padding: 10px;
}

.success {
    color: green;
}

.error {
    color: red;
}

Прелесть шаблонных селекторов в том, что они не попадают в сгенерированные стили. Шаблонный селектор %equal-heights не был никак задействован в коде и не оставил никаких следов в CSS. Селектор же %message отразился в виде правил для селекторов, которые его расширили. Наследоваться можно и от обычных селекторов, но предпочтительнее использовать шаблонные, чтобы не оставалось лишнего мусора.

Форматирование


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

Всего в Sass есть 4 вида форматирования.

//expanded
span {
    color: gold; 
    display: block;
}

div {
    color: red;
}

//nested
span {
     color: gold; 
     display: block; }

div {
 color: red; }

//compact
span { color: gold; display: block; }
div { color: red; }

//compressed
span{color:gold;display:block}div{color:red}

expanded — Наиболее всего похож на код, написанный человеком.
nested — Приближен к формату старого синтаксиса. Читаемость не теряется, но это холиварный вопрос.
compact — Все еще сохраняет читаемость, но уже с трудом. Полезен для определения на глаз кол-ва селекторов в проекте.
compressed — Уже совершенно не читаемый формат. Все символы, которые можно удалить, удаляются. Подходит для «скармливания» браузеру.

Постскриптум


Я не разобрал некоторые возможности добавляемые Sass. Например циклы или особенности арифметических операторов. Я оставлю их вам на самостоятельное ознакомление.

Постпроцессоры


Разобравшись с препроцессорами переходим к постпроцессорам.

В контексте Css постпроцессор по сути тоже самое, что и препроцессор, но на вход постпроцессору дается не код написанный на языке препроцессора, а тоже css. То есть постпроцессор — это программа на вход которой дается css, а на выходе получается css. Пока не сильно понятно зачем это надо.

Объясню на конкретном примере работы PostCSS — единственного представителя постпроцессоров в контексте css.

PostCSS из коробки на самом деле не делает с CSS ничего. Он просто возвращает файл, который был дан ему на вход. Изменения начинаются, когда к PostCSS подключаются плагины.

Весь цикл работы PostCSS можно описать так:

  • Исходный файл дается на вход PostCSS и парсится
  • Плагин 1 что-то делает
  • ...
  • Плагин n что-то делает
  • Полученный результат преобразовывается в строку и записывается в выходной файл

Рассмотрим же основные плагины, которые есть в экосистеме PostCSS

Плагины


Autoprefixer


Этот плагин настолько популярен, что многие считают, что они используют этот плагин, но не используют PostCSS. Они не правы.

//in.css
div {
    display: flex
}

//out.css
div {
    display: -webkit-box;
    display: -webkit-flex;
    display: -moz-box;
    display: -ms-flexbox;
    display: flex
}

Autoprefixer добавляет браузерные префиксы к вашим правилам. Ничем не заменимый и один из самых важных плагинов, с которого и началась история PostCSS. Можно даже сказать, что имеет смысл поставить PostCss только ради этого плагина.

Preset Env


//in.css
@custom-media --med (width <= 50rem);

@media (--med) {
 a:hover {
    color: color-mod(black alpha(54%));
  }
}

//out.css
@media (max-width: 50rem) {
   a:hover  {
     color: rgba(0, 0, 0, 0.54);
   }
 }

PostCSS Preset Env добавляет возможности, которые только обсуждаются в черновиках разработчиков css. В данном примере была реализована директива @custom-media, а так же функция color-mod. Начни использовать css будущего уже сегодня!

CSS Modules


Все эти BEM не для вас, но проблема с конфликтами имен классов все еще стоит? Тогда PostCSS предлагает другое решение.

//in.css
.name {
 color: gray;
}

//out.css
.Logo__name__SVK0g {
 color: gray;
}

CSS Modules изменяет названия классов по некоторому паттерну(все настраивается). Теперь мы не знаем заранее имя класса, ибо оно определяется динамически. Как же теперь проставлять классы элементам, если мы не знаем их заранее? Объединяя PostCSS, Webpack и ES6 могу предложить такое решение:

import './style.css'; // раньше
import styles from './style.css'; // сейчас

Теперь мы не просто импортируем файл со стилями(например в файле React компонента) и подставляем заранее известные нам значения, а импортируем некий объект. Ключами этого объекта будут изначальные селекторы, а значениями — преобразованные. То есть в данном примере styles['name'] = 'Logo__name__SVK0g'.

Short


//in.css
.icon {
 size: 48px;
}

.canvas {
 color: #abccfc #212231;
}

//out.css
.icon {
 width: 48px;
 height: 48px;
}

.canvas {
 color: #abccfc;
 background-color: #212231;
}

PostCSS Short добавляет кучу сокращенных записей для различных правил. Код становится короче, а следовательно в нем меньше места для ошибок. Плюс повышается читаемость.

Auto Reset


//in.css
div {
 margin: 10px;
}

a {
 color: blue;
}

//out.css
div, a {
 all: initial;
}

div {
 margin: 10px;
}

a {
 color: blue;
}

PostCSS Auto Reset позволяет нам не создавать отдельный файл со сбросом всех стилей. Плагин создает для всех селекторов один большой селектор, куда помещает правила, сбрасывающее все стили. По умолчанию создается лишь правило all со значением initial. Это полезно в комбинации с плагином postcss-initial, который в свою очередь превращает это правило в портянку правил на 4 экрана. Впрочем все можно настроить и сделать сброс например таким:

//out.css
div, a {
 margin: 0;
 padding: 0;
}
div {
 margin: 10px;
}
a {
 color: blue;
}

Помните в начале статьи я говорил что PostCSS не только постпроцессор?

PostCSS — препроцессор?


Рассмотрим один парсер и один плагин, после которых вы измените свое сложившееся мнение о PostCSS.

SugarSS


//in.sss
.parent
  color: white

.parent > .child
  color: black

//out.css
.parent {
  color: white
}

.parent > .child {
  color: black
}

SugarSS — парсер(не плагин!), который базируется на отступах, а не на фигурных скобках, как стандартный. Имеет отдельное расширение ".sss". Код написанный с помощью SugarSS по стилю схож со старым синтаксисом Sass, но без его примочек вроде переменных, миксинов, наследования и тд.

Вы ведь догадались что добавит следующий плагин?

PreCSS


//in.sss
$color: black

.parent
 .child
   color: $color

//Результат работы SugarSS
$color: black;

.parent {
 .child {
   color: $color
 }
}

//out.css
.parent .child {
   color: black
}

PreCSS как раз и добавляет те самые возможности препроцессоров о которых написано в первой половине статьи.

И чем же PostCSS теперь не препроцессор?

Stylelint


О Stylelint уже написано довольно много. Он попал в этот обзор, так как использует PostCSS, как парсер строк CSS файлов. Предположим у нас есть такой файл.

a {
 color: rgb(1, 1, 1)
}

div {
 color: rgb(0, 0, 0)
}

Вот его вывод для текущего файла:

 2:21    Expected a trailing semicolon              declaration-block-trailing-semicolon
 6:21    Expected a trailing semicolon              declaration-block-trailing-semicolon
 7:1     Unexpected missing end-of-source newline   no-missing-end-of-source-newline

Полезность этого инструмента довольно сложно переоценить.

Выводы


Препроцессоры добавляют очень много новой функциональности, которой нет в CSS. Однажды попробовав, вы с трудом вернетесь к обычному CSS.

PostCSS гораздо ближе к изначальному CSS, чем препроцессоры, но тем не менее при определенных подключенных плагинах может обладать той же функциональностью(и даже похожим синтаксисом). Новички верстальщики могут верстать даже не задумываясь, что верстают не на чистом CSS. Некоторые плагины(например Autoprefixer) не имеют аналогов в препроцессорном мире.

Никто не мешает использовать препроцессоры и PostCSS в связке. Вариант довольно неплох для проектов, которые уже используют препроцессоры и имеет место на жизнь.

Для новых же проектов я бы посоветовал использовать только PostCSS. Верстальщики привыкли к синтаксису препроцессора? Поставьте плагин PreCSS и парсер SugarSS. Нужна кроссбраузерность? Поставьте плагин Autoprefixer. Больше не нужна кроссбраузерность(например ваш проект обернули в электрон и он стал десктопным)? Просто удалите Autoprefixer! С PostCSS вы сможете, как с помощью конструктора, собрать именно то, что нужно вашему проекту.




К сожалению, не доступен сервер mySQL