План архитектурного рефакторинга BELF
Архив. Этот план заменён
docs/quality-gates-refactor-plan.mdкак единственным исполняемым документом для текущего цикла. Он сохранён только как исторический контекст и не должен использоваться как источник параллельных задач или критериев готовности.
1. Назначение документа
Этот документ — исполняемое техническое задание для AI-агента или разработчика. Цель работы — не внести ещё одну локальную правку в navbar, а привести проект к предсказуемой архитектуре, сохранить текущий дизайн и закрыть визуальные регрессии автоматическими проверками.
Работа считается завершённой только тогда, когда выполнены все этапы и пройден quality gate из раздела 10.
2. Контекст проблемы
Текущая версия сайта работает и проходит базовые проверки, но архитектура пока не соответствует требованию «чистый и понятный код» в полной мере.
Основные проблемы:
- стили страниц собраны в крупных монолитных файлах;
- глобальные правила и правила отдельных секций частично смешаны;
- navbar зависит от неявных DOM-контрактов и стилей страниц;
- тема шапки определяется глобальным поиском элементов в DOM;
- отдельные компоненты содержат слишком много независимых обязанностей;
- проверка
npm run checkне включает Playwright; - GitHub Pages может начать публикацию без полного набора проверок;
- автоматические тесты проверяют CSS-свойства navbar, но не гарантируют отсутствие визуального изменения прозрачности на уровне пикселей.
3. Обязательный результат
После рефакторинга проект должен обладать следующими свойствами:
- Navbar одинаково и предсказуемо работает на всех страницах.
- При открытии меню внешний вид шапки не меняется: фон, прозрачность, blur, контраст логотипа, кнопки открытия/закрытия и CTA остаются согласованными с текущей секцией.
- На мобильных устройствах меню является полноэкранной модальной страницей без градиента, просвечивания и смещения основного контента.
- На desktop меню компактное и не превращается в растянутую hero-секцию.
- Стили каждой секции находятся рядом с её компонентом и не протекают на другие страницы.
- Компоненты имеют одну понятную ответственность.
- Контакты, данные навигации и повторяемый контент имеют один источник истины.
- Любой pull request проходит форматирование, линтеры, unit/component tests, production build и браузерные тесты до публикации.
- Внешний вид на поддерживаемых разрешениях не ухудшается относительно исходной версии.
4. Неприкосновенные ограничения
- Не менять дизайн, тексты, маршруты, SEO и пользовательский сценарий без прямого требования.
- Не добавлять градиенты в navbar или его overlay.
- Не менять прозрачность шапки только из-за открытия или закрытия меню.
- Не размещать стили navbar в CSS страниц.
- Не использовать
!importantкак способ скрыть архитектурную проблему. - Не отключать правила ESLint, Stylelint, TypeScript или тесты ради зелёного CI.
- Не удалять существующие тесты без равноценной или более сильной замены.
- Не обновлять visual snapshots вслепую: каждое изменение сначала просмотреть.
- Использовать иконки из уже установленного
react-icons; не добавлять ещё одну библиотеку иконок без необходимости. - Сохранять SSR/build-поведение и существующие пути
/,/partners/,/careers/. - Не коммитить и не отправлять изменения в remote без отдельного указания владельца репозитория.
- Не стирать и не перезаписывать несвязанные изменения в рабочем дереве.
5. Целевая структура
Ориентир для структуры проекта:
src/
app/
entrypoints/
mountApp.tsx
components/
navigation/
SiteHeader/
SiteHeader.tsx
SiteHeader.module.css
SiteHeader.test.tsx
NavigationOverlay/
NavigationOverlay.tsx
NavigationOverlay.module.css
NavigationGroups/
NavigationContact/
model/
navigation.types.ts
useHeaderTheme.ts
useNavigationDialog.ts
footer/
ui/
features/
calculator/
components/
model/
tests/
pages/
home/
HomePage.tsx
sections/
HeroSection/
PlatformSection/
ModulesSection/
...
partners/
PartnersPage.tsx
sections/
careers/
CareersPage.tsx
sections/
shared/
data/
types/
utils/
styles/
fonts.css
tokens.css
reset.css
global.css
Это ориентир, а не требование механически переименовать каждый файл. Любое отклонение допустимо, если оно уменьшает связанность и остаётся последовательным.
6. План выполнения
Этап 0. Зафиксировать исходное состояние
Перед первой правкой:
- Выполнить
git status --shortи сохранить список уже существующих изменений. - Выполнить
npm run checkиnpm run test:e2e. - Снять эталонные скриншоты всех страниц с закрытым и открытым navbar на
разрешениях:
- 390 × 844;
- 430 × 932;
- 768 × 1024;
- 1024 × 768;
- 1440 × 900;
- 1920 × 1080.
- Отдельно зафиксировать navbar над светлой секцией, над тёмной/зелёной секцией и немного ниже hero-секции.
- Не начинать рефакторинг, если baseline уже красный: сначала описать исходную ошибку и отделить её от новых изменений.
Этап 1. Установить архитектурные границы
- Оставить в глобальных CSS только:
- подключение шрифтов;
- design tokens;
- reset;
- базовые правила
html,body,main; - действительно общие утилиты.
- Удалить из page CSS глобальные правила для
html,body,main,.containerи общих[id]. - Запретить страницам стилизовать внутренние классы navbar и footer.
- Перенести данные, общие для нескольких страниц, в единый модуль
shared/dataлибо сохранить вsrc/data, если перенос не даёт реальной пользы.
Критерий завершения: глобальные стили не содержат правила конкретных секций, а компонентные стили не зависят от порядка подключения CSS страниц.
Этап 2. Изолировать navbar
- Собрать
SiteHeader,NavigationOverlay,NavigationGroupsиNavigationContactв самостоятельный модуль. - Перевести их стили на CSS Modules или другую локально изолированную схему.
- Убрать зависимость от page-класса
link-icon; размеры иконок должны задаваться самим компонентом, который ими владеет. - Ввести явную модель состояния:
type HeaderTone = "light" | "dark";
type NavigationState = {
isOpen: boolean;
headerTone: HeaderTone;
mode: "desktop" | "mobile";
};
- Открытие меню должно менять только состояние диалога. Оно не должно переопределять фон, opacity, backdrop-filter или tone шапки.
- Крестик и логотип получают контрастный цвет из одного вычисленного
headerTone, а не из набора несвязанных CSS-исключений. - Mobile overlay должен иметь сплошной зелёный фон,
position: fixed, корректныйinset, собственный scroll и слой выше страницы. - Desktop overlay должен иметь ограниченную ширину/высоту, достаточные внутренние отступы и не копировать композицию hero.
Критерий завершения: navbar визуально одинаков до и после открытия, кроме появления самого меню и замены menu icon на close icon.
Этап 3. Сделать тему шапки явной
Текущий глобальный поиск [data-nav-theme] заменить на явный контракт.
Предпочтительный вариант:
- Создать
NavThemeRegion, который регистрирует секцию и еёHeaderTone. - Хранить реестр секций в React context.
- Использовать один
IntersectionObserverдля определения активной секции под контрольной линией шапки. - Вынести высоту/контрольную точку шапки в именованную константу или CSS custom
property; не использовать magic number
42. - На страницах явно оборачивать только те секции, где меняется контраст.
Допустим более простой вариант с props, если он одинаково работает со всеми
страницами и не возвращает глобальный querySelectorAll.
Критерий завершения: по коду страницы видно, какая секция требует светлую или тёмную шапку; механизм не зависит от случайных DOM-атрибутов.
Этап 4. Разрезать монолитные стили
Мигрировать не весь CSS одним большим переписыванием, а по одной секции:
- Выбрать секцию.
- Перенести её компонент и стили в одну директорию.
- Локализовать селекторы.
- Проверить страницу визуально на всех контрольных ширинах.
- Удалить перенесённые правила из монолитного файла.
- Запустить Stylelint и релевантные Playwright-тесты.
- Только после этого переходить к следующей секции.
Очередность:
- navigation;
- footer;
- home hero;
- calculator;
- остальные home sections;
- partners;
- careers.
Итог: home.css, partners.css и careers.css должны исчезнуть либо остаться
тонкими файлами композиции без стилей отдельных секций.
Этап 5. Нормализовать design tokens
- Проинвентаризировать цвета, отступы, радиусы, border, shadow и типографику.
- Создать семантические токены, например:
:root {
--color-brand: #159334;
--color-brand-dark: #07531f;
--color-surface: #ffffff;
--color-surface-muted: #f3f6f3;
--color-text: #111411;
--color-text-on-brand: #ffffff;
--color-border-subtle: rgb(17 20 17 / 14%);
}
- Не создавать токен на каждый единичный hex. Токены должны выражать назначение, а не просто переименовывать цвет.
- Удалять hardcoded-значения постепенно, одновременно с переносом секций.
Критерий завершения: повторяемые визуальные решения используют семантические токены; уникальные иллюстративные значения могут оставаться локальными.
Этап 6. Разделить крупные компоненты
- Разбить
CalculatorSectionна:- оболочку секции;
- форму конфигурации;
- элементы управления;
- сводку/результат;
- чистую модель расчёта;
- форматирование результата.
- Разбить
HeroSection, только если дочерние части имеют самостоятельную ответственность; не создавать компоненты ради каждой строки JSX. - Разделить
PartnersProgramSectionsиCareersSectionsпо принципу «одна смысловая секция — один компонент». - Оставить page-компоненты тонкой композицией секций.
- Вынести
mailto, clipboard и форматирование контактов из JSX в тестируемые функции.
Критерий завершения: компонент можно понять без чтения нескольких несвязанных сценариев в одном файле.
Этап 7. Усилить тесты navbar
Unit/component tests должны проверять:
- открытие и закрытие кнопкой;
- закрытие по
Escape; - возврат фокуса на trigger;
- focus trap;
- блокировку scroll;
- закрытие после выбора ссылки;
- корректные
aria-expanded,aria-controls,role="dialog"и accessible name; - единый источник контактов;
- вычисление контрастной темы на светлой и зелёной секции.
Playwright должен проверять для каждого маршрута и viewport:
- отсутствие горизонтального overflow;
- открытие/закрытие меню;
- mobile full-screen overlay;
- desktop compact overlay;
- сохранение computed
background-color,opacity,backdrop-filterи цвета logo/button до и после открытия; - отсутствие смещения документа;
- корректную прокрутку длинного мобильного меню;
- navbar над hero и ниже hero;
- работоспособность клавиатурной навигации.
Этап 8. Добавить visual regression
- Добавить Playwright screenshots для navbar в ключевых состояниях.
- Использовать стабильные шрифты, отключённые переходы и ожидаемую загрузку ресурсов перед снимком.
- Начальный порог
maxDiffPixelRatio: не более0.003. - Если допустимое динамическое содержимое создаёт шум, маскировать только этот конкретный элемент.
- Запрещено повышать порог глобально, чтобы скрыть реальную регрессию.
Критерий завершения: изменение прозрачности, неожиданный градиент, неверный цвет крестика и смещение панели обнаруживаются автоматически.
Этап 9. Встроить quality gate в CI
В package.json добавить единый публичный скрипт:
{
"scripts": {
"quality": "npm run check && npm run test:e2e"
}
}
GitHub Actions разделить минимум на следующие зависимости:
quality ─┐
├──> deploy
e2e ─────┘
Возможны два варианта:
- один job выполняет
npm run quality, аdeployзависит от него; - отдельные jobs
qualityиe2e, аdeployимеетneeds: [quality, e2e].
Обязательные условия CI:
- использовать
npm ci; - зафиксировать поддерживаемую версию Node;
- установить Playwright Chromium с системными зависимостями;
- кэшировать npm и браузер только безопасным штатным способом;
- загружать Playwright report при падении;
- не запускать deploy при красном quality gate;
- публиковать только артефакт, построенный после успешных проверок.
7. Архитектурные лимиты
Лимиты служат сигналом для ревью, а не поводом искусственно дробить код:
- page-компонент: желательно до 100 строк;
- обычный UI-компонент: желательно до 200 строк;
- сложный feature-компонент: желательно до 250 строк;
- CSS Module секции: желательно до 300 строк;
- один глобальный CSS-файл: желательно до 150 строк;
- cyclomatic complexity функции: желательно не выше 10;
- public component props должны иметь явный TypeScript-тип;
- бизнес-логика не должна жить в CSS или разметке страницы.
Превышение допустимо только с кратким объяснением в отчёте.
8. Обязательная проверка после каждого этапа
После каждого законченного этапа выполнить:
npm run format:check
npm run lint
npm run lint:css
npm run typecheck
npm run test
npm run build
После изменений UI дополнительно выполнить:
npm run test:e2e
После добавления финального скрипта итоговая команда:
npm run quality
Также обязательно:
git diff --check
git status --short
9. Порядок работы AI-агента
AI-агент должен работать небольшими проверяемыми итерациями.
Для каждого этапа:
- назвать конкретную проблему;
- перечислить файлы, которые планируется менять;
- внести минимально достаточное изменение;
- выполнить релевантные проверки;
- визуально проверить результат, если затронут UI;
- сравнить с baseline;
- сообщить, что изменилось и какие риски остались;
- не переходить к следующему этапу при красной проверке.
Если обнаружена несвязанная ошибка или чужие изменения:
- не исправлять их автоматически;
- зафиксировать факт в отчёте;
- продолжать только если их можно безопасно обойти.
10. Финальный quality gate
Работа может быть объявлена завершённой только при одновременном выполнении всех условий:
-
npm run qualityзавершился с кодом0; -
git diff --checkне нашёл проблем; - все три маршрута открываются напрямую и после навигации;
- navbar проверен на всех шести viewport;
- кнопка закрытия контрастна на светлом и зелёном фоне;
- открытие navbar не меняет прозрачность/фон шапки;
- на мобильном navbar полностью перекрывает экран;
- на desktop navbar остаётся компактным;
- в navbar нет градиента;
- отсутствуют горизонтальные overflow и layout shift;
- клавиатурная навигация и focus management работают;
- visual regression snapshots просмотрены человеком;
- deploy зависит от успешных quality/e2e jobs;
- монолитные page CSS устранены или сведены к композиционному минимуму;
- page-компоненты не содержат стили и внутреннюю логику navbar;
- контакты и навигационные данные имеют один источник истины;
- README отражает фактические команды и архитектуру;
- ни одно несвязанное пользовательское изменение не потеряно;
- изменения не отправлены в remote без отдельного разрешения.
11. Формат итогового отчёта
AI-агент обязан закончить работу отчётом следующей структуры:
Результат
- что исправлено для пользователя;
- что изменено в архитектуре.
Изменённые области
- список модулей и их новых обязанностей.
Проверки
- команда: результат;
- команда: результат;
- visual review: маршруты и viewport.
Quality gate
- каждый пункт: PASS/FAIL.
Оставшиеся риски
- конкретный риск и причина;
- либо «не обнаружены».
Git
- изменённые файлы;
- коммиты, если владелец отдельно разрешил их создать;
- статус push/deploy.
Фраза «всё готово» недопустима, если хотя бы один обязательный пункт quality
gate имеет статус FAIL или не был проверен.