HTML/CSSSeniorExperience

Проект на 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 и безопасность
  • Умеет ранжировать риски по вероятности и влиянию

Related topics