НовостиАдминистрированиеУправления хостингомАрхив
22 октября 2023

CSS модули - решаем проблему масштабирования стилей

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

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

Проблема масштабирования CSS

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

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

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

.widget table row .cell .content .header .title{ 
padding: 10px 20px; 
font-weight: bold; 
font-size: 2rem;
}

Такой подход имеет ряд значительных недостатков:

  • Он убивает производительность. Небольшой пример, который мы привели выше, требует от браузера нахождения семи объектов в DOM прежде, чем начнется рендеринг. Это довольно незначительно, если количество таких селекторов невелико, но если они лежат в основе вашего подхода, вы увеличиваете время корректного отображения страницы на несколько секунд.
    • Он увеличивает вес вашего сайта. Байты все еще имеют значение, особенно, если речь идет о мобильных устройствах с ограниченной скоростью соединения.
    • Он практически исключает повторное использование. Вместо того чтобы работать с каскадированием, вы боретесь с ним, тем самым снижая возможность повторного использования и дублирования.

Движение в правильном направлении

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

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

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

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

Типичный root-файл для Saas:

// root.scss
@import 'reset'; 
@import 'global-values'; 
@import 'header'; 
@import 'item-list'; 
@import 'footer';

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

BEM (Block Element Modifier)

BEM методология позволяет добиться модульности CSS благодаря соглашению об именовании селекторов:

[block]__[element] — [modifier] { 
padding: 10px 20px; 
font-weight: bold;
font-size: 2rem; 
}
  • Блок отдельная часть, которая довольно значима сама по себе (header, container, menu, input) .
    • Элемент часть блока, которая имеет самостоятельное значение в контексте этого блока (menu item, list item, checkboxcaption, header title) .
    • Модификатор метка на блоке или элементе. Используется, чтобы изменить поведение или отображение (disabled, active, checked, big, red, error).

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

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

Модульный CSS

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

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

Пример:

./* components/demo/ScopedSelectors.css */
.root {
composes: box from \"shared/styles/layout.css\";
border-color: red;
}
.text {
composes: heading from \"shared/styles/typography.css\";
color: red;
}

Загрузка модуля в пределах необходимого компонента такая же простая задача, как использование import или использование другого JavaScript-модуля.

/* components/demo/ScopedSelectors. js */
import styles from '.
/ScopedSelectors.css';

Как только модуль загружен, вы можете ссылаться на имена CSS классов, как и на любые другие свойства. Простой пример для React:

import React, { Component } from 'react';
import styles from './ScopedSelectors.css';
export default class ScopedSelectors extends Component {
   render() {
      return (
         <div className={styles.root}>
            <p className={styles.text}>Scoped Selectors</p>
         </div>
      );
   }
};

Заметьте, мы указываем активный класс из нашего модуля, используя точечную нотацию.

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

<div class=”ScopedSelectors__root___16yOh”>
<p class=”ScopedSelectors__text___1hOhe”>Scoped Selectors</p></div>

Загрузчик модулей превратил {styles.root} и {styles.text} в глобальные стили с уникальными именами. Сгенерированные классы используют BEM методологию для именования, включая имена классов и имена компонентов. Благодаря такому подходу вы без последствий можете использовать два класса с одинаковыми именами в разных компонентах.

Возможность повторного использования

Эффективное повторное использование класса имеет решающее значение для минимизации дублирования и поддержания UI в вашем проекте. CSS модули значительно упрощают повторное использование благодаря сохранению четкой структуры. Один класс может становиться частью другого или множества других классов.Для того чтобы вы понимали о чем речь, немного дополним наш пример:

/* components/demo/ScopedSelectors.css */
.
root { 
composes: box from \"shared/styles/layout.css\"; 
border-color: red; 
}
.
text { 
composes: heading from\"shared/styles/typography.css\";
color: red; 
}

Файл layuout это еще один модуль:

/* shared/styles/layout.css */
. box { 
border-width: 2px; 
border-style: solid; 
padding: 0 20px; 
margin: 0 6px; 
max-width: 400px; 
}

А так выглядит модуль typography:

/* shared/styles/typography.css */
. heading { 
font-size: 24px; 
font-family: helvetica, arial, sans-serif; 
font-weight: 600; 
}

Работа с препроцессорами

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

Заключение

Оглядываясь на наши три попытки решить проблему масштабирования CSS, стоит выделить несколько преимуществ модулей:

  1. Модули устраняют проблему глобальной области видимости, генерируя уникальные имена классов, основываясь на принципах BEM методологии;
    1. Так как модули существуют на уровне компонентов, нет необходимости придумывать сложные имена классов, вы можете использовать простые, понятные имена, связанные с особенностями компонента;
    2. Благодаря работе на уровне компонентов, значительно упрощается рефакторинг.
По материалам medium.com