Статьи

PostCSS Deep Dive: бросьте свой собственный препроцессор

В предыдущем уроке мы узнали, как использовать превосходный пакет предварительной обработки «PreCSS». В этом уроке мы подойдем к предварительной обработке на основе PostCSS; установка подобранного набора плагинов для создания собственного препроцессора с нуля.

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

В этом красота этого процесса; вы можете настроить препроцессор по своему усмотрению. Цель этого учебного пособия — дать вам практический опыт создания препроцессора PostCSS и познакомить вас с функциями доступных в настоящее время плагинов, чтобы вы могли сами решить, какие из них вы хотите использовать.

Давайте начнем!

Первое, что вам нужно сделать, это настроить ваш проект на использование Gulp или Grunt, в зависимости от ваших предпочтений. Если у вас еще нет предпочтений тому или иному, я рекомендую использовать Gulp, так как вам потребуется меньше кода для достижения тех же целей.

О том, как настроить проекты Gulp или Grunt для PostCSS, вы можете прочитать в предыдущих уроках

соответственно.

Если вы не хотите вручную настраивать свой проект с нуля, вы можете загрузить исходные файлы, прикрепленные к этому учебному пособию , и извлечь предоставленный стартовый проект Gulp или Grunt в пустую папку проекта.

Затем с помощью терминала или командной строки, указывающих на папку, выполните команду npm install .

В этом руководстве предполагается, что вы следовали предыдущим записям в серии и теперь знакомы с тем, как установить плагин в свой проект и загрузить его через ваш Gulpfile или Gruntfile.

Важный! По мере прохождения, обязательно загружайте плагины в ваш Gulpfile / Gruntfile в порядке, который вы видите в этом руководстве; порядок загрузки важен в PostCSS, чтобы все работало гладко.

Самое первое, что мы собираемся начать с создания нашего собственного препроцессора — это импорт. Вы уже видели, как PostCSS вставлял таблицы стилей @import в предыдущих уроках по минимизации, оптимизации и предварительной обработке с PreCSS . Способ использования импорта в этом препроцессоре не отличается.

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

Например, мы могли бы сохранить все наши переменные в файле с частичными @import и использовать @import для переноса этой части в нашу основную таблицу стилей. Если бы мы не запустили плагин, который сначала @import файлы @import , наши переменные не были бы импортированы и, следовательно, не были бы доступны для дальнейшей обработки.

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

Примечание : если вы используете Grunt, вам не нужно вносить никаких изменений на этом этапе.

Прямо сейчас у нас есть любой файл «.css», найденный в папке «src», но мы не хотим случайно скомпилировать частичные файлы. Мы будем импортировать все в наш файл «style.css», так что это единственный файл, который нужно скомпилировать.

Найдите эту строку:

1
return gulp.src(‘./src/*.css’)

… и измените его на:

1
return gulp.src(‘./src/style.css’)

Это тот же плагин, который мы использовали в учебном пособии «Для минимизации и оптимизации», и он также используется в PreCSS, так что вы будете знакомы с ним на данном этапе.

Установите плагин в свой проект, затем в папке «src» создайте файл с именем «_vars.css» и добавьте в него базовый тестовый код. Обратите внимание, что мы еще не добавили функциональность переменных, поэтому просто немного CSS, например:

1
2
3
.test {
    background: black;
}

Теперь импортируйте ваш новый файл переменных в ваш основной файл «src / style.css», добавив этот код в первую строку:

1
@import «_vars»;

Скомпилируйте ваш код, затем проверьте файл «dest / style.css», и вы должны увидеть, что теперь он содержит код из вашего файла «_vars.css».

Примечание : этот плагин должен быть запущен до плагинов postcss-nested и postcss-simple-vars , которые мы будем использовать.

Идите вперед и установите postcss-mixins, затем добавьте следующий код в ваш файл «src / style.css»:

1
2
3
4
5
6
7
8
@define-mixin icon $network, $color {
    .button.$(network) {
        background-image: url(‘img/$(network).png’);
        background-color: $color;
    }
}
 
@mixin icon twitter, blue;

После компиляции ваш «dest / style.css» должен иметь следующий скомпилированный код:

1
2
3
4
.button.twitter {
    background-image: url(‘img/twitter.png’);
    background-color: blue;
}

Плагин postcss-mixins, который мы здесь используем, такой же, как и в PreCSS. Мы рассмотрели, как использовать его в учебнике по PreCSS, поэтому для получения полной информации о его синтаксисе ознакомьтесь с разделом «Mixins» в предыдущем учебнике.

Если вы предпочитаете использовать синтаксис Sass при создании миксов, проверьте плагин Энди Янссона postcss-sassy-mixins , который работает так же, как и postcss-mixins, но с синтаксисом @mixin для определения @include и @include для использования один.

Примечание : плагин postcss-for — это еще один плагин, который должен быть запущен перед postcss-nested и postcss-simple-vars .

Установите плагин postcss-for , затем проверьте, работает ли он должным образом, добавив этот код в файл «src / style.css»:

1
2
3
4
5
@for $i from 1 to 3 {
        p:nth-of-type($i) {
        margin-left: calc( 100% / $i );
    }
}

Это должно скомпилировать, чтобы дать вам:

01
02
03
04
05
06
07
08
09
10
11
p:nth-of-type(1) {
    margin-left: calc( 100% / 1 );
}
 
p:nth-of-type(2) {
    margin-left: calc( 100% / 2 );
}
 
p:nth-of-type(3) {
    margin-left: calc( 100% / 3 );
}

Еще раз, плагин, который мы используем для добавления циклов @for такой же, как и в PreCSS, поэтому для получения дополнительной информации о его синтаксисе ознакомьтесь с разделом «Петли» в предыдущем уроке.

Плагин postcss-for должен быть запущен перед postcss-simple-vars , что означает, что нет способа использовать переменные для установки диапазона, через который должен @for цикл @for .

Если это проблема, вы можете вместо этого использовать этот плагин postcss-for, который должен быть загружен после плагинов postcss-simple-vars.

Поскольку он запускается после оценки переменных, вы можете использовать переменные для установки диапазона, через который должен @for цикл @for , например:

1
2
3
4
5
6
7
8
@from: 1;
@count: 3;
 
@for $i from @from to @count {
    p:nth-of-type($i) {
        margin-left: calc( 100% / $i );
    }
}

Мы собираемся добавить два вида переменных в наш препроцессор, оба из которых могут быть очень полезными. Первый тип использует Sass-подобный синтаксис, а второй использует синтаксис пользовательских свойств CSS , иначе называемых переменными CSS.

Установите эти два плагина, тогда мы будем тестировать каждый из них по очереди.

Сначала мы протестируем Sass-подобный синтаксис postcss-simple-vars. Откройте файл «_vars.css», который вы создали ранее, удалите его содержимое и добавьте следующий код:

1
$default_padding: 1rem;

Добавьте следующее в ваш файл «src / style.css» и перекомпилируйте:

1
2
3
.post {
    padding: $default_padding;
}

Это должно скомпилировать, чтобы дать вам:

1
2
3
.post {
    padding: 1rem;
}

Теперь мы протестируем пользовательские свойства CSS, такие как синтаксис postcss-css-variable. Добавьте следующий код в ваш файл «src / style.css»:

01
02
03
04
05
06
07
08
09
10
11
12
13
:root {
    —h1_font_size: 3rem;
}
 
h1 {
    font-size: var(—h1_font_size);
}
 
@media ( max-width: 75rem ){
    h1 {
        —h1_font_size: 4vw;
    }
}

Он должен компилироваться в:

01
02
03
04
05
06
07
08
09
10
h1 {
    font-size: 3rem;
}
 
@media ( max-width: 75rem ) {
 
    h1 {
        font-size: 4vw;
    }
}

Обратите внимание, что при использовании переменных CSS нам нужно было только изменить значение переменной --h1_font_size внутри --h1_font_size и он автоматически --h1_font_size соответствующее свойство font-size . Это особенно полезный функционал.

Прежде чем я продолжу, я еще раз кратко упомяну, что подход, использованный в этом уроке, не тот подход, который вы должны использовать. Если вы хотите использовать один тип переменных, а не другой, это вполне нормально.

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

Это позволяет мне устанавливать пользовательские свойства CSS для типа переменных, которые я мог бы фактически использовать в живом проекте, если / когда они хорошо поддерживаются в браузерах. Как вы видели в приведенном выше примере, они также обладают определенной функциональностью, которой нет в Sass-подобных переменных.

Между тем, я могу использовать переменные, подобные Sass, для вещей, которые не принадлежат живой таблице стилей, особенно те, которые существуют исключительно для обработки через такие вещи, как циклы каждые, условные выражения и другие преобразования.

В качестве альтернативы использованию postcss-simple-vars вы можете рассмотреть возможность использования postcss-advanced-variable , плагина, который используется как часть пакета PreCSS.

Это также отличный вариант, с основным отличием в том, что он обрабатывает условные выражения, циклы и переменные в одном плагине. Для меня причина, по которой я сейчас выбираю postcss-simple-vars, заключается в том, что я предпочитаю использовать условные выражения из отдельного плагина; postcss-условные выражения, которые мы вскоре рассмотрим.

Вместо использования postcss-css-variable вы можете предпочесть postcss-custom-properties .

Существенное различие между ними заключается в том, что postcss-custom-properties строго соответствует спецификации W3C для пользовательских свойств, поэтому вы можете быть уверены, что пишете только правильный будущий CSS. С другой стороны, postcss-css-variable предлагает дополнительную функциональность, но при этом не претендует на полное равенство со спецификацией.

Я лично выбираю postcss-css-variable, потому что я использую его в контексте предварительной обработки, где я в любом случае пишу много не специфицированного кода. Таким образом, я предпочел бы иметь дополнительную функциональность более 100% соответствия спецификации.

Однако, если вы используете переменные в контексте написания будущего CSS, вы можете обнаружить, что postcss-custom-properties вам лучше подойдут.

Установите плагин postcss-each, затем добавьте этот код переменной в ваш файл «_vars.css»:

1
$social: twitter, facebook, youtube;

Этот код определяет список, хранящийся в переменной $social .

Теперь мы собираемся создать цикл @each для перебора значений, хранящихся в нашей переменной $social . Добавьте этот код в ваш файл «src / style.css»:

1
2
3
4
5
@each $icon in ($social){
    .icon-$(icon) {
        background: url(‘img/$(icon).png’);
    }
}

Наш цикл @each теперь готов, но прежде чем мы сможем его скомпилировать, нам нужно немного изменить конфигурацию параметров postcss-simple-vars.

Вы заметите, что в приведенном выше коде мы используем $icon для представления текущего значения, через которое мы перебираем. Некоторые трудности могут возникнуть из-за того, что плагин postcss-simple-vars ищет знак $ , чтобы идентифицировать переменные.

Это означает, что он будет видеть $icon , думать, что это переменная, попытаться обработать ее, а затем увидеть, что она не имеет значения. Это остановит компиляцию и выдаст сообщение об ошибке консоли, в которой обнаружена неопределенная переменная.

Чтобы решить эту проблему, мы хотим добавить опцию silent: true к нашим опциям для плагина. Это означает, что если он обнаружит неопределенную переменную, он не прекратит компиляцию для регистрации ошибки, он просто продолжит работу. Следовательно, его не будет беспокоить присутствие $icon в нашем цикле @each и мы сможем успешно скомпилировать.

В массиве процессоров вашего Gulpfile или Gruntfile установите параметр:

1
2
3
4
5
/* Gulpfile */
simple_vars({silent: true})
 
/* Gruntfile */
require(‘postcss-simple-vars’)({silent: true})

Теперь скомпилируйте ваш CSS, и вы должны получить:

01
02
03
04
05
06
07
08
09
10
11
.icon-twitter {
    background: url(‘img/twitter.png’);
}
 
.icon-facebook {
    background: url(‘img/facebook.png’);
}
 
.icon-youtube {
    background: url(‘img/youtube.png’);
}

Как упоминалось ранее, postcss-advanced-variable — это еще одна отличная опция плагина, которая обрабатывает переменные, циклы и условные выражения все в одном.

Ранее я упоминал, что этот плагин является моим предпочтением для обработки условных выражений. Это потому, что я обнаружил, что он способен обрабатывать более сложные условные проверки. Он включает в себя поддержку синтаксиса @else if , что означает, что вы можете протестировать больше условий в одном фрагменте кода.

После установки плагина postcss-conditionals протестируйте его, добавив этот код в файл «src / style.css»:

01
02
03
04
05
06
07
08
09
10
11
12
13
$column_count: 3;
 
.column {
    @if $column_count == 3 {
        width: 33%;
        float: left;
    } @else if $column_count == 2 {
        width: 50%;
        float: left;
    } @else {
        width: 100%;
    }
}

Этот код проверит значение, которое мы установили в переменной @column_count и выведет различные значения ширины и числа с плавающей запятой в зависимости от того, что он найдет. Он работает так же, как и код, который мы использовали в предыдущем руководстве по предварительной обработке, но теперь, когда у нас есть возможность использовать @else if строках нам удалось увеличить количество тестируемых условий с двух до трех.

После перекомпиляции это должно дать вам:

1
2
3
4
.column {
    width: 33%;
    float: left
}

Попробуйте изменить $column_count на 2 или 1 и снова скомпилировать, чтобы увидеть, как он меняет вывод CSS.

Мы также можем использовать эти типы условных выражений в миксинах, для которых мы добавили поддержку ранее. Например, мы можем создать миксин для генерации кода макета столбца следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@define-mixin columns $count {
    @if $count == 3 {
        width: 33%;
        float: left;
    } @else if $count == 2 {
        width: 50%;
        float: left;
    } @else {
        width: 100%;
    }
}
 
.another_column {
    @mixin columns 2;
}

Это даст вам вывод:

1
2
3
4
.another_column {
    width: 50%;
    float: left;
}

Как упоминалось ранее, postcss-advanced-variable — это еще одна отличная опция плагина, которая обрабатывает переменные, циклы и условные выражения все в одном.

В предыдущем уроке мы использовали postcss-calc через cssnano, чтобы повысить эффективность использования экземпляров calc() . Однако в контексте предварительной обработки она может быть очень полезна везде, где мы можем захотеть использовать математику в наших таблицах стилей.

Начните установку postcss-calc, затем мы протестируем ее, сделав добавленный выше микс для генерации столбцов более эффективным.

Прямо сейчас мы используем условные выражения для проверки, установлен ли аргумент $count в mixin на 1 , 2 или 3 затем выводим соответствующую предварительно рассчитанную ширину. Вместо этого мы будем использовать calc() чтобы автоматически выводить правильную ширину для нашего кода столбца, независимо от того, какое число передается через миксин.

Добавьте в свой файл «src / style.css»:

01
02
03
04
05
06
07
08
09
10
@define-mixin columns_calc $count {
    width: calc( 100% / $count );
    @if $count > 1 {
        float: left;
    }
}
 
.column_calculated {
    @mixin columns_calc 2;
}

Вместо того, чтобы жестко кодировать процентную ширину, которая нам понадобится для определенного числа столбцов, мы теперь вычисляем ее на лету.

Плагин postcss-calc преобразует width: calc( 100% / $count ); в статическую сумму в зависимости от значения, переданного, когда мы вызываем mixin, в этом случае 2 .

Перекомпилируйте ваш код, и вы должны увидеть этот вывод:

1
2
3
4
.column_calculated {
    width: 50%;
    float: left;
}

Примечание . Когда postcss-calc может преобразовать calc() в статическое значение, он будет выводиться в ваш код. Если это невозможно, это ничего не изменит, поэтому вы все равно можете использовать calc() для значений, которые должны обрабатываться браузером во время выполнения.

Для вложения мы используем тот же плагин, который используется в пакете PreCSS, поэтому вы можете обратиться к предыдущему учебнику для получения полной информации о синтаксисе.

Установите postcss-nested, затем проверьте, что все работает правильно, скомпилировав этот код:

1
2
3
4
5
6
.menu {
  width: 100%;
    a {
        text-decoration: none;
    }
}

Ваш полученный CSS должен быть:

1
2
3
4
5
6
7
.menu {
    width: 100%;
}
 
.menu a {
    text-decoration: none;
}

Для расширений мы будем использовать плагин postcss-sass-extend . Это даст нам другой синтаксис для использования, чем тот, который мы рассмотрели в нашем предыдущем руководстве по работе с PreCSS. Вместо @define-extend extend_name {...} определяемых с помощью @define-extend extend_name {...} они определяются с помощью %extend_name {...} .

Они по-прежнему используются с практически тем же синтаксисом, что и @extend %extend_name; ,

Обратите внимание, что плагин postcss-sass-extended на самом деле поставляется с PreCSS, однако я предполагаю, что он не загружается по умолчанию, так как при попытке использовать требуемый синтаксис он не компилируется.

После установки postcss-sass-extension в ваш проект протестируйте его с помощью следующего кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
%rounded_button {
    border-radius: 0.5rem;
    padding: 1em;
    border-width: 0.0625rem;
    border-style: solid;
}
 
.blue_button {
    @extend %rounded_button;
    border-color: #2F74D1;
    background-color: #3B8EFF;
}
 
.red_button {
    @extend %rounded_button;
    border-color: #C41A1E;
    background-color: #FF2025;
}

Он должен компилироваться в:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
.blue_button, .red_button {
  border-radius: 0.5rem;
    padding: 1em;
    border-width: 0.0625rem;
    border-style: solid;
}
 
.blue_button {
    border-color: #2F74D1;
    background-color: #3B8EFF;
}
 
.red_button {
    border-color: #C41A1E;
    background-color: #FF2025;
}

До сих пор мы рассмотрели то, что можно считать основными функциями, общими для большинства препроцессоров. Тем не менее, есть еще больше плагинов, которые предлагают дополнительные функции; некоторые из этих функций можно найти в других препроцессорах, а некоторые вам нужно найти в PostCSS. Сейчас мы кратко рассмотрим эти дополнительные параметры.

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

Функциональность, предлагаемую этим плагином, можно сравнить с @mixin Stylus, при этом вместо использования синтаксиса, такого как @mixin , вы определяете куски кода таким образом, чтобы они впоследствии могли использоваться в коде так же, как нативная собственность, например

01
02
03
04
05
06
07
08
09
10
/* Define a property */
size: $size {
  height: $size;
  width: $size;
}
 
/* Use it like a native property */
.square {
  size: 50px;
}

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

Поиск свойств — это особенность, найденная в Stylus, которая может быть очень удобной. Это позволяет вам искать значение свойства в том же стиле. Например, вы можете установить правое поле для соответствия левому с помощью: margin-left: 20px; margin-right: @margin-left; margin-left: 20px; margin-right: @margin-left;

В то время как обычное вложение, которое мы рассмотрели выше, разворачивает селекторы, плагин postcss-nested-props разворачивает вложенные свойства, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/* Origincal code */
.element {
    border: {
        width: 1px;
        style: solid;
        color: #ccc;
    }
}
 
/* After processing */
.element {
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
}

Сопоставление дает вам еще один способ выполнения условных проверок, на этот раз с использованием Rust, как сопоставление с образцом, что-то похожее на операторы switch в JavaScript или PHP. Это может дать вам более эффективный способ проверки нескольких условий, чем написание многих проверок @if else .

Генерация спрайтов CSS, популярная функция в Compass, также может быть выполнена с помощью плагина postcss-sprites. Плагин будет сканировать ваш CSS на наличие изображений, объединять эти изображения в лист спрайта и обновлять ваш код в соответствии с требованиями для правильного отображения из нового листа спрайта.

В настоящее время существует действительно надежный список плагинов для языковых расширений, который мы можем охватить здесь, поэтому ознакомьтесь с полным списком по адресу: https://github.com/postcss/postcss#language-extensions

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

PostCSS все еще относительно нов, и вы можете найти что-то, чего хотите достичь с помощью своего собственного препроцессора, для которого в настоящее время нет плагинов. Прелесть этой модульной экосистемы в том, что у вас есть возможность решить эту проблему самостоятельно, создав собственный плагин. Любой может сделать это, и барьер для входа намного ниже, чем если бы вы попытались добавить свои собственные функции в Stylus, Sass или LESS. Мы узнаем, как в следующем уроке.

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

В следующем уроке мы узнаем, как использовать PostCSS в сочетании со Stylus, Sass или LESS. Увидимся там!