Вложенные CSS-правила (CSS Nesting)

Недавно был опубликован редакторский черновик (Editor’s Draft) модуля CSS Nesting. В нем описаны новые правила синтаксиса, с помощью которых можно будет вкладывать одни CSS-правила в другие (наподобие того, как это делается в препроцессорах).

CSS Nesting Module. Вложенности в CSS

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

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

  • postcss-nesting. Плагин, отвечающий только за поддержку CSS-вложенности. Используется в postcss-preset-env. Его можно подключить отдельно, если вам в проекте нужна только вложенность.

Примеры

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

.header {
  position: relative;
  z-index: 1000;
  padding: 5px 0;
  height: 40px;
  background-color: #233239;

  & .logo {
    display: flex;
    align-items: center;
    width: 140px;

    & .title {
      font-size: 22px;
      line-height: 1;
    }

    & .image {
      width: 30px;
      height: 30px;
    }
  }
}

Эквивалентно:

.header {
  position: relative;
  z-index: 1000;
  padding: 5px 0;
  height: 40px;
  background-color: #233239;
}

.header .logo {
  display: flex;
  align-items: center;
  width: 140px;
}

.header .logo .title {
  font-size: 22px;
  line-height: 1;
}

.header .logo .image {
  width: 30px;
  height: 30px;
}

Знак & является обязательным для вложенных селекторов. Вы не можете написать так:

.search {
  & .button,
  .icon {
    /* стили для .button и .icon */
  }
}

Нужно обязательно добавлять & для каждого вложенного селектора:

.search {
  & .button,
  & .icon {
    /* стили для .button и .icon */
  }
}

Во вложенных правилах также работают комбинаторы +, >, ~. Перед вложенным селектором можно писать @nest. Но это не обязательно, если первым идет &, а потом через пробел селектор:

.list {
  color: #000;

  @nest & > .item { /* здесь @nest не обязателен */
    font-weight: 600;
  }
}

Эквивалентно:

.list {
  color: #000;
}

.list > .item {
  font-weight: 600;
}

А в этих случаях наличие @nest обязательно:

.plate {
  display: none;

  @nest .is-open & { /* @nest обязателен */
    display: block;
  }
}

Эквивалентно:

.plate {
  display: none;
}

.is-open .plate {
  display: block;
}

В спецификации пока что не говорится о вкладывании @-правил (таких как, например, @media), но PostCSS-плагин допускает такую возможность:

.parent {
  color: red;

  @media (min-width: 1024px) {
    color: black;

    @media (max-width: 1440px) {
      & .child {
        color: yellow;
      }
    }
  }
}

Результатом будет следующий код:

.parent {
  color: red;
}

@media (min-width: 1024px) {
  .parent {
    color: black;
  }
}

@media (min-width: 1024px) and (max-width: 1440px) {
  .parent .child {
    color: yellow;
  }
}

В данной спецификации есть недостаток — не работает «склейка» селекторов. То есть, если при именовании классов вы руководствуетесь методологией BEM, то у вас ничего не получится, как ожидалось.

Например, если, используя препроцессор SCSS можно делать так:

.card {
  &__image {}
  &__title {}
}

То можно было бы подумать, что, используя CSS вложенность, придется делать это так, как в следующем примере, но это не совсем правильно:

.card {
  & .card__image {}
  & .card__title {}
}

Такой подход приводит к увеличению веса селекторов (что в дальнейшем может привести к ошибкам с перезаписью свойств):

.card .card__image {} /* вес 20 */

.card. .card__title {} /* вес 20 */

Применяя BEM, придется записывать без вложенности, по-обычному:

.card {} /* вес 10 */

.card__image {} /* вес 10 */

.card__title {} /* вес 10 */

Итак, с основными правилами синтаксиса мы ознакомились. Давайте разберем, как это можно использовать на практике в своих проектах.

Использование

Онлайн-компилятор

Самый простой способ опробовать возможности CSS вложенности — онлайн компилятор плагина postcss-nesting.

Использование в командной строке

Для того, что запускать PostCSS в командной строке необходимо установить следующие пакеты:

npm install --save-dev postcss postcss-cli

Также потребуется установить один из плагинов, перечисленных в начале статьи (postcss-nesting или postcss-preset-env). Мы выберем postcss-nesting как наиболее простой:

npm install --save-dev postcss-nesting

После чего можно преобразовывать исходный CSS-файл:

postcss input.css --output output.css --use postcss-nesting

Также вместо того, чтобы указывать при каждом вызове аргумент --use, можно создать файл настроек postcss.config.js:

module.exports = {
  plugins: [
    require('postcss-nesting'),
  ],
};

Теперь аргумент --use можно опустить:

postcss input.css --output output.css

Подробнее о работе с postcss-cli можно узнать по ссылке.

Использовани с Gulp

Для Gulp необходимо установить следующие пакеты:

npm install --save-dev gulp-postcss postcss-nesting

После чего требуется настроить задачу в gulpfile.js:

let gulp = require('gulp');
let postcss = require('gulp-postcss');

gulp.task('css', () => {
  return gulp.src('./src/*.css')
    .pipe(postcss())
    .pipe(gulp.dest('./dest'));
});

Плагин автоматически загружает настройки из файла postcss.config.js, поэтому возьмем его из предыдущего примера:

module.exports = {
  plugins: [
    require('postcss-nesting'),
  ],
};

Подробнее о работе с gulp-postcss можно узнать по ссылке.

Использование с Webpack

Для работы с Webpack потребуется установить следующие пакеты:

npm install --save-dev postcss postcss-nesting postcss-loader css-loader style-loader

Далее нужно настроить webpack.config.js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
            },
          },
        ],
      },
    ],
  },
};

Файл postcss.config.js остается таким же, как и в предыдущих примерах:

module.exports = {
  plugins: [
    require('postcss-nesting'),
  ],
};

Подробнее о postcss-loader можно узнать по ссылке.

Использование с Rollup

Для использования CSS-вложенности в Rollup необходимо установить следующие пакеты:

npm install --save-dev rollup-plugin-postcss postcss-nesting

Далее нужно настроить rollup.config.js:

module.exports = {
  plugins: [
    require('rollup-plugin-postcss'),
  ],
};

Подробнее о rollup-plugin-postcss можно узнать по ссылке.