Аудит таблицы стилей с помощью Project Wallace
Аудит CSS в Project Wallace превращает уникальные цвета, размеры шрифта, специфичность, дубликаты и размер файла в понятные правки.
CSS-аудит с Project Wallace означает, что вы вставляете таблицу стилей в онлайн-анализатор и читаете пять чисел: количество уникальных цветов, количество уникальных font-size, максимальную специфичность селектора (рядом с ней — счётчики id и !important), разрыв между общим и уникальным числом объявлений, а также размер несжатого файла в сравнении с размером после gzip. Каждое из этих чисел указывает на своё изменение.
Если вашей таблице стилей три года, вы, скорее всего, уже подозреваете, что она «поплыла». Чего вам не хватает — так это числа, которое можно вписать в тикет. Формулировка «CSS выглядит запущенным» не попадёт в приоритеты; «мы отдаём шесть оттенков серого там, где дизайн определяет два» — попадёт.
В этой статье мы прогоним одну небольшую тестовую таблицу стилей через анализатор, разберём вывод метрика за метрикой и сопоставим каждую находку с конкретным изменением, которое она должна повлечь. Здесь речь о диагностике. Продолжение — How to Organize CSS in Modern Web Projects — посвящено лечению.
Ключевые выводы
- Браузеры отбрасывают CSS, который не могут разобрать или не распознают, и продолжают рендеринг, поэтому таблица стилей копит ошибки, ни разу не роняя сборку.
- Когда вы вставляете или загружаете CSS, Project Wallace выполняет анализ в WebWorker прямо на вашем устройстве, так что таблица стилей никогда не покидает браузер.
- Разрыв между числом уникальных цветов в поставке и палитрой, заданной дизайном, — это измеримая форма дизайн-дрейфа, и лечится он консолидацией значений в токены, а не удалением правил.
- Всплеск специфичности в середине таблицы стилей обходится дороже, чем высокий максимум в конце, потому что каждому последующему переопределению приходится дотягиваться до этого уровня.
- Пустые правила — единственное исправление в аудите, которое никогда не требует проверки на визуальные регрессии; дублирующиеся объявления перед удалением требуют оценки.
Почему CSS-аудит находит то, чего не находит сборка?
Браузер, встретив CSS-объявление, которое он не может разобрать или не распознаёт, отбрасывает это объявление и продолжает рендерить страницу — именно поэтому таблица стилей может годами копить ошибки, ни разу не уронив сборку. Такое восстановление — специфицированное поведение. Согласно правилам обработки ошибок в CSS Syntax Module Level 3, недостроенное объявление отбрасывается, парсер проскакивает вперёд за ближайшую точку с запятой и оттуда продолжает обычный разбор.
Следствие в том, что повреждения CSS никогда не выглядят как сбой. Они выглядят как дрейф: четвёртый серый, отличающийся от третьего на два пункта; заголовок в 17px между ступенями 16px и 18px; id-селектор, добавленный под давлением дедлайна, а затем !important, добавленный, чтобы его перебить. Ничто из этого ничего не ломает. Всё это делает следующее изменение сложнее.
Как запустить анализатор Project Wallace?
CSS-анализатор Project Wallace принимает ввод тремя способами: URL сайта, загруженный файл или CSS, вставленный напрямую. Когда вы вставляете или загружаете CSS, работа выполняется локально: анализ делает WebWorker на вашем собственном устройстве, и ничто из вставленного никуда не отправляется. Такая архитектура появилась вместе с переписыванием анализатора в 2021 году. Режим URL, разумеется, перед анализом загружает целевой сайт по сети.
На странице ввода есть переключатель «Prettify CSS?», а рядом с ним — примечание, предупреждающее, что эта опция немного смещает цифры. Выберите одно состояние и не меняйте его во всех запусках, которые вы собираетесь сравнивать.
Вот тестовая таблица стилей. Три контрибьютора, два года, один header и один компонент карточки:
/* header.css — three contributors, two years */
#site-header {
background: #f5f5f5;
color: #333333;
font-size: 16px;
}
#site-header .nav-link {
color: #343434;
font-size: 15px;
padding: 8px 12px;
}
.nav-link:hover {
color: #222222 !important;
}
.card {
background: #f4f4f4;
color: #333333;
font-size: 1rem;
padding: 16px;
}
.card .card-title {
font-size: 18px;
color: #333333;
}
.card--featured .card-title {
font-size: 17px;
font-weight: 700 !important;
}
.legacy-banner {
}
.footer {
background: #f5f5f5;
color: #444444;
font-size: 14px;
}
В ответ приходит страница результатов, сгруппированная по тем же категориям, что и документация по метрикам: Stylesheet, Atrules, Rules, Selectors, Declarations, Properties и Values. Метрик там заметно больше сотни. Пять описанных ниже — это те, что превращаются в коммит.
О чём говорят уникальные цвета и font-size?
Разница между общим числом цветов и числом уникальных цветов показывает, насколько часто переиспользуется каждый цвет, а разница между числом уникальных цветов и палитрой, заданной вашей дизайн-системой, показывает, насколько далеко код ушёл от дизайна. Анализатор считает и то, и другое и выдаёт полный список найденных уникальных цветов; с font-size он поступает так же.
Если считать вручную, в тестовой таблице шесть различных hex-строк: #f5f5f5 и #f4f4f4 для поверхностей и #333333, #343434, #222222, #444444 для текста. Дизайн-система для такого компонента почти наверняка предполагала одну поверхность и два цвета текста. С типографической шкалой дело хуже: 16px, 15px, 1rem, 18px, 17px, 14px — это шесть значений как написано, причём 16px и 1rem в большинстве случаев всё равно разрешаются в один и тот же размер в пикселях.
Лечение — консолидация, а не удаление. Консолидировать почти одинаковые оттенки серого не значит удалять правила; это значит заменить каждый литерал ближайшим токеном и дать правилу и дальше делать свою работу:
:root {
--color-surface: #f5f5f5;
--color-text: #333333;
--color-text-muted: #444444;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
}
.card {
background: var(--color-surface);
color: var(--color-text);
font-size: var(--font-size-base);
padding: 16px;
}
Три цветовых токена заменяют шесть литералов; три размерных токена заменяют шесть. Отдельный инструмент Design Tokens от Project Wallace извлекает из существующего CSS кандидатов в цвета и font-size, и на реальной таблице стилей это гораздо более быстрая стартовая точка, чем чтение списка вручную.
Специфичность: всплески важнее максимума
Селектор с высокой специфичностью в конце таблицы стилей — локальная проблема, но такой же селектор в середине заставляет каждое последующее правило, которому нужно его переопределить, подниматься на тот же уровень, и именно так размножаются id-селекторы и флаги !important. График специфичности Гарри Робертса приводит тот же аргумент визуально: тренд должен плавно расти к концу, а любой всплеск — это издержки, которые оплачивает всё, что идёт после него.
Анализатор сообщает специфичность как значение из трёх частей (id, класс, тип) через метрики Maximum selector specificity, Total selectors having maximum specificity и Top specificity selectors, а рядом — Total id selectors, Total !important declarations и Ratio of !important declarations.
Тестовая таблица показывает этот механизм в миниатюре. #site-header .nav-link имеет специфичность (1,1,0). Идущее позже .nav-link:hover со специфичностью (0,2,0) не может его перебить, поэтому контрибьютор потянулся за !important. Один id-селектор породил один флаг !important, а второй флаг на .card--featured .card-title перебивает правило, которое вообще никогда не задавало font-weight.
Счётчик id-селекторов и счётчик !important — это две метрики специфичности, которые команда может снижать по одному коммиту за раз и перезамерять после каждого изменения. В нашем примере смена разметки с id="site-header" на class="site-header" уплощает оба правила header до (0,1,0) и (0,2,0), и оба флага !important становятся ненужными.
Дублирующиеся объявления и пустые правила
Пустое правило добавляет байты и совпадение селектора, но ничего не меняет для пользователя, поэтому его удаление — единственное исправление в аудите, которое никогда не требует проверки на визуальные регрессии. С дублирующимися объявлениями иначе: одно и то же намерение, записанное дважды, — это запах, но удаление не той копии меняет каскад.
Анализатор напрямую считает Total empty rules; в нашем примере такой случай один — .legacy-banner {}, и он идёт под нож. Отдельной метрики для дублирующихся объявлений нет. Сопоставьте Total declarations с Total unique declarations и считайте разрыв количеством дубликатов, помня, что документация до сих пор оставляет открытым вопрос, делают ли пробельные символы или форматирование два объявления различными.
color: #333333 встречается в трёх правилах примера. Правильный ход — не удалить две копии, а провести все три через var(--color-text), что делает повторение видимым как общее решение, а не как совпадение.
Почему размер после gzip скрывает раздутую таблицу стилей?
Gzip эффективно сжимает повторения, поэтому таблица стилей, полная дублирующихся объявлений, может показывать лестный сжатый размер, тогда как её несжатый размер — те самые байты, которые реально парсит браузер, — продолжает расти. Анализатор показывает Uncompressed filesize, Gzip filesize и Gzip filesize compression ratio вместе.
Растущий коэффициент сжатия — вот характерный признак: он означает, что таблица стилей становится более повторяющейся, а не более компактной. На несжатый размер влияет количество правил и объявлений — именно поэтому описанная выше работа по консолидации снижает его как побочный эффект. Размер файла — это диагностический показатель, который читают после остальных исправлений, а не цель для самостоятельной оптимизации.
Отслеживание таблицы стилей от релиза к релизу
Чтобы отслеживать таблицу стилей от релиза к релизу, сохраняйте сырой CSS каждого релиза рядом с результатами его анализа и перезапускайте каждый анализ с одним и тем же состоянием Prettify. CSS Diff viewer форматирует две вставленные таблицы стилей и сравнивает их построчно прямо в браузере, показывая, где сдвинулся счётчик уникальных цветов или id-селекторов.
Заключение
Аудит таблицы стилей окупает потраченное время тогда, когда каждое число отображается в изменение: уникальные цвета и font-size — в токены, id-селекторы и флаги !important — в более плоскую специфичность, пустые правила — в удаление, дубликаты — в общие объявления, а размер файла — в проверку того, что всё остальное сработало. Вставьте свою самую большую продакшен-таблицу стилей в анализатор, выпишите эти пять чисел и откройте по одному pull request на каждое.
FAQ
Можно ли запускать анализатор Project Wallace из командной строки или в CI?
Да. npm-пакет wallace-cli запускает тот же анализатор в терминале: установите его командой npm install wallace-cli, затем выполните wallace path/to/styles.css или передайте CSS через stdin, а для машиночитаемого вывода в CI-скриптах добавьте флаг --json. Версия CLI 4.x требует Node 20.12 или новее. Для программного использования импортируйте функцию analyze из @projectwallace/css-analyzer — это ESM-only пакет, работающий и в Node, и в браузерах.
Что анализировать: исходники Sass или Tailwind либо скомпилированный CSS?
Анализируйте скомпилированный CSS, который получают ваши пользователи, а не исходники Sass, Less или Tailwind. Переменные, миксины и @extend разворачиваются на этапе сборки, поэтому исходные файлы дают неверные данные по уникальным цветам, количеству селекторов и размеру файла. Собственный Stylelint-плагин Project Wallace говорит то же самое о том, куда его направлять: нацеливайте его на бандл, который вы отдаёте, потому что счётчики уникальных значений и построенные на них соотношения описывают именно поставляемый файл, а не исходник. Режим URL и так загружает собранные таблицы стилей, которые отдаёт сайт.
Чем CSS Analyzer от Project Wallace отличается от инструмента CSS Code Quality?
CSS Analyzer выдаёт сырые метрики; инструмент CSS Code Quality берёт этот вывод, прогоняет по нему собственные проверки и сводит результат к трём оценкам по 100-балльной шкале — Performance, Maintainability и Complexity. Используйте анализатор, когда нужно проследить число до конкретного правила, и Code Quality — когда нужна оценочная сводка, которой можно поделиться с командой. Оба принимают URL, загруженные файлы или вставленный CSS.
Как не допустить регресса по уникальным цветам или специфичности после аудита?
Добавьте @projectwallace/stylelint-plugin в конфигурацию Stylelint. Он поставляется с более чем 60 правилами, построенными на том же движке анализа, включая projectwallace/max-unique-colors, а его holistic-пресет оценивает файл целиком (суммы, средние, соотношения, уникальность), а не проверяет по одному узлу за раз. Пресет recommended позволяет начать работу одной строкой extends. Запускайте его на собранном CSS-бандле в CI, чтобы pull request, добавляющий седьмой оттенок серого, падал ещё до merge.