Files
easy-png-tools2/docs/plan-redesign.md
T

30 KiB
Raw Blame History

План: переезд на новый дизайн (refs) — параллельная сборка

Статус: C17 выполнен (docs/archive/plan-c17.md), C18 выполнен, Q3 (удаление старого UI) выполнен. Остался C20 (шаг 6, проверка нового дизайна) — следующий этап. C21 (удалить refs) остаётся отдельной уборкой. Общий tech-debt baseline и следующие quality-задачи зафиксированы в docs/plan-codebase-quality.md. Покупка домена НЕ была блокером C17: домен нужен для S1 (plan-seo.md) — canonical, sitemap, снятие noindex.

Источники: refs/ — Next/React-референс (правда по пикселям и интерактиву), refs-html/ — статические HTML-снимки для быстрого просмотра в браузере.

Историческая стратегия перехода: сначала новый UI собирался отдельно, затем стал production, после чего legacy-ветка была удалена Q3. Текущие критерии относятся только к production UI; старые решения и детали перехода остаются в архивных планах.

0. Почему меняем стратегию

  • Переход выполнялся параллельной сборкой, чтобы новый UI можно было проверить независимо; теперь production использует только новые маршруты и app.css.
  • Дизайн-токены и глобальные стили живут в web/src/app.css; отдельные layout для удалённого UI-v1 больше не нужны.

1. Источники и их оценка

refs-html/ — витрина разметки

Покрытие достаточное:

Файл Что показывает Оценка
index.html Главная: pipeline-воркспейс + превью ✅
demo.html Зеркало index.html (идентичны) ⚠️ дубликат
gradient.html Инструмент «Градиент»: настройки + превью ✅ (тёмная тема)
background-remover.html Инструмент «Удаление фона»: source/result ✅ (тёмная тема)
list-tools.html Каталог инструментов ✅
  • Статика без JS — интерактив (тумблеры/слайдеры/перетаскивание) брать из кода refs/, не из HTML. HTML — только визуальная правда по разметке/классам.
  • По 4 файлам собран инвентарь классов — это и есть спецификация kit'а: app-shell, page-grid, eyebrow, brand-mark, topbar, footer, panel-heading, setting-group, settings-footer, settings-panel, segmented, status, step-card (step-index/step-heading/step-type/ step-body/drag), color-field (swatch/native-color), preview-toolbar/checker (tile-canvas/remover-canvas/comparison-grid), result-meta/meta-row, tool-card/image-card (tool-icon/tool-index/ tool-copy), code-block, icon-btn, download-btn, reset-btn, lede, auto-note, label, toggle/toggle-group, direction-grid, gradient-bar.
  • Дыры в refs-html: нет состояний empty/error/loading, нет глобальных модалок/тултипов, i18n не представлен (все лейблы — английские брендовые). Решения — см. §9.

2. Токены (фундамент нового дизайна)

Живут в src/lib/styles/design2.css как обычный :root { … } (плюс [data-theme='dark'] { … }). Файл грузится только layout'ом новой ветки (§5), поэтому глобально не конфликтует со старым app.css. Маппинг старых токенов web/src/app.css → новые:

Старый Новый Light Dark
--bg --background #eef1f4 #11171d
--surface --panel #f8fafb #182129
--text --foreground #17212b #e8eef2
--text-muted --muted #6d7883 #91a0ac
--border --line #cbd3da #33414c
--accent --blue #1769d2 #54a2ff
— --cyan #00a8c7 #00a8c7
— --amber #bd7411 #bd7411
— --success #25a96a #25a96a (статус-точки)
--danger без изменений #e5484d #ff7479

Прочее:

  • Радиусы: один --radius: 4px (мягкость старой темы уходит).
  • Шрифты: IBM Plex Sans + IBM Plex Mono, self-host через @fontsource (offline-first, CDN нельзя). Mono — рабочий шрифт для лейблов/метаданных/ цифр; Sans — заголовки и текст. Задаются на .app-shell (корень новой ветки), чтобы не зависеть от старых глобальных правил body.
  • Фон-сетка (32px blueprint-grid) и шахматка канвасов — утилиты .page-grid и .checker-canvas внутри .ds2, не токены.
  • Тёмная тема — на [data-theme='dark'] (инлайн-скрипт уже есть в app.html); класс .dark-mode из рефа не переносим, используем наш механизм. На переходный период никаких алиасов старых токенов (старые страницы сами живут на своём app.css).

3. Базовые примитивы дизайн-системы (все — компоненты)

Правило фазы 1: любой повторяющийся паттерн — компонент, даже «просто div с двумя стилями». Никакой копипасты классов между страницами. Каждый примитив ниже — отдельный .svelte в src/lib/components/kit/.

  • MonoLabel — микро-лейбл: mono 10px, uppercase, letter-spacing .1em, muted; слот-модификатор accent (синие eyebrow/version).
  • StatusDot / StatusLine — точка --success + подпись («AUTO PIPELINE», «LIVE PREVIEW»).
  • Panel / PanelHeading / SettingGroup / SettingsFooter — панель с 1px-бордером, составные части с разделителями.
  • CheckerCanvas — рабочая поверхность превью: тёмная подложка + шахматка.
  • MetaRow / MetaList — пары «CAPTION значение».
  • Segmented — сегмент-переключатель (язык, тип градиента, пресеты).
  • Badge — тег типа шага (BACKGROUND/TRANSFORM/STYLE), чип «✓ AUTO».
  • StepCard — нумерованная карточка этапа: индекс 01, drag-handle, Badge типа, заголовок, кнопка удаления; слот для параметров.
  • ToolCard — карточка каталога (icon, index, title, copy, arrow).
  • ImageCard / PreviewTile — label сверху + canvas-превью.
  • AppShell — корневой контейнер .app-shell + .page-grid (обёртка новой ветки маршрутов); задаёт шрифты и фон нового дизайна.
  • BlueprintGrid — фоновая сетка (используется в AppShell).
  • IconButton, Icon (обёртка lucide-svelte, 16–17px, muted).

4. Минимальный UI-kit (src/lib/components/kit/)

Контролы, поверх примитивов §3. API проектируем сразу под новый дизайн (обратной совместимости со старым ui/ не нужно — старые страницы его продолжают использовать).

Компонент Назначение
Button.svelte primary (solid blue) / secondary (outline) / ghost + icon
Field.svelte обёртка: MonoLabel + слот значения/контрола
SliderField.svelte range + <output>, опц. строка подсказок
ColorField.svelte swatch + hex-input + нативный picker
TextField.svelte текстовый ввод в стиле дизайна
SelectField.svelte селект в стиле дизайна
CheckboxField.svelte чекбокс → switch-toggle 32×18
Toggle.svelte переключатель (для toggle-group)
SegmentedControl.svelte на базе Segmented — RU/EN, Linear/Radial, пресеты
Badge.svelte на базе Badge
MetaList.svelte на базе MetaRow
EmptyState.svelte пустое состояние под новый стиль
CodeBlock.svelte блок кода (gradient-art / direction-grid)
DownloadButton.svelte кнопка скачивания (можно переиспользовать идею старой)

Иконки: lucide-svelte (те же пути, MIT), 16–17px, stroke, muted.

5. Изоляция старого и нового (критично)

Без префиксов и обёрток — только за счёт того, какие layout грузят какой CSS.

  • Корневой layout src/routes/+layout.svelte перестаёт импортировать ../app.css. В нём остаётся только общая логика (init темы/locale) и нейтральный <div>{children}</div> без глобальных стилей дизайна.
  • Старая ветка src/routes/(old)/+layout.svelte импортирует app.css и содержит текущую шапку/подвал (разметку из нынешнего корневого layout). Все старые страницы переносятся внутрь (old)/ (группа в скобках не меняет URL): (old)/+page.svelte, (old)/demo/..., (old)/list-tools/..., (old)/tools/[id]/....
  • Новая ветка src/routes/preview/+layout.svelte импортирует design2.css и рендерит <AppShell>. Сюда же — новые страницы (§6).
  • Благодаря code-splitting CSS в SvelteKit: на старых маршрутах грузится только app.css, на preview/* — только design2.css. Скоуп стилей Svelte-компонентов добавляет второй уровень изоляции. Никаких .ds2.
  • Переключатель для ручной проверки: временный флаг previewDesign в localStorage + кнопка в старой шапке («Preview v2»), ведущая на /preview. На фазе 4 флаг убираем.

Почему не (new)-группа с теми же путями вместо preview/*: две группы не могут одновременно определять один и тот же URL ((old)/+page.svelte и (new)/+page.svelte оба → / = конфликт маршрутов). Поэтому новая ветка живёт под префиксом preview/, а на фазе 4 файлы переносятся на реальные пути и группа (old) удаляется.

Это совпадает с рекомендованным в документации SvelteKit паттерном: «put your entire app inside one or more groups except the routes that should not inherit the common layouts» — старое целиком в (old), новое снаружи. Синтаксис @-breakout (+layout@.svelte, +page@.svelte) здесь не требуется: «выломаться» можно лишь до корня, а нам нужно, чтобы именно у корня НЕ было app.css; если позже захотим держать новые маршруты внутри группы, @ пригодится для сброса родительского layout.

6. Маршрутизация: историческая схема перехода

Во время перехода UI-kit проверялся в отдельной ветке, затем его страницы были перенесены на production-маршруты. Эта схема нужна только для восстановления контекста C17; текущая структура описана в разделе 7, отдельных preview- или legacy-layouts в production больше нет.

7. Фазы выполнения

  • C1–C16 — UI-kit и проверка нового дизайна. Токены, компоненты и интерактивные состояния приняты; детали перехода остаются в архивном plan-design-fix.md.
  • C17/C18 — production-переход. Новые страницы перенесены на реальные корневые маршруты; app.css стал единственным production-стилем.
  • Q3/C19 — cleanup. Удалены UI-v1, legacy CSS, v1-тесты, fixtures и isolation-конфигурация; production build и verify проверяются отдельно.
  • C20 — следующий этап. Финальный проход по брейкпоинтам и визуальной приёмке production UI.
  • C21 — отдельная уборка. Удалить refs/ и refs-html/, если они больше не нужны.

Критерии каждого этапа — зелёные test/check/build, отсутствие остаточных legacy-путей в production-коде и ревью в браузере для UI-изменений.

8. Смоук (после фазы 3, контроль на фазе 4)

  1. Светлая и тёмная тема: сетка фона, панели, шахматка канвасов читаются, контраст mono-лейблов достаточный.
  2. Инструмент с параметрами (градиент): slider/color/toggle в стиле setting-groups, автозапуск не регрессировал.
  3. Удаление фона: сравнение source/result, маска, пипетка работают на новой шахматке.
  4. Chain из 3+ инструментов: step-cards рендерятся, удаление/сброс работают.
  5. Узкий экран 800px: настройки над превью, sticky выключается как в рефе.
  6. RU/EN: новые лейблы локализованы; mono-лейблы рефа — либо ключи i18n, либо осознанно английские брендовые элементы (см. §9).

9. Открытые вопросы

  • Имя финального каталога компонентов: решено на C1 — писали в kit/, C17 разложил его в components/ (подпапки fields/layout/ui). На C21 — только навести порядок внутри components/, переименование не требуется.
  • Единый production CSS: app.css подключается в корневом layout; отдельных legacy-layouts и переключателей версий больше нет.
  • Mono-лейблы рефа на английском (PROCESSING PIPELINE): завести i18n- ключи, но допустить часть английских брендовых («приборная панель»).
  • Версия в шапке/футере: из package.json через define или захардкодить — мелочь, решить при сборке topbar.
  • refs/ и refs-html/ — одноразовый источник, НЕ поддерживаемый. После внедрения нового дизайна все правки дизайна делаются сразу в продакшен-коде (design2.css + kit/), а не в refs/. Механизм синхронизации «ref → код» не нужен и не создаётся. refs/ и refs-html/ — только историческая справка; их можно удалить в C21, они не являются частью рантайма.

10. План исполнения (мелкие коммиты)

Ветка feat/redesign. Каждый шаг — отдельный коммит, цель < ~500 строк. Перед стартом: test/build зелёные на базе (якорь регрессии).

Шаг 0. Изоляция layout (без дизайна)

  • [C1] Сделать корневой routes/+layout.svelte нейтральным: убрать import '../app.css', оставить только init темы/locale и <div>{children}</div>.
  • [C2] Создать routes/(old)/+layout.svelte (импорт app.css + старая шапка/подвал) и перенести старые страницы (+page, demo, list-tools, tools/[id]) в (old)/. URL не меняются. Проверка: старый сайт идентичен. (файлы — move, объём строк минимален)

Шаг 1. Фундамент нового дизайна

  • [C3] Создать src/lib/styles/design2.css (токены :root + [data-theme='dark'] по §2) и подключить @fontsource/ibm-plex-sans + @fontsource/ibm-plex-mono (self-host, offline-first). подключить шрифты в design2.css (self-host, offline-first).
  • [C4] (слит в C3: шрифты нельзя подключить до создания design2.css) [data-theme='dark'] (таблица §2). Без утилитарных классов.
  • [C5] Примитивы batch 1: AppShell, BlueprintGrid, CheckerCanvas, MonoLabel, StatusDot/StatusLine.
  • [C6] Примитивы batch 2: Panel, PanelHeading, SettingGroup, SettingsFooter, MetaRow/MetaList, Segmented, Badge, StepCard, ToolCard, ImageCard/PreviewTile.
  • [C7] Контролы: Icon, IconButton, Button, Field, SliderField, ColorField, TextField, SelectField, CheckboxField, Toggle, SegmentedControl (= Segmented из C6), EmptyState, CodeBlock, DownloadButton.

Шаг 2. Витрина kit (проверка компонентов)

  • [C8] preview/+layout.svelte (импорт design2.css + <AppShell> + переключатель темы) и временная preview/kit страница со всеми состояниями (light/dark, hover/disabled, варианты Button/Segmented/Badge и пр.)

Шаг 3. Новые страницы

  • [C9] /preview — воркспейс (eyebrow, h1, lede, поиск, «открыть последний»). Поиск фильтрует TOOLS из $lib/registry; карточки результата пока ведут на # (реальные /preview/tools/[id] подключим на C12, иначе prerender падает на 404).
  • [C10] /preview/list-tools — каталог карточек (группировка по CATEGORIES, ToolCard). Ссылки пока # (реальные /preview/tools/[id] подключим на C12).
  • [C11] /preview/demo — зеркало воркспейса (4 шага + панель превью), статичный приёмочный стенд.
  • [C12] /preview/tools/[id] — data-driven settings-panel + preview-panel (gradient live, остальные — плейсхолдер); NumberField, entries() для prerender, ToolCard-ссылки подключены.
  • [C13] /preview/tools/[id] — comparison-grid для file-source инструментов (Dropzone + before/after CheckerCanvas, result-meta); охватывает background-remover и прочие.
  • [C14] /preview/tools/[id] — исполняемая цепочка: StepCard-шаги (параметры inline) + «Add step» (chainable), живой прогон пайплайна на исходнике, превью каждого шага + итог + Download.

Шаг 4. Переключатель и проверка

  • [C15] В старой шапке ссылка «Preview v2» → /preview, ставит localStorage['easy-png-tools:preview-v2']='1'.
  • [C16] База под дизайн: рабочий переключатель тем (persist в localStorage, применяется на <html>; проверено — фон #eef1f4↔#11171d), TopBar приведён к виду demo.html (бренд + синий хлебный крош /* страница */, безрамочные иконки, язык — active = --foreground/--background), страницы /preview/* на всю ширину (убран max-width). Точечное выравнивание к рефу отложено: реф-дизайн неконсистентен. (доказательство отсутствия регресса). Фиксы по результатам — отдельными мелкими коммитами.

Шаг 5. Сделать новый дизайн основным

C17 — выполнен (docs/archive/plan-c17.md): production-маршруты перенесены в корень, app.css стал корневым. C18 (правки импортов/редиректов после переноса, проверка билда) — выполнен. Q3 удалил старые UI-маршруты и код. Остались C20 и C21.

  • [C17] Перенести preview/* → реальные маршруты (move), удалить (old)/, корневой +layout.svelte импортирует design2.css + новый shell. Убрать флаг/кнопку. Снять noindex, nofollow со страницы инструмента (preview/tools/[id]/+page.svelte) — страницы становятся боевыми лендингами ровно в момент переезда (SEO: plan-seo.md, S1a). Временное решение: если домен ещё не куплен, noindex можно оставить до S1 — тогда C17 делается без снятия noindex, а снятие происходит вместе с подключением canonical в S1. И обновить newCode в web/eslint.config.js: заменить src/routes/preview/** на src/routes/** (после переезда все маршруты — новый дизайн), компонентный glob src/lib/components/kit/** пока оставить. (Итог: newCode обновлён (components/**, routes/**), noindex оставлен до S1 — домен ещё не куплен. Детали: docs/archive/plan-c17.md.)
  • [C18] Правки импортов/редиректов после переноса, проверка билда. (Билд собирается без ошибок, линт/свелт-чека нет — выполнен.)

Шаг 6. Проверка нового дизайна

  • [C20] Финальный проход по брейкпоинтам 1200/1100/800/480; build без предупреждений; grep по старым токенам/классам пуст. Доказательство отсутствия регресса: production-маршруты и основные пользовательские потоки работают.

Шаг 7. Cleanup

  • [C19/Q3] Удалить старое: маршруты routes/v1/**, код lib/v1/**, app_v1.css, v1-тесты, fixtures и isolation-плагин; убрать v1-исключения из конфигурации и активной документации.
  • [C21] Удалить refs/ и refs-html/ из репо — они больше не источник правды.
  • Навести порядок в components/ (согласовать подпапки, финальное имя каталога).

C19/Q3 — отдельная cleanup-задача, не источник пользовательской ценности. После удаления проверяются только production-маршруты и общие модули.

Если какой-то коммит тянет за собой > 500 строк (например, C6/C7), разбивать на под-коммиты по 2–4 компонента. Тесты/билд прогонять после каждого коммита, чтобы регрессия локализовалась одним шагом назад.

Атомарность и точки «поломки» между коммитами.

  • C1+C2 уже сделаны вместе (связаны: нейтральный root без (old)-layout ломает старый сайт). Аналогично связанных пар больше нет.
  • C3 и C4 слиты: шрифты подключаются внутри design2.css, поэтому файл должен существовать в том же коммите.
  • Коммиты C3→C16 не трогают старый сайт (old)/ — они только добавляют новые файлы/маршруты, сломать старый сайт на них нельзя.
  • C17 — атомарный переключатель: перенос preview/* на реальные пути + удаление (old)/ + смена root-layout — в ОДНОМ коммите. Снятие noindex также в C17, если домен уже куплен; иначе — отложить на S1. Не дробить сам C17.
  • Очерёдность зависимостей: design2.css (C3) и kit-компоненты (C5–C7) должны существовать до страниц, которые их импортируют (C8–C14).

Бэклог полировки (после C9–C21, отдельным проходом)

Конкретные расхождения витрины preview/kit с refs-html/. Править системно в конце, а не по ходу сборки базы.

  • Toggle в дизайне прямоугольный, у нас — закруглённый. Сделать прямоугольным (или уточнить радиус у свича отдельно от общих токенов).
  • Радиусы бордеров не совпадают: в refs радиусы вообще 0, а мы взяли ненулевой --radius для всех бордеров. Проверить токен --radius против референсов; возможно он должен быть 0 (или близко), а скругления оставить только там, где они реально есть в дизайне.
  • DownloadButton: нет отступа между текстом и размером файла (size-чип). Добавить gap/отступ внутри кнопки.
  • Поля ввода (controls): выглядят без паддингов (возможно артефакт самой витрины, но визуально плохо). Проверить паддинги в TextField/SelectField/ ColorField/SliderField против референса и поправить.
  • Segmented: на витрине последняя кнопка растянута и нечитаемо, что это сегмент. Проверить, как Segmented выглядит в реальных раскладках (C9–C14); возможно нужен фиксированный/авто размер сегментов или перенос.