Методология CSS-модулей
Оригинал статьи: Understanding the CSS Modules Methodology
Оглавление:- Что такое CSS-модуль ?
- Как это работает
- Возможные возражения
- Начало работы с CSS-модулями
- Заключение
В постоянно меняющемся мире фронтенд разработки очень сложно найти действительно оригинальную концепцию и правильно ее представить, вызвав у других людей желание попробовать ее.
Если взглянуть на CSS, то, на мой взгляд, последней значительной инновацией в нашем подходе к написанию стилей стало появление CSS-препроцессоров, в первую очередь это касается Sass, как наиболее известного из них. Также существует PostCSS, в котором реализован несколько иной подход, но общий принцип действия тот же — в постпроцессор передается неподдерживаемый браузерами синтаксис, а возвращается поддерживаемый синтаксис.
Сейчас у нас появилась очередная интересная новинка — CSS-модули. В этой статье я представлю эту новую технику, расскажу о ее сильных сторонах и о том, как начать с ней работать.
Что такое CSS-модуль ?
Начнем с определения из официального репозитория проекта:
CSS-модуль это файл CSS, в котором названия классов и анимаций по умолчанию заданы локально.
На самом деле все немного сложне. Так как названия классов заданы локально, это требует определенной настройки, сборочного процесса и немного магии, которую иногда сложно понять.
Но в конце концов, мы можем ценить CSS-модули за то, что они реализуют, а это способ ограничивать контекст CSS одним компонентом и возможность избежать ада глобального пространства имен. Больше нет необходимости искать методику именования компонентов, ведь этот шаг делается за вас на стадии сборки!
Как это работает
CSS-модули надо подключать в сборочный процесс, это значит, что они не работают сами по себе. Плагины есть для webpack или Browserify. На базовом уровне это работает так: при импорте файла в модуль JavaScript (например, в компонент React), CSS-модули переопределят объект с названиями классов из файла в динамически задаваемые пространства имен классов, которые можно использовать как строки в JavaScript. Проиллюстрируем это на примере:
Ниже показан очень простой файл CSS. Класс .base
не является уникальным в проекте и это не то название класса, которое будет присвоено элементу. Это своего рода псевдоним внутри таблицы стилей, который будет использован в модуле JavaScript.
.base {
color: deeppink;
max-width: 42em;
margin: 0 auto;
}
И вот его использование в тестовом компоненте JavaScript:
import styles from './styles.css';
element.innerHTML = `<div class="${styles.base}">
CSS Modules are fun.
</div>`;
В итоге у нас будет сгенирировано что-то вроде следующего кода (это пример использования CSS-модулей в Webpack с дефолтными настройками):
<div class="_20WEds96_Ee1ra54-24ePy">CSS Modules are fun.</div>
._20WEds96_Ee1ra54-24ePy {
color: deeppink;
max-width: 42em;
margin: 0 auto;
}
Можно настроить генерацию более коротких названий классов или названий, следующих определенному паттерну. Это совершенно не важно (разве что более короткие названия классов дадут небольшой выигрыш в размере таблиц стилей), ведь главное, что они динамически генерируются, уникальны и связаны с правильными стилями.
Возможные возражения
Итак, мы разобрались, как это работает. И теперь вы думаете: “Что же с этим делать? Ведь это…”. ОК, я вас понимаю. Сейчас мы разберем все возражения по отдельности.
Это выглядит ужасно!
Это правда. Но имена классов не должны быть красивыми — их предназначение в применении стилей к элементу. И это именно то, что они делают, поэтому такой аргумент нельзя назвать веским.
Их трудно отлаживать!
Как только в вашей сборке производится какая-либо обработка таблиц стилей, их отладка становится болью. С Sass в этом плане не легче. Поэтому у нас есть sourcemaps, которые также можно настроить и для CSS-модулей.
Я на самом деле утверждаю, что непредсказуемость названий классов не влечет серьезных проблем с отладкой, так как классы по умолчанию специфичны для модулей. И если вы знаете, какой модуль вы сейчас проверяете в инструментах разработчика, то вы знаете, где искать соответствующие стили.
Это препятствует многократному использованию стилей
Да и нет. С одной стороны — да, но это на самом деле и есть цель: мы привязываем стили к компоненту, чтобы избежать конфликтов глобальных стилей. И согласитесь, что это хорошо.
С другой стороны, мы можем создавать глобальные классы (с помощью :global()
), например, вспомогательные классы, которые сохраняются в том же виде после сборки и позволяют абстрагировать стили также просто как и в обычных условиях. Эти классы можно использовать в ваших компонентах JavaScript.
:global(.clearfix::after) {
content: '';
clear: both;
display: table;
}
В CSS-модулях также есть способ расширения стилей из другого модуля, работающий аналогично директиве @extend
в Sass. Он не копирует стили, а складывает селекторы для расширения стилей.
.base {
composes: appearance from '../AnoherModule/styles.css';
}
Они требуют webpack, Browserify или другие инструменты?
Точно также как требуется Sass для компиляции .scss
в обычный CSS, PostCSS необходим для обработки стилей, чтобы сделать их понятными браузеру. От этапа сборки никуда не деться.
Почему мы вообще это обсуждаем?
Хм, я не совсем уверен, что в будущем CSS-модули останутся такими же как сейчас, но я думаю, что это разумный способ написания стилей. Глобальная массивная таблица стилей не слишком подходит к большим сайтам, разбитым на мелкие компоненты.
Уникальность пространства имен CSS делает его одновременно мощным и уязвимым. Такие решения как модули CSS или любой будущий инструмент, основанный на этой идее, позволяют нам сохранять силу глобальных стилей и в то же время избегать конфликтов внутри изолированных областей стилей. Это вин!
Начало работы с CSS-модулями
Как уже было сказано, нам нужен webpack или Browserify для работы CSS-модулей.
Webpack
Начнем с версии для webpack. В файл webpack.config.js
добавьте следующую конфигурацию, чтобы webpack обрабатывал файлы CSS с помощью CSS-модулей:
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules'
}
С такой настройкой полученные стили будут размещаться внутри элемента <style>
на странице. Это не самое лучшее решение, поэтому мы сконфигурируем вывод итоговых стилей в отдельный файл благодаря плагину извлечения текста для webpack:
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules')
}
Этого достаточно для работы с webpack.
Browserify
Я всегда использовал Browserify только через командную строку, так что это оказалось немного сложнее. Я добавил скрипт npm в файл package.json
:
{
"scripts": {
"build": "browserify -p [ css-modulesify -o dist/main.css ] -o dist/index.js src/index.js"
}
}
Эта строчка сообщает Browserify, что надо трансформировать src/index.js
, в dist/index.js
и скомпилировать файл dist/main.css
с помощью плагина css-modulesify. Если вы хотите добавить автопрефиксер, то вы можете завершить команду так:
{
"scripts": {
"build": "browserify -p [ css-modulesify --after autoprefixer -o dist/main.css ] -o dist/index.js src/index.js"
}
}
Как видите, вы можете использовать опцию --after
для обработки стилей после их компиляции.
Заключение
По состоянию на сегодня, система CSS-модулей еще сыровата, как вы можете заметить по конфигурации в Browserify. Но я убежден, что она будет совершенствоваться, поскольку все большее число людей понимает, что это устойчивое решение для больших и малых проектов.
Я думаю, что идея в основе CSS-модулей это направление, по которому надо двигаться. Я не скажу, что эта библиотека является лучшим из возможных решений, но в ней однозначно есть возможности реализующие то, какими должны быть стили: модульные, изолированные и при этом многократно используемые.
В качестве дальнейшего чтения по теме я рекомендую введение в CSS-модули от Глена Мэддерна, автора этого проекта.