feat: theme forms tables and data states
This commit is contained in:
@@ -93,7 +93,7 @@ FastAPI ─ PostgreSQL 17
|
||||
|
||||
Production CSP ограничивает browser-запросы текущим доменом и отдельным files-доменом для изображений, запрещает plugins, frames, inline handlers и attributes, eval, wildcard и HTTP. Page scripts и scoped styles принудительно выпускаются отдельными same-origin `_astro`-ассетами; единственное временное inline-исключение остаётся для динамического JSON-LD и описано в [CSP inventory](docs/csp-inventory.md).
|
||||
|
||||
Тема по умолчанию следует системному `prefers-color-scheme`, а переключатель в header позволяет выбрать системную, светлую или тёмную палитру. Выбор сохраняется в cookie и применяется Astro при SSR без localStorage-only flash и ослабления CSP. Базовые поверхности, каталог, навигационная леска, паспорта данных и состояния загрузки уже используют семантические light/dark-токены; ограничения дальнейшей миграции описаны в [dark-theme.md](docs/dark-theme.md).
|
||||
Тема по умолчанию следует системному `prefers-color-scheme`, а переключатель в header позволяет выбрать системную, светлую или тёмную палитру. Выбор сохраняется в cookie и применяется Astro при SSR без localStorage-only flash и ослабления CSP. Базовые поверхности, каталог, формы, таблицы, moderation-карточки, provenance/status-плашки, навигационная леска, паспорта данных и состояния загрузки уже используют семантические light/dark-токены; ограничения дальнейшей миграции описаны в [dark-theme.md](docs/dark-theme.md).
|
||||
|
||||
Gitea Actions workflow `.gitea/workflows/ci.yml` на каждый push и pull request проверяет Python, миграции на чистой PostgreSQL, Astro build и полный Compose/Playwright-сценарий. При падении E2E сохраняются логи контейнеров и Playwright-артефакты.
|
||||
|
||||
|
||||
@@ -14,5 +14,5 @@ const { catches } = Astro.props as { catches: Catch[] };
|
||||
})}
|
||||
</div>
|
||||
<style>
|
||||
time{margin-top:7px;color:#496357;font-size:10px;font-weight:750}
|
||||
time{margin-top:7px;color:var(--text-secondary);font-size:10px;font-weight:750}
|
||||
</style>
|
||||
|
||||
@@ -31,6 +31,6 @@ const groups = [...signals.reduce((map, signal) => {
|
||||
})}</div>
|
||||
</section>
|
||||
<style>
|
||||
.repeat-note{position:relative;z-index:1;margin:-8px 0 10px!important;padding:6px 9px;border-left:2px solid #6b8d74;color:#496357!important;font-size:10px!important}
|
||||
.repeat-note{position:relative;z-index:1;margin:-8px 0 10px!important;padding:6px 9px;border-left:2px solid var(--border-strong);color:var(--text-secondary)!important;font-size:10px!important}
|
||||
.signal-card__top .source-strip{margin:0}
|
||||
</style>
|
||||
|
||||
@@ -35,6 +35,15 @@
|
||||
--decorative-water: light-dark(#315f63, #72a9ad);
|
||||
--skeleton: light-dark(#e7ede7, #1c3537);
|
||||
--skeleton-strong: light-dark(#d8e2d8, #294548);
|
||||
--status-healthy: light-dark(#5f8c26, #a8d85e);
|
||||
--status-warning: light-dark(#9a741d, #e8c768);
|
||||
--status-changed: light-dark(#a44236, #ff9587);
|
||||
--source-official: light-dark(#815713, #e3b65b);
|
||||
--source-rf4db: light-dark(#286581, #74bee3);
|
||||
--source-rf4stat: light-dark(#60499a, #b7a2ed);
|
||||
--source-rf4map: light-dark(#2f704f, #79c79e);
|
||||
--source-rf4posts: light-dark(#984a33, #ee9a7e);
|
||||
--source-players: light-dark(#526b1c, #add066);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] { color-scheme: light; }
|
||||
@@ -84,7 +93,13 @@
|
||||
.signal-card dt { color: light-dark(#776f56, #b9ae82); }
|
||||
.signal-card .missing-note { background: light-dark(#efe5bd, #453c20); color: light-dark(#6b550f, #f0d889); }
|
||||
.signal-card .data-passport { background: light-dark(#fffdf7, #172628); border-color: light-dark(#ddd3af, #59605a); }
|
||||
.source-chip { background: color-mix(in srgb, var(--chip) 24%, var(--surface-elevated)); }
|
||||
.source-chip { background: color-mix(in srgb, var(--chip) 24%, var(--surface-elevated)); box-shadow: 0 5px 14px var(--shadow-color); }
|
||||
.source-chip[data-source="rf4-official"] { --chip: var(--source-official); }
|
||||
.source-chip[data-source="rf4db"] { --chip: var(--source-rf4db); }
|
||||
.source-chip[data-source^="rf4stat"] { --chip: var(--source-rf4stat); }
|
||||
.source-chip[data-source="rf4map"] { --chip: var(--source-rf4map); }
|
||||
.source-chip[data-source="rf4posts-spot"] { --chip: var(--source-rf4posts); }
|
||||
.source-chip[data-source="players"] { --chip: var(--source-players); }
|
||||
.notice.success { background: var(--success-soft); color: var(--success); }
|
||||
.notice.warning, .data-quality { background: var(--warning-soft); color: var(--warning); }
|
||||
.notice.error { background: var(--danger-soft); color: var(--danger); }
|
||||
@@ -120,6 +135,29 @@
|
||||
.state::before { border-color: var(--border-strong); }
|
||||
.state::after { border-color: var(--decorative-water); box-shadow: 0 -7px 0 -6px var(--decorative-water),0 7px 0 -6px var(--decorative-water); }
|
||||
.data-passport header strong[data-passport-status="unverified"] { background: var(--info-soft); color: var(--info); }
|
||||
|
||||
.record-table, .report-form, .moderation-card { background: var(--surface-elevated); border-color: var(--border-subtle); box-shadow: 0 30px 80px var(--shadow-color); }
|
||||
.record-row, .optional-fields, .moderation-summary dl div, .moderation-actions { border-color: var(--border-subtle); }
|
||||
.record-head, .no-proof { background: var(--surface-raised); }
|
||||
.record-head span, .record-row > span, .record-row > time, .report-form label,
|
||||
.field-help, .privacy, .moderation-summary > p, .moderation-summary dt,
|
||||
.moderation-actions label { color: var(--text-secondary); }
|
||||
.report-form input, .report-form textarea, .report-form select,
|
||||
.moderation-actions textarea { background: var(--surface-control); color: var(--text-primary); border-color: var(--border-strong); }
|
||||
.report-form input:focus, .report-form textarea:focus, .report-form select:focus { border-color: var(--focus); box-shadow: 0 0 0 3px color-mix(in srgb,var(--focus) 28%,transparent); }
|
||||
.report-form input:user-invalid, .report-form select:user-invalid { border-color: var(--danger); }
|
||||
.moderation-summary blockquote { background: var(--surface-raised); color: var(--text-secondary); }
|
||||
|
||||
.data-legend { border-color: var(--border-strong); }
|
||||
.data-legend__intro p, .legend-sources > div > span, .legend-quality span { color: var(--text-secondary); }
|
||||
.legend-sources > div { border-color: var(--border-subtle); }
|
||||
.legend-quality > div { background: var(--surface-elevated); }
|
||||
.legend-quality i { background: var(--success-soft); color: var(--success); }
|
||||
.legend-quality i[data-quality="unverified"] { background: var(--info-soft); color: var(--info); }
|
||||
.legend-quality i[data-quality="incomplete"] { background: var(--warning-soft); color: var(--warning); }
|
||||
.health-state i { background: var(--status-warning); }
|
||||
.source-health-grid [data-status="healthy"] .health-state i { background: var(--status-healthy); }
|
||||
.source-health-grid [data-status="source_changed"] .health-state i { background: var(--status-changed); }
|
||||
@media print {
|
||||
:root { color-scheme: light; }
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
- [x] **D02 · Системный режим без вспышки.** Первый визит следует `prefers-color-scheme` полностью через CSS: серверный HTML сразу совместим с системной темой, состояние не хранится, inline bootstrap не добавлен и CSP не ослаблена. Явное переопределение появится только вместе с D03–D04.
|
||||
- [x] **D03 · Переключатель темы.** В header добавлен доступный трёхпозиционный выбор «Системная / Светлая / Тёмная»; на узких экранах он сохраняет три понятные иконки и доступные названия. Кнопки работают с клавиатуры, имеют видимый focus и синхронизируют `aria-pressed`.
|
||||
- [x] **D04 · Сохранение и SSR-согласование.** Выбор хранится год в allowlist-cookie `rf4-theme` с `SameSite=Lax` и `Secure` на HTTPS; Astro SSR выставляет `data-theme` до отрисовки, а системный режим оставляет выбор браузеру. Собранный Astro client script переключает тему без inline-кода, localStorage и ослабления CSP.
|
||||
- [ ] **D05 · Темизация компонентов и графики.** На семантические light/dark-токены уже переведены базовые поверхности, header, каталог, breadcrumb-леска, ссылки сущностей, паспорта данных, pagination, empty/loading-состояния и часть декоративной графики. Следующий пакет: таблицы и формы, provenance/quality badges, admin-панель, радар, timeline, силуэты и глифы; затем проверить фотографии, прозрачные PNG, screenshots, тени, градиенты и forced-colors. Источники и статусы не должны различаться только цветом.
|
||||
- [ ] **D05 · Темизация компонентов и графики.** На семантические light/dark-токены уже переведены базовые поверхности, header, каталог, breadcrumb-леска, ссылки сущностей, таблицы, формы, moderation-карточки, provenance/quality/status badges, паспорта данных, pagination, empty/loading-состояния и часть декоративной графики. Следующий пакет: оставшиеся admin-поверхности, радар, timeline, силуэты и глифы; затем проверить фотографии, прозрачные PNG, screenshots, тени, градиенты и forced-colors. Источники и статусы сохраняют текстовые подписи и не различаются только цветом.
|
||||
- [ ] **D06 · Метаданные браузера и CSP.** Добавить парные `theme-color` для light/dark media queries, проверить manifest/PWA chrome и отсутствие новых inline script/style/attributes. Сохранить `inlinedScripts: []`, `style-src 'self'`, `style-src-attr 'none'` и не возвращать `unsafe-inline` ради инициализации темы.
|
||||
- [ ] **D07 · Визуальная и accessibility-приёмка.** Проверить light/dark/system на 320/390/768/1280 px для главной, каталогов, detail, records, report, status и всех admin-экранов; покрыть normal/hover/focus/disabled/error/loading/empty и длинные данные. Для обеих тем обеспечить WCAG AA, отсутствие горизонтального scroll и CLS, корректную печать, reduced motion и переключение без потери введённых данных; сохранить эталонные screenshots и краткий отчёт.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user