Задача 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>
This commit is contained in:
1 parent
d3495fb077
commit
a6e70ffd1a
7 files changed
+221
-47
No files matched your search
@@ -0,0 +1,60 @@
|
||||
# Тёмная тема 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`), без изменений.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Итог: тёмная тема UI (изменение 038)
|
||||
|
||||
План: `PLAN.md` (с разделом доработки по проверке пользователя). У UI появилась тёмная тема. По умолчанию тема следует настройке ОС и браузера (`prefers-color-scheme`),
|
||||
её можно переключить пиктограммой в шапке: авто / светлая / тёмная. Выбор хранится в браузере (`localStorage["ipam.theme"]`).
|
||||
|
||||
## Что сделано
|
||||
| Файл | Изменение |
|
||||
|---|---|
|
||||
| `web/styles.css` | Все цвета — CSS-переменные в `:root`, светлые значения прежние. Тёмная палитра в двух одинаковых блоках: `@media (prefers-color-scheme: dark){:root:not([data-theme="light"])}` для режима «авто» и `:root[data-theme="dark"]` для принудительной тёмной; `color-scheme`. Контраст — WCAG AA; в тёмной теме у основных кнопок тёмный текст на светло-синем фоне (белый дал бы ≈2.7:1) |
|
||||
| `web/theme.js` (новый) | Синхронно в `<head>` до CSS ставит `data-theme` по сохранённому выбору — без вспышки светлой темы. Отдельный файл, так как CSP `default-src 'self'` запрещает инлайн-скрипты; CSP не менялся |
|
||||
| `web/index.html` | `<meta name="color-scheme" content="light dark">`, подключение `theme.js` |
|
||||
| `web/app.js` | Цвета (`utilColor`, полосы загрузки, инлайн-стили, иконка логотипа) — через токены. Кнопка-пиктограмма темы в шапке слева от логина (солнце / луна / монитор) с выпадающим списком «Авто (как в системе) / Светлая / Тёмная». QR 2FA — на белой подложке (`.qr-frame`) ради сканируемости |
|
||||
| `README.md` | «Интерфейс» — тёмная тема и переключатель; строка 038 в истории |
|
||||
|
||||
## Доработка по проверке пользователя
|
||||
Сначала переключатель был пунктами «Тема: …» в меню логина. По замечанию пользователя вынесен в отдельную пиктограмму на панели шапки;
|
||||
в меню пользователя остались «Сменить пароль», «Двухфакторная аутентификация», «Выйти».
|
||||
|
||||
## Проверки
|
||||
- Светлая тема не изменилась: во всех 49 объявлениях с цветом прежнего `styles.css` после подстановки светлых токенов значения совпадают (скриптовое сравнение).
|
||||
- Литеральные цвета вне токенов:
|
||||
- `.qr-frame` (белый) — QR 2FA;
|
||||
- стрелка `<select>` — SVG в `url()`, куда переменную не подставить, поэтому она переопределена в тёмных блоках.
|
||||
|
||||
Оба тёмных блока идентичны; заголовок CSP прежний.
|
||||
- `node --check` — чисто; стенд пересобран (`--force-recreate`), отдаёт новые `styles.css`, `app.js`, `theme.js`.
|
||||
- Визуальная проверка обеих тем и переключателя — пользователь.
|
||||
Reference in new issue
Block a user