Files
ipam_control/docs/changes/038-dark-theme/PLAN.md
T
ayurishchevandClaude Opus 5.5 a6e70ffd1a Задача 038: тёмная тема UI
Все цвета — CSS-переменные; тёмная палитра по prefers-color-scheme (режим «авто»)
и принудительно через data-theme; светлая тема не изменилась. Переключатель —
пиктограмма в шапке (авто / светлая / тёмная), выбор в localStorage; theme.js ставит
тему до отрисовки CSS (без вспышки, без ослабления CSP). QR 2FA на белой подложке.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:44:48 +03:00

61 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Тёмная тема UI (изменение 038)
## Context
Сейчас у UI только светлая тема. Нужна тёмная. Решение пользователя: по умолчанию тема следует настройке ОС и браузера
(`prefers-color-scheme`), в меню логина есть переключатель «Тема: авто / светлая / тёмная». Выбор хранится в браузере (`localStorage`).
На экране входа тема тоже берётся по системе или по сохранённому выбору.
Сейчас в `web/styles.css` есть токены в `:root` (`--bg`, `--card`, `--line`, `--text`, `--muted`, `--primary`, `--head`, `--hover` …),
но около 36 цветов прописаны прямо в правилах: бейджи, баннеры, тени, `#fff` у кнопок и `.pop`, `.org-btn` и т. п.
Ещё около 16 цветов задано в `web/app.js`: `utilColor`, цвета загрузки, инлайн-стили.
## Изменения
1. **Токены.** Все цвета в `styles.css` перевести на CSS-переменные в `:root`. Для каждого прописанного цвета —
семантический токен: `--card`, `--input-bg`, `--shadow`, `--badge-*-bg/fg`, `--danger`, `--warn`, `--ok`, `--primary-soft`, `--overlay` …
Светлые значения остаются прежними: светлая тема визуально не меняется.
2. **Тёмная палитра.** Та же структура токенов в двух местах:
- `@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { … } }` — режим «авто»;
- `:root[data-theme="dark"] { … }` — принудительная тёмная.
Плюс `color-scheme: light` / `dark` — нативные поля ввода, скроллбары, автозаполнение.
Палитра: фон `#0f1419`–`#151b23`, карточки на ступень светлее, линии приглушённые, текст `#e6edf3`, `--muted` около `#9aa7b8`,
`--primary` светлее для контраста около `#6b9bff`. Бейджи — тёмный насыщенный фон и светлый текст.
Контраст текста не ниже WCAG AA.
3. **Цвета в `web/app.js`.** Прописанные цвета (`utilColor`, инлайн `style="color:#…"`, фон полос загрузки) заменить на
`var(--…)` или классы. Логика выбора (порог загрузки → зелёный/жёлтый/красный) не меняется.
Особые случаи:
- QR-код 2FA остаётся тёмным на белом: сканеры хуже читают инвертированный. Его контейнеру — белый фон с отступом.
- Коды восстановления и моноширинные блоки — через токены.
4. **Переключатель.**
- `web/theme.js` (новый), подключается в `<head>` `web/index.html` обычным синхронным `<script src="theme.js">` до CSS.
Скрипт читает `localStorage["ipam.theme"]` (`light`/`dark`; нет значения — «авто») и ставит `data-theme` на `<html>`,
поэтому нет вспышки светлой темы. Инлайн-скрипт нельзя: CSP `default-src 'self'` в `app/main.py` его запрещает, ослаблять CSP не нужно.
- `web/app.js`: в меню логина пункт «Тема: <текущая>» → вложенный выбор «Авто / Светлая / Тёмная»
либо циклическое переключение — выбрать то, что проще вписывается в `popMenu`. Запись в `localStorage`
(в `try/catch`, как у `store`), обновление `data-theme` без перезагрузки.
- `<meta name="color-scheme" content="light dark">`.
5. **Документация.** `README.md` («Интерфейс»: тёмная тема, режимы, где хранится выбор; строка 038 в истории);
`SUMMARY.md` по завершении.
## Исполнение
По принятой схеме: код пишет агент на Sonnet, он же пересобирает стенд с `--force-recreate`. Тесты не нужны: API не меняется.
Моя часть — ревью: не осталось прописанных цветов, светлые значения совпадают с прежними, CSP не нарушен.
Визуальная проверка — пользователь.
## Проверка
- `grep` по `#[0-9a-f]{3,6}` и `rgb(` в `styles.css` вне блоков токенов, а также в `app.js` — пусто или обоснованные исключения (QR).
- Светлая тема визуально как раньше. Тёмная: все экраны, диалоги, выпадающие списки, бейджи, журнал, экран входа, 2FA.
- Переключатель: авто следует ОС, выбор переживает перезагрузку, нет вспышки при загрузке.
- Стенд отдаёт новые `styles.css`, `app.js`, `index.html`; CSP-заголовок не менялся, в консоли нет нарушений CSP.
## Доработка по проверке пользователя: переключатель темы — отдельная пиктограмма в шапке
Переключатель не должен быть частью меню пользователя. Вместо пунктов «Тема: …» в `popMenu("user", …)`:
- **Кнопка.** Отдельная кнопка-пиктограмма в шапке (`shell()`) слева от логина; `iconBtn` или `btn ghost` без текста, `aria-label` и `title` —
«Тема: авто/светлая/тёмная». Пиктограмма отражает текущий режим: солнце — светлая, луна — тёмная, монитор — авто (по системе).
Иконки — inline SVG в стиле существующего набора `I` (`stroke="currentColor"`, 16px).
- **Выбор.** По клику — общий выпадающий список `popMenu("theme", […], "row-pop")`: «Авто (как в системе)», «Светлая», «Тёмная»,
у текущей — `cls:"sel"`. Выбор — ветка `id === "theme"` в `actions.pick`, существующая `setTheme()`, затем `draw()`.
- **Меню пользователя.** Возвращается к трём пунктам: «Сменить пароль», «Двухфакторная аутентификация», «Выйти».
- **Экран входа.** Шапки нет, тема берётся по системе или по сохранённому выбору (`theme.js`), без изменений.