> **Status: COMPLETED** — All 5 phases executed on 2026-04-09.

# UI Consistency Fix — Design Spec

**Date:** 2026-04-09
**Goal:** Bring the cameleer-server UI to consistent design system usage — eliminate CSS duplication, hardcoded colors, inline styles, and native HTML where DS components exist.
**Source:** `UI-CONSISTENCY-AUDIT.md` (same repo, same date)

---

## Approach: Fix by Category

Extract shared patterns first (unblocks everything), then sweep all files per issue type. Phases are independent after Phase 1 and can run in parallel.

## Phase 1: Extract Shared CSS Modules

### 1a. Table Section Pattern
Create `ui/src/styles/table-section.module.css` containing:
- `.tableSection`, `.tableHeader`, `.tableTitle`, `.tableMeta`, `.tableRight`

Update these 5 files to import from shared module instead of local definitions:
- `pages/DashboardTab/DashboardTab.module.css`
- `pages/Admin/AuditLogPage.module.css`
- `pages/Admin/ClickHouseAdminPage.module.css`
- `pages/Routes/RoutesMetrics.module.css`
- `pages/Routes/RouteDetail.module.css`

### 1b. Log Panel Pattern
Create `ui/src/styles/log-panel.module.css` containing:
- `.logCard`, `.logHeader`, `.logToolbar`, `.logSearchWrap`, `.logSearchInput`, `.logSearchClear`, `.logClearFilters`, `.logEmpty`, `.sortBtn`, `.refreshBtn`, `.headerActions`

Update `AgentHealth.module.css` and `AgentInstance.module.css` to import shared.

### 1c. Rate Color Classes
Create `ui/src/styles/rate-colors.module.css`:
- `.rateGood`, `.rateWarn`, `.rateBad`, `.rateNeutral`

Update DashboardTab, RouteDetail, RoutesMetrics.

### 1d. Tap Modal Dedup
Remove duplicate tap modal CSS from `RouteDetail.module.css` — it already uses the `TapConfigModal` component which has its own CSS module.

### 1e. Refresh Indicator Dedup
Move `.refreshIndicator`, `.refreshDot`, `.refreshText`, `@keyframes pulse` to shared module. Update DashboardTab and RoutesMetrics.

### 1f. Other Small Dedup
- `.chartCard` — shared between AgentInstance and RouteDetail
- `.section` — shared between AppConfigDetailPage and OidcConfigPage
- `.metaGrid/.metaLabel/.metaValue` — shared between AboutMeDialog and UserManagement

## Phase 2: Fix Hardcoded Colors in CSS

For each CSS module file:
- Replace naked hex values with design system variables
- Strip hex fallbacks from `var(--token, #hex)` -> `var(--token)`
- Fix undefined variable names:
  - `--accent` -> `--amber`
  - `--bg-base` -> `--bg-body`
  - `--surface` -> `--bg-surface`
  - `--bg-surface-raised` -> `--bg-raised`
- For tint colors without DS tokens (e.g., `#FDF2F0` error background), check if DS provides equivalents. If not, define them as local CSS custom properties at the component level using `color-mix()` from DS base colors.

Files to fix (priority order):
1. `ExecutionDiagram/ExecutionDiagram.module.css` (~57 violations)
2. `ProcessDiagram/ProcessDiagram.module.css` (~20 violations)
3. `Dashboard/Dashboard.module.css` (4 violations including naked `#5db866`)
4. `DashboardTab/DashboardTab.module.css` (rgba shadow)
5. `RoutesMetrics/RoutesMetrics.module.css` (rgba shadow)
6. `AppsTab/AppsTab.module.css` (`--accent` + rgba)
7. `AgentHealth/AgentHealth.module.css` (`#fff`)
8. `LoginPage.module.css`, `ContentTabs.module.css`, `EnvironmentSelector.module.css` (undefined vars)

## Phase 3: Fix Hardcoded Colors in TSX

Create `ui/src/utils/theme-colors.ts`:
```ts
// Reads CSS custom properties from the document root at call time.
// Use for SVG attributes where var() isn't supported in JS string contexts.
export function cssVar(name: string): string {
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
```

Then replace all hex literals in ProcessDiagram components:
- `DiagramNode.tsx` (~20 values)
- `CompoundNode.tsx` (~8 values)
- `DiagramEdge.tsx` (~3 values)
- `ConfigBadge.tsx` (~3 values)
- `ErrorSection.tsx` (~2 values)
- `NodeToolbar.tsx` (~2 values)
- `Minimap.tsx` (~3 values)
- `ProcessDiagram.tsx` (~2 values)

Note: SVG `fill` and `stroke` attributes DO support `var()` in modern browsers. Where the color is a static attribute (not computed in JS), use `fill="var(--success)"` directly. Only use `cssVar()` where the value must be a JS string (e.g., in computed color logic).

Also fix TSX color references in:
- `pages/Dashboard/Dashboard.tsx` (hardcoded `#3D7C47`)
- `components/LayoutShell.tsx` (inline `color: 'var(--error)'` -> CSS class)

Leave data-visualization computed `hsl()` values (Treemap, PunchcardHeatmap, DiagramNode heatmap) — these are intentional gradients.

## Phase 4: Eliminate Inline Styles

### 4a. Create Missing CSS Modules
- `pages/Admin/AdminLayout.module.css` — extract the single inline layout style
- `pages/Admin/DatabaseAdminPage.module.css` — extract all inline layout/typography
- `auth/OidcCallback.module.css` — extract full-page layout

### 4b. Extract Shared Components
- Create `components/PageLoader.tsx` — replaces the 3 copy-pasted `<Spinner>` + flex + padding patterns in router.tsx, RuntimePage, DashboardPage

### 4c. Move Static Inline Styles to CSS Modules
Priority files (most violations):
- `AppsTab.tsx` — fixed-width inputs -> size classes, visually-hidden pattern, table cell layouts
- `LayoutShell.tsx` — StarredList styles
- `EnvironmentsPage.tsx` — save/cancel button rows, raw `<select>` styling
- `RouteDetail.tsx` — heading styles, tab panel margins

## Phase 5: Replace Native HTML with DS Components (time-permitting)

1. `EnvironmentSelector.tsx` — bare `<select>` -> DS `Select`
2. `ExecutionDiagram/tabs/LogTab.tsx` — raw table -> DS `LogViewer`
3. `AppsTab.tsx` sub-tab bars -> DS `SegmentedTabs` or `Tabs`
4. `AppConfigDetailPage.tsx` — `<select>` -> DS `Select`, `<input checkbox>` -> DS `Toggle`, `<label>` -> DS `FormField`
5. `AgentHealth.tsx` — config bar `<select>` -> DS `Select`, action `<button>` -> DS `Button`

## Out of Scope

- Responsive design (zero `@media` queries) — separate initiative
- Data visualization color scales (Treemap, PunchcardHeatmap) — intentional computed gradients
- ProcessDiagram SVG structural class names (`className="nodes"`) — semantic, acceptable
- Adding new DS tokens to the design-system package — use `color-mix()` locally if needed

## Success Criteria

- Zero naked hex values in CSS modules
- Zero `var(--token, #fallback)` patterns (strip fallbacks)
- Zero undefined CSS variable names
- CSS class duplication reduced from ~22% to <5%
- All pages with >3 inline styles get a CSS module
- Shared `PageLoader` component replaces copy-pasted spinner pattern
