UI Modernization Notes
Scope
This pass improves the operator-facing UI layer for:
- Tickets
- Dashboard
- COP
- Briefing
- Analytics
- Admin / System status
The backend contracts, routes, and business logic are intentionally left unchanged.
Reusable UI layer
Added reusable macros in templates/_macros.html:
layout_containerstatus_badgeicon_buttoncopy_buttonurl_displaytable_empty_statepagination_widget
Added shared behavior in static/ui-helpers.js:
- copy-to-clipboard
- confirm-aware actions
- async submit button state
- lightweight toasts
Legacy note: early UI passes added shared styling in static/design-fixes.css and later compact operator rules in static/interface-craft.css. The legacy entrypoints have been removed from static/; the active UI contract is the AV DS 4 stack below.
The active AV DS 4 surface contract is now static/av-ds-4.css:
- layout container and grids
- table shell with sticky headers
- status badges
- icon buttons
- URL display
- async block states
- pagination sizing
- map panel and briefing list helpers
- typography tightening for operator surfaces
- compact KPI, table, list-group and commandbar density
- ticket detail, kanban and COP spacing normalization
- mobile-safe sizing for pills, buttons and dense tables
Cascade governance
Current CSS order in templates/base.html:
fonts/avds/avds-fonts.cssav-ds-tokens.cssav-ds-components-v2.cssav-ds-4.css
Rules for future UI work:
- Put reusable operator-density and alignment fixes only in
static/av-ds-4.css. - Do not recreate
static/style.css,static/responsive.css,static/design-fixes.css,static/av-ds-bridge.css,static/crisis-theme-bridge.css,static/interface-craft.cssorstatic/dist/app.cssas base-template override layers. - Do not add inline
style=""sizing for cards, tables, thumbs or grids when a reusable class can be introduced instead. - Prefer shared hooks such as
cm-table-shell--dense,ops-kpi-grid,ops-list-group,ticket-row-actions,link-buffer-entry__*. - If a template needs a local layout hook, add a semantic class in the template and style it in
av-ds-4.css; do not stack anonymous utility overrides on top of legacy selectors. - Any new project stylesheet loaded after
av-ds-4.cssis a regression unless explicitly justified.
Architecture note: av-ds-components-v2.css is a transition compatibility layer, not the target product design system. New product UI should use semantic template hooks plus static/av-ds-4.css. The current runtime and frontend layer map is documented in docs/audit/CRISIS_ARCHITECTURE_LAYER_AUDIT_2026-07-29.md; run .venv/bin/python scripts/audit_architecture_layers.py before and after broad UI work.
Page-level changes
Tickets
- action buttons moved to accessible icon-button pattern
- empty table uses reusable empty state
- pagination moved to reusable widget
- table shell styling prepared for sticky headers and denser reading
- sorting headers are keyboard-focusable and expose
aria-sort
Dashboard
- chart cards use shared chart shell sizing
- buffer links now show compact URL display with copy support
- recent incidents table uses shared empty state
Import / Buffer workbench
- the buffer form now uses the same AV DS 4 work-section shell in both Flask and FastAPI paths instead of a standalone Bootstrap card
- the import upload screen and preview now share compact operator actions, summary metrics and dense table shell styling
- import preview is covered by direct route tests so it does not silently fall back to a raw table-only screen
Search / favorites microflows
- the global search view now uses work-section result panels and summary metrics instead of isolated Bootstrap cards
- saved filters in favorites now use a valid row pattern with a separate link target and delete action, avoiding nested interactive elements
- both screens are backed by direct route-level HTML tests in addition to the broader render checks
Approvals
- the approvals queue now uses work-section panels for both reviewer and requester queues instead of Bootstrap card wrappers
- approval action buttons now sit in a dedicated action group hook instead of ad hoc inline form spacing
- the FastAPI mirror now matches Flask production by keeping requester-side approvals limited to pending requests
COP
- map panel gets expand / collapse control
- active incidents and side tables use shared table shell
- empty incidents state is explicit
Briefing
- PDF export now shows temporary in-progress state
- top incidents list is denser and supports URL display when source links exist
- empty state is explicit instead of plain muted text
Analytics
- period selector receives a reusable class hook
- chart blocks expose visible loading state while period links trigger navigation
- summary tables use explicit empty states
- the remaining analytics views now share the same tab, table, chart-shell, and empty-state patterns
Admin / System Status
- system checks use unified status badges
- API neutral state explicitly communicates "not configured"
- backup creation gets confirm + in-progress button state
Validation
Frontend tests:
npm run test:frontend
Bundled runtime fallback used in this environment:
/Users/belilovsky/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node tests/js/run-tests.mjs
This pass also expects manual browser verification for:
- sticky table headers
- tooltips on copy / action buttons
- map expand behavior in COP
- PDF button state in Briefing
- mobile menu / tables on narrower widths
Follow-up inline style normalization
- removed remaining recurring inline layout attributes in operator pages by introducing shared classes in
static/av-ds-4.css - introduced reusable selectors:
cm-period-tabs-scroll,cm-map-frame(--md,--lg,--xl),cm-progress--lg,cm-progress--md,cm-progress-track,cm-inline-form,cm-inline-form--block,cm-th-select,cm-th-actions,cm-col-truncate-*,cm-fullwidth,cm-form-select-auto,ic-timeline-divider,login-demo-note - updated templates in
analytics,playbook,tickets,watch_zones,teams,templates,resources,briefing,geozones, and login shell helpers for consistent density
Production deployment note
- deployment helper is
scripts/deploy.sh - this environment does not have
/opt/crisis-monitormounted, so prod deployment must be executed on the target VPS host:
cd /opt/crisis-monitor && bash scripts/deploy.sh
Final operator interface contract 2026-07-16
The final AV DS 4 layer now also owns the minimum interaction and typography contract. It is static/av-ds-4.css; no project stylesheet may be loaded after it.
- buttons and icon actions keep stable desktop and mobile hit areas;
- badges, quality markers and status pills wrap instead of clipping;
- checkboxes, labels, inputs and empty states use one readable rhythm;
- the incident form uses the shared page header and an un-nested workbench grid;
- the profile no longer depends on an external avatar service or exposes a dead edit action;
- COP and shift-log search/input controls have explicit accessible names;
- card and section headings preserve a valid page hierarchy.
Browser verification covered 38 public and authenticated routes. The audit checks horizontal overflow, broken images, clipped controls, undersized action targets, missing control names and duplicate IDs. Key public routes were also checked at desktop, tablet and mobile widths.
Release 0.6.24 was rechecked across all 38 authenticated workbench routes at 390 x 844 and 1024 x 900. Both matrices finished with zero horizontal overflow, broken images, server-render errors, clipped labels or undersized visible controls. Mobile command controls use a 40 px minimum target; tablet and desktop compact controls use at least 34 px, while native checks keep an explicit 18 px visual box and their surrounding row remains the interaction target.
Final verification 2026-07-09
Passed locally in this checkout:
.venv/bin/ruff check .git diff --checknpm run test:frontendnpm run build.venv/bin/pytest -q(528 passed, 95 skipped)
Known non-blocking local environment notes:
dockeris not available in this Codex environment, sodocker compose -f docker-compose.prod.yml buildandscripts/deploy.shcannot be executed here./opt/crisis-monitoris not mounted here; production deploy and live smoke must run on the VPS host.- repo-wide mypy is still legacy-red (
624 errors in 98 files) across Flask/SQLAlchemy model typing, untyped third-party stubs, and older response annotations. This is pre-existing type debt and was not introduced by the UI modernization pass.