Как я сократил вес CSS-бандла в 3.5 раза: рефакторинг, Bootstrap и единая система карточек

Как я сократил вес CSS-бандла в 3.5 раза: рефакторинг, Bootstrap и единая система карточек

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

Часто один из самых «тяжёлых» элементов на странице — это CSS. В моём случае основной сгенерированный CSS-файл весил 986 КБ — это довольно много, особенно, если учесть, что основная задача сайта — как можно быстрее предоставить пользователю контент.

В этой статье я расскажу, как мне удалось сократить объем CSS до 284 КБ — практически уменьшить в три с половиной раза. Без потери функциональности. Более того — с улучшением читаемости и управляемости кода.

🚨 Проблема: дублирование, фрагментарность и кастомные велосипеды

Первоначально проект имел:

  • собственную кастомную сетку;
  • десятки вариаций отступов с уникальными классами;
  • 12 различных версий карточек публикаций;
  • бессистемную организацию медиа-запросов;
  • отсутствие Mobile First-подхода;
  • вложенные классы.

Каждый компонент разрабатывался «в моменте», что привело к множеству схожих, но не унифицированных решений. Например:

  • .card-news, .card-event, .article-card — все были карточками, но с разной версткой;
  • .margin-bottom-10, .mb-15px, .block-space — все обозначали отступы, но не системно;
  • сетка использовалась своя, а потом ещё одна, слегка изменённая.
  • .hh-cols__item--right .card-news — неочевидные зависимости, которые увеличивают вес селектора и которые трудно искать в коде.

Последствия:

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

🔧 Решение: системный подход и Bootstrap как основа

1. Отказ от кастомной сетки в пользу Bootstrap Grid

Я перенёс структуру сетки на Bootstrap 5, ограничившись использованием только Grid-системы и миксинов отступов (без полной зависимости от всей библиотеки).

Почему это важно:
Bootstrap Grid давно оптимизирован и протестирован. Она основана на Mobile First-подходе и предлагает гибкую 12-колоночную структуру. Главное — не тащить всю библиотеку, а использовать только нужное.

Результат:

  • взяты брейкпоинты Bootstrap и единая логика классов вида:row, col-md-6;
  • исчезновение дубликатов сетки;
  • упрощение шаблонов.

2. Унифицированная система отступов

Вместо десятков кастомных классов я оставил стандартизированные классы вида mb-{x}, px-{y}, в духе Bootstrap.

Для этого использовал генерацию через SCSS-циклы:

Плюсы:

  • читаемость и предсказуемость;
  • переиспользуемость;
  • уменьшение объема итогового CSS.

3. Одна универсальная карточка вместо 12 шаблонов

Я выделил общие элементы всех карточек (заголовок, дата, изображение) и создал один компонент card-universal, где блоки могут включаться или выключаться через условия.

В шаблоне это может выглядеть так:

Что это дало:

  • избавление от дублирующего CSS;
  • один набор стилей для десятков карточек;
  • упрощение логики и шаблонов;
  • -100 КБ в финальном CSS только за счёт этого шага.

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

4. Композиция медиа-запросов через Webpack + SCSS

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

Я использовал плагин postcss-sort-media-queries в Webpack, который группирует запросы.

До:

После:

Плюс:
Код не повторяется 20 раз — все медиа-правила организованы в одном месте.

5. Mobile First — как философия

Переход на Mobile First дал два главных результата:

  • уменьшение веса: базовые стили применяются всем, а расширения добавляются только для отдельных медиа-правил;
  • улучшение UX на мобильных: сайт стал быстрее и адаптивнее.

📊 Результаты оптимизации

ПоказательБылоСтало
Размер итогового CSS976 КБ284 КБ
Количество карточек121
Кастомных отступов>301 система
Подключение Bootstrap✅ (только grid и spacing)
Медиа-инструкциихаосструктурированы
Стиль адаптивностиmixedMobile First

📚 Подтверждение практики

Согласно исследованию Google Web Fundamentals, задержки при загрузке стилей напрямую влияют на First Contentful Paint (FCP) и Time to Interactive (TTI) — ключевые метрики Core Web Vitals.

Сокращение CSS-файла даже на 100 КБ может ускорить загрузку на 300–500 мс, особенно в 3G-сетях.

💡 Выводы и советы

  • Не бойтесь использовать части фреймворков, если они помогают решать ваши задачи.
  • Не плодите зависимости без необходимости.
  • Не изобретайте велосипед для стандартных вещей.
  • Унификация компонентов — залог чистоты и скорости.
  • Используйте Mobile First и собирайте медиа-запросы — это даёт реальные преимущества.

📈 Что дальше?

Работа по оптимизации еще не завершена, так как в ряде случаев еще остались не унифицированные карточки и отступы, хотя бóльшая часть уже переработана. Далее буду оптимизировать JS.

🤔 Зачем это нужно владельцу сайта?

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