Организация проекта на 5000 строк
Структура папок, порядок подключения, борьба с мёртвыми стилями и правила ревью. Всё, что отличает большой проект от разросшегося маленького.
Что ты научишься делать
- Разложишь один большой файл по папкам так, чтобы нужное находилось за секунды
- Заведёшь порядок подключения, при котором ничего не дерётся
- Научишься находить и безопасно удалять мёртвые стили
#Как из порядка получается хаос
Файл на восемьсот строк ещё читается: нужное находится поиском. На двух тысячах поиск перестаёт помогать — совпадений слишком много. На пяти тысячах открывать файл страшно.
Хаос никогда не создаётся сознательно. Он набегает по строчке: правило дописали в конец, потому что «так быстрее», значение продублировали, потому что «в тот раз работало».
Структура нужна не ради красоты. Она отвечает на один вопрос: где лежит то, что я сейчас правлю, и куда положить то, что я сейчас пишу.
#Раскладка по папкам
styles/ main.css подключает всё, задаёт порядок слоёв base/ reset.css сброс браузерных стилей tokens.css переменные: палитра, шкалы, тени typography.css заголовки, текст, ссылки forms.css базовый вид полей layout/ container.css ширины и поля страницы grid.css сетки раскладки header.css шапка и подвал components/ button.css по файлу на компонент card.css modal.css utilities/ spacing.css отступы по шкале visibility.css скрытие, доступностьПравило деления простое и совпадает со слоями каскада из двадцать шестого урока.
base — то, что действует на голые теги. layout — то, что расставляет блоки по странице. components — сущности со своими именами. utilities — одиночные правки, побеждающие всё.
#Порядок подключения
Без слоёв порядок подключения — единственное, что определяет приоритет при равном весе селекторов. Со слоями он перестаёт быть хрупким: даже если файл случайно окажется не на своём месте, слой удержит приоритет.
/* Порядок слоёв объявлен первой строкой — дальше он нерушим */@layer reset, tokens, base, layout, components, utilities;@import url("base/reset.css") layer(reset);@import url("base/tokens.css") layer(tokens);@import url("base/typography.css") layer(base);@import url("base/forms.css") layer(base);@import url("layout/container.css") layer(layout);@import url("layout/grid.css") layer(layout);@import url("layout/header.css") layer(layout);@import url("components/button.css") layer(components);@import url("components/card.css") layer(components);@import url("components/modal.css") layer(components);@import url("utilities/spacing.css") layer(utilities);@import url("utilities/visibility.css") layer(utilities);#Комментарии, которые не врут
Комментарий, повторяющий код, бесполезен и быстро устаревает. Комментарий, объясняющий почему, живёт годами.
Так не надо
/* Отступ 16 пикселей */
padding: 16px;
/* Красный цвет */
color: #c8394a;Комментарий пересказывает код. Поменяется значение — комментарий начнёт врать, и никто этого не заметит.
Так надо
/* 34px — высота липкой шапки: под неё
резервируем место при переходе по якорю */
scroll-padding-top: 34px;
/* Safari до 17 не понимал :has() внутри
@supports — поэтому проверка отдельной строкой */
@supports selector(:has(*)) { … }Объясняет причину и контекст. Такой комментарий отвечает на вопрос, который возникнет через полгода у другого человека.
#Мёртвые стили
В любом проекте старше года есть правила, которые больше нигде не применяются. Опасность не в весе файла, а в том, что их боятся удалять: непонятно, не сломается ли что-то.
Надёжного автоматического способа нет: классы могут добавляться скриптом или приходить из шаблонов на сервере. Но есть порядок действий, который снижает риск почти до нуля.
Найди кандидатов
Вкладка Coverage в инструментах разработчика показывает, какая часть CSS не использовалась при обходе страниц. Это не приговор, а список для проверки.
Проверь поиском по всему проекту
Ищи имя класса не только в разметке, но и в скриптах и шаблонах. Классы, собираемые из кусков строк, поиск не найдёт — это главный риск.
Пометь, а не удаляй
Добавь комментарий
/* кандидат на удаление, проверено 12.03 */и выложи. Через пару недель, если никто не пожаловался, удаляй.Удаляй отдельным коммитом
Один коммит — только удаление, без других правок. Если что-то сломается, откатить будет одно движение.
#Простой стайлгайд
Отдельная страница со всеми компонентами в одном месте окупается быстрее, чем кажется. На ней сразу видно, что кнопок стало пять вместо трёх, а два бейджа отличаются на два пикселя.
Никакого инструмента не нужно: обычная HTML-страница, подключающая те же стили.
<link rel="stylesheet" href="styles/main.css"><main class="container stack gap-8"> <section> <h2>Кнопки</h2> <div class="row gap-2"> <button class="btn btn--primary">Основная</button> <button class="btn btn--ghost">Второстепенная</button> <button class="btn btn--primary" disabled>Недоступная</button> </div> </section> <section> <h2>Карточки</h2> <div class="grid gap-4"> <!-- обычная, со скидкой, без картинки, длинное название --> </div> </section></main>#@supports как страховка
Правило @supports проверяет, понимает ли браузер конструкцию. Оно нужно не всегда: незнакомое свойство и так игнорируется. Но есть два случая, где без него не обойтись.
Первый — когда фоллбэк и улучшение конфликтуют: например, запасная раскладка на флексах и основная на subgrid. Второй — когда улучшение состоит из нескольких свойств, и применить их нужно только все вместе.
/* База: работает везде */.cards { display: flex; flex-wrap: wrap; gap: 16px;}.card { flex: 1 1 240px; }/* Улучшение: применяется целиком или не применяется вовсе */@supports (grid-template-rows: subgrid) { .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); grid-auto-rows: auto 1fr auto; } .card { grid-row: span 3; grid-template-rows: subgrid; }}#Что смотреть на ревью
Короткий список вопросов, который ловит большую часть проблем ещё до того, как они попадут в проект.
- Селектор длиннее двух частей — почему?
- Есть
!important— что он перебивает и нельзя ли иначе? - Новое значение цвета или отступа — почему не из шкалы?
- У компонента появился внешний
margin— точно ли это его забота? - Правило дописано в конец файла — это его место или так вышло?
- Добавлен новый компонент — он есть на стайлгайде?
#Когда НЕ надо
- Двадцать папок на лендинге. Для трёхсот строк структура из пяти каталогов — чистые накладные расходы. Одного файла достаточно, пока поиск работает.
- Заводить структуру задним числом ради структуры. Большая перекладка файлов — риск без немедленной пользы. Вводи новый порядок в новом коде, старый трогай при очередной правке.
- Двадцать
@importбез сборки. Задержка загрузки съест выигрыш от аккуратности. - Стайлгайд, который никто не открывает. Если он устарел на полгода, он вреднее отсутствия: показывает то, чего в проекте уже нет.
Где спотыкаются почти все
Файл называется по странице, а не по компоненту
Почему Файл about.css собирает стили всего, что встретилось на странице «О нас». Кнопка оттуда понадобится на другой странице — и её скопируют.
Как надо Файл называется по сущности: button.css, card.css. Страница — это композиция компонентов.
Порядок подключения держится на честном слове
Почему Кто-то добавил файл в середину, и утилиты перестали побеждать компоненты. Ошибка проявляется в одном месте и ищется долго.
Как надо Объяви слои каскада: тогда приоритет не зависит от порядка строк.
Мёртвые стили удаляют вместе с другими правками
Почему Если что-то сломалось, непонятно, что именно откатывать: удаление или соседнюю правку.
Как надо Удаление — всегда отдельный коммит.
Комментарии пересказывают код
Почему При правке значения комментарий не обновляют. Через год он врёт и сбивает с толку сильнее, чем его отсутствие.
Как надо Пиши почему: причину, ограничение, ссылку на баг.
- Мёртвый CSS dead CSS
- Правила, которые больше не применяются ни к одному элементу.
- Покрытие coverage
- Вкладка DevTools, показывающая, какая часть кода использовалась при обходе страниц.
- Стайлгайд styleguide
- Страница со всеми компонентами в одном месте, включая крайние случаи.
- Проверка поддержки @supports
- Правило, применяющее блок только если браузер понимает конструкцию.
Проверь себя
Иначе кнопку со страницы «О нас» скопируют на другую страницу, и появится второй экземпляр тех же стилей.
Со слоями приоритет задаётся один раз и не зависит от того, в каком порядке подключены файлы.
Такие классы указывают в списке исключений вручную — и регулярно об этом забывают.
Он объясняет причину. Комментарии, пересказывающие код, устаревают при первой же правке значения.
Одиночное незнакомое свойство и так игнорируется. Проверка нужна там, где ветки мешают друг другу.
Задание
Разложить свалку
Перед тобой кусок файла, где всё свалено в кучу: сброс, переменные, компонент и утилита идут вперемешку, а порядок держится на удаче — поэтому утилита .text-center не работает.
Наведи порядок: объяви слои reset, tokens, base, components, utilities и разложи по ним существующие правила. Селекторы не меняй, !important не добавляй.
Подсказка 1
Первой строкой: @layer reset, tokens, base, components, utilities;
Подсказка 2
Дальше просто оберни существующие правила: @layer reset { * { box-sizing: border-box; } }
Подсказка 3
Теговые селекторы h3 и p — это base. Классы .card и .card__title — components. .text-center — utilities.
Показать решение
<article class="card text-center"> <h3 class="card__title">Раф</h3> <p class="card__text">Должен быть по центру</p></article>@layer reset, tokens, base, components, utilities;@layer reset { * { box-sizing: border-box; }}@layer tokens { :root { --space-4: 16px; --color-surface: #f4f4f8; }}@layer base { h3 { margin: 0 0 6px; font-size: 16px; } p { margin: 0; font-size: 14px; }}@layer components { .card { padding: var(--space-4); background: var(--color-surface); border-radius: 12px; text-align: left; } .card__title { color: #16181d; } .card__text { color: #5b6070; }}@layer utilities { .text-center { text-align: center; }}