Все цвета — CSS-переменные; тёмная палитра по prefers-color-scheme (режим «авто») и принудительно через data-theme; светлая тема не изменилась. Переключатель — пиктограмма в шапке (авто / светлая / тёмная), выбор в localStorage; theme.js ставит тему до отрисовки CSS (без вспышки, без ослабления CSP). QR 2FA на белой подложке. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
7.2 KiB
Markdown
61 lines
7.2 KiB
Markdown
# Тёмная тема 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`), без изменений.
|