Проект на HTML/CSS вырос из небольшого интерфейса в большой продукт. Какие архитектурные проблемы вы ожидаете увидеть?
При росте проекта CSS деградирует в сторону специфичности-гонок, неконтролируемого глобального пространства имён, дублирования стилей и отсутствия дизайн-токенов. Решение — BEM/CSS Modules/Tailwind + design tokens + @layer + регулярный аудит.
Архитектурные проблемы растущего CSS-проекта
Небольшой интерфейс с плоской структурой CSS неизбежно приходит к деградации при масштабировании команды и функциональности. Вот конкретные симптомы и решения.
1. Война специфичности (Specificity Wars)
Разработчики добавляют !important и увеличивают специфичность селекторов, чтобы переопределить чужие стили. Итог — каскад становится непредсказуемым.
/* Типичная деградация */
.sidebar .widget .title { color: blue; } /* specificity: 0,3,0 */
.sidebar .widget .title.active { color: red; } /* 0,4,0 */
#main .sidebar .widget .title { color: green !important; } /* 1,3,0 + !important */
Решение — CSS Cascade Layers (@layer):
@layer reset, tokens, base, components, utilities;
@layer reset {
*, *::before, *::after { box-sizing: border-box; margin: 0; }
}
@layer tokens {
:root {
--color-primary: #c28b00;
--spacing-md: 1rem;
}
}
@layer components {
.card { padding: var(--spacing-md); border-radius: 12px; }
}
@layer utilities {
.mt-auto { margin-top: auto; }
}
Слои имеют явный приоритет независимо от специфичности — utilities всегда выигрывает у components, даже с меньшей специфичностью.
2. Глобальное пространство имён
В большом проекте классы из разных компонентов конфликтуют. Решения:
- CSS Modules — классы локализуются автоматически:
.card→.Card_card__x7j2k. - BEM — явное именование:
.job-card__title--featured. - Tailwind CSS — утилитарные классы без глобальных конфликтов по определению.
// CSS Modules в Next.js
import styles from './JobCard.module.css';
export function JobCard({ title }) {
return <div className={styles.card}><h2 className={styles.title}>{title}</h2></div>;
}
3. Дублирование стилей и отсутствие дизайн-токенов
Цвет #c28b00 встречается в 47 местах — потом его меняют в 23, а в остальных 24 забывают.
/* Правильно: CSS Custom Properties как дизайн-токены */
:root {
--color-gold: #c28b00;
--color-gold-dark: #a07200;
--radius-card: 12px;
--font-size-body: 1rem;
}
.button-primary {
background: var(--color-gold);
border-radius: var(--radius-card);
}
4. Неиспользуемый CSS (Dead CSS)
Компоненты удаляют из кода, но стили остаются. Через год — 40% CSS не используется.
- PurgeCSS в CI-пайплайне автоматически удаляет неиспользуемые правила.
- Chrome Coverage DevTools → вкладка Coverage показывает % неиспользованного CSS.
5. Отсутствие архитектурного гайда
Каждый разработчик пишет CSS по-своему. Нужны явные договорённости:
- Выбор методологии (BEM / Tailwind / CSS Modules) и запрет смешивания.
- Линтинг:
stylelint+stylelint-config-standard+ кастомные правила. - Запрет
!importantчерезstylelint-declaration-no-important.
// .stylelintrc.json
{
"extends": ["stylelint-config-standard"],
"rules": {
"declaration-no-important": true,
"selector-max-id": 0,
"color-no-invalid-hex": true
}
}
6. Responsive Design без системы
Breakpoints разбросаны по файлам с «магическими» значениями (768, 992, 1024, 1280). При смене дизайна нужно менять в десятках мест.
/* Правильно: tokens + custom media */
@custom-media --mobile (max-width: 639px);
@custom-media --tablet (min-width: 640px) and (max-width: 1023px);
@custom-media --desktop (min-width: 1024px);
@media (--mobile) {
.grid { grid-template-columns: 1fr; }
}
Подводные камни
- Миграция методологий на ходу: переход с global CSS на CSS Modules в работающем проекте требует полного рефакторинга — лучше определить методологию в самом начале.
- Tailwind + CSS Modules одновременно: смешивание двух подходов в одном проекте приводит к разногласиям в команде и непредсказуемому поведению.
- CSS Custom Properties в IE11: если нужна поддержка IE, custom properties требуют полифила — postcss-custom-properties.
- @layer и старые браузеры: Cascade Layers поддерживаются с Chrome 99, Safari 15.4 — для legacy-окружений нужен fallback.
- Дизайн-токены без single source of truth: если токены хранятся и в Figma-переменных, и в CSS, и в JS — они рассинхронизируются. Нужен генератор (Style Dictionary).
- Отсутствие CSS-ревью в PR: CSS-изменения часто проходят без ревью как «просто стили» — технический долг накапливается незаметно.
- specificity 0,0,0 в @layer utilities Tailwind: кастомные компонентные стили могут случайно переопределяться утилитами — важно понимать приоритет слоёв.
What hurts your answer
- Говорить только о запуске HTML/CSS, но не об эксплуатации
- Не упоминать observability, обновления, безопасность и rollback
- Описывать риски абстрактно, без способов их снижать
What they're listening for
- Видит production-риски HTML/CSS
- Говорит про monitoring, rollout, rollback и безопасность
- Умеет ранжировать риски по вероятности и влиянию