Какие преимущества у CSS Modules
Что проверяют
Понимаете ли вы, какую конкретно проблему решают CSS Modules и как это устроено на уровне сборки. Следом обычно просят сравнить с БЭМ, CSS-in-JS и Tailwind или спрашивают, как переопределить стили дочернего компонента.
Как отвечать
Начните с механизма: файл Button.module.css при сборке превращается в обычный CSS, где каждый класс получает уникальное имя вроде Button_primary__x7f2a, а в JS импортируется объект соответствий styles. Поддержка есть из коробки в Vite и Next.js, в webpack это опция modules у css-loader.
Преимущества:
- локальная область видимости: имя
.titleможно использовать в каждом компоненте без конфликтов, договорённость в духе БЭМ соблюдает сборщик, а не люди; - нулевой рантайм: на выходе статический CSS, стили не генерируются в браузере, в отличие от styled-components. Это важно для производительности и для React Server Components, с которыми рантаймовый CSS-in-JS дружит плохо;
- явные зависимости: стили лежат рядом с компонентом и импортируются им. Удалили компонент, удалили и его файл стилей, мёртвый CSS не копится годами;
- обычный CSS: работают Sass (
.module.scss), медиазапросы, псевдоклассы, естьcomposesдля переиспользования и:globalдля осознанного выхода в глобальную область; - типизация: для TypeScript можно генерировать
.d.tsна каждый модуль, тогда опечатка вstyles.titelстанет ошибкой компиляции.
Условные классы собирают через clsx, а значения, вычисляемые в рантайме, передают CSS-переменной через style, класс при этом остаётся статическим.
Где спотыкаются
Считают изоляцию полной, как в Shadow DOM. Локальными становятся только имена классов и id, каскад и специфичность работают как обычно, а селектор по тегу глобален. Next.js на такие «нечистые» селекторы в модуле вообще ругается при сборке.
Говорят, что CSS Modules дают «динамические стили». Они умеют только переключать заранее описанные классы, для произвольных значений нужны CSS-переменные.
Не знают, как стилизовать дочерний компонент снаружи. Правильный путь: компонент принимает className и склеивает его со своим, а не родитель лезет в чужие классы через :global.