docs: UI consistency audit and fix design spec
Full audit of design system adoption, color consistency, inline styles, layout patterns, and CSS module duplication across the server UI. Includes 6-phase fix plan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
257
UI-CONSISTENCY-AUDIT.md
Normal file
257
UI-CONSISTENCY-AUDIT.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# UI Consistency Audit — cameleer3-server
|
||||
|
||||
**Date:** 2026-04-09
|
||||
**Scope:** All files under `ui/src/` (26 CSS modules, ~45 TSX components, ~15 pages)
|
||||
**Verdict:** ~55% design system adoption for interactive UI. Significant duplication and inline style debt.
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
| Dimension | Score | Key Issue |
|
||||
|-----------|-------|-----------|
|
||||
| Design system component adoption | 55% | 32 raw `<button>`, 12 raw `<select>`, 8 raw `<input>` should use DS |
|
||||
| Color consistency | Poor | ~140 violations: 45 hardcoded hex in TSX, 13 naked hex in CSS, ~55 fallback hex in `var()` |
|
||||
| Inline styles | Poor | 55 RED (static inline styles), 8 YELLOW, 14 GREEN (justified) |
|
||||
| Layout consistency | Mixed | 3 different page padding values, mixed gap/margin approaches |
|
||||
| CSS module duplication | 22% | ~135 of 618 classes are copy-pasted across files |
|
||||
| Responsive design | None | Zero `@media` queries in entire UI |
|
||||
|
||||
---
|
||||
|
||||
## 1. Critical: Hardcoded Colors (CLAUDE.md violation)
|
||||
|
||||
The project rule states: *"Always use `@cameleer/design-system` CSS variables for colors — never hardcode hex values."*
|
||||
|
||||
### Worst offenders
|
||||
|
||||
| File | Violations | Severity |
|
||||
|------|-----------|----------|
|
||||
| `ProcessDiagram/DiagramNode.tsx` | ~20 hex values in SVG fill/stroke | Critical |
|
||||
| `ExecutionDiagram/ExecutionDiagram.module.css` | 17 naked hex + ~40 hex fallbacks in `var()` | Critical |
|
||||
| `ProcessDiagram/CompoundNode.tsx` | 8 hex values | Critical |
|
||||
| `ProcessDiagram/DiagramEdge.tsx` | 3 hex values | High |
|
||||
| `ProcessDiagram/ConfigBadge.tsx` | 3 hex values | High |
|
||||
| `ProcessDiagram/ErrorSection.tsx` | 2 hex values | High |
|
||||
| `ProcessDiagram/NodeToolbar.tsx` | 2 hex values | High |
|
||||
| `ProcessDiagram/Minimap.tsx` | 3 hex values | High |
|
||||
| `Dashboard/Dashboard.module.css` | `#5db866` (not even a DS color) | High |
|
||||
| `AppsTab/AppsTab.module.css` | `var(--accent, #6c7aff)` (undefined DS variable) | Medium |
|
||||
|
||||
### Undefined CSS variables (not in design system)
|
||||
|
||||
| Variable | Files | Should be |
|
||||
|----------|-------|-----------|
|
||||
| `--accent` | EnvironmentSelector, AppsTab | `--amber` (or define in DS) |
|
||||
| `--bg-base` | LoginPage | `--bg-body` |
|
||||
| `--surface` | ContentTabs, ExchangeHeader | `--bg-surface` |
|
||||
| `--bg-surface-raised` | AgentHealth | `--bg-raised` |
|
||||
|
||||
### Missing DS tokens needed
|
||||
|
||||
Several tint/background colors are used repeatedly but have no DS variable:
|
||||
- `--error-bg` (used as `#FDF2F0`, `#F9E0DC`)
|
||||
- `--success-bg` (used as `#F0F9F1`)
|
||||
- `--amber-bg` / `--warning-bg` (used as `#FFF8F0`)
|
||||
- `--bg-inverse` / `--text-inverse` (used as `#1A1612` / `#E4DFD8`)
|
||||
|
||||
---
|
||||
|
||||
## 2. Critical: CSS Module Duplication (~22%)
|
||||
|
||||
~135 of 618 class definitions are copy-pasted across files.
|
||||
|
||||
### Table section pattern — 5 files, ~35 duplicate classes
|
||||
|
||||
`.tableSection`, `.tableHeader`, `.tableTitle`, `.tableMeta`, `.tableRight` are **identical** in:
|
||||
- `DashboardTab.module.css`
|
||||
- `AuditLogPage.module.css`
|
||||
- `ClickHouseAdminPage.module.css`
|
||||
- `RoutesMetrics.module.css`
|
||||
- `RouteDetail.module.css`
|
||||
|
||||
### Log viewer panel — 2 files, ~50 lines identical
|
||||
|
||||
`.logCard`, `.logHeader`, `.logToolbar`, `.logSearchWrap`, `.logSearchInput`, `.logSearchClear`, `.logClearFilters`, `.logEmpty`, `.sortBtn`, `.refreshBtn`, `.headerActions` — byte-for-byte identical in `AgentHealth.module.css` and `AgentInstance.module.css`.
|
||||
|
||||
### Tap modal form — 2 files, ~40 lines identical
|
||||
|
||||
`.typeSelector`, `.typeOption`, `.typeOptionActive`, `.testSection`, `.testTabs`, `.testTabBtn`, `.testTabBtnActive`, `.testBody`, `.testResult`, `.testSuccess`, `.testError` — identical in `TapConfigModal.module.css` and `RouteDetail.module.css`.
|
||||
|
||||
### Other duplicates
|
||||
|
||||
| Pattern | Files | Lines |
|
||||
|---------|-------|-------|
|
||||
| Rate color classes (`.rateGood/.rateWarn/.rateBad/.rateNeutral`) | DashboardTab, RouteDetail, RoutesMetrics | ~12 each |
|
||||
| Refresh indicator + `@keyframes pulse` | DashboardTab, RoutesMetrics | ~15 each |
|
||||
| Chart card (`.chartCard`) | AgentInstance, RouteDetail | ~6 each |
|
||||
| Section card (`.section`) | AppConfigDetailPage, OidcConfigPage | ~7 each |
|
||||
| Meta grid (`.metaGrid/.metaLabel/.metaValue`) | AboutMeDialog, UserManagement | ~9 each |
|
||||
|
||||
---
|
||||
|
||||
## 3. High: Inline Styles (55 RED violations)
|
||||
|
||||
### Files with zero CSS modules (all inline)
|
||||
|
||||
| File | Issue |
|
||||
|------|-------|
|
||||
| `pages/Admin/AdminLayout.tsx` | Entire layout wrapper is inline styled |
|
||||
| `pages/Admin/DatabaseAdminPage.tsx` | All layout, typography, spacing inline — no CSS module |
|
||||
| `auth/OidcCallback.tsx` | Full-page layout inline — no CSS module |
|
||||
|
||||
### Most inline violations
|
||||
|
||||
| File | RED count | Primary patterns |
|
||||
|------|-----------|-----------------|
|
||||
| `pages/AppsTab/AppsTab.tsx` | ~25 | Fixed-width inputs (`width: 50-90px` x18), visually-hidden pattern x2, table cell layouts |
|
||||
| `components/LayoutShell.tsx` | 6 | StarredList sub-component, sidebar layout |
|
||||
| `pages/Admin/EnvironmentsPage.tsx` | 8 | Raw `<select>` fully styled inline, save/cancel button rows |
|
||||
| `pages/Routes/RouteDetail.tsx` | 5 | Heading styles, tab panel margins |
|
||||
|
||||
### Repeated inline patterns that need extraction
|
||||
|
||||
| Pattern | Occurrences | Fix |
|
||||
|---------|-------------|-----|
|
||||
| `style={{ display: 'flex', justifyContent: 'center', padding: '4rem' }}` (loading fallback) | 3 files | Create shared `<PageLoader>` |
|
||||
| `style={{ position: 'absolute', width: 1, height: 1, clip: 'rect(0,0,0,0)' }}` (visually hidden) | 2 in AppsTab | Create `.visuallyHidden` utility class |
|
||||
| `style={{ width: N }}` on `<Input>`/`<Select>` (fixed widths) | 18+ in AppsTab | Size classes or CSS module rules |
|
||||
| `style={{ marginTop: 8, display: 'flex', gap: 8, justifyContent: 'flex-end' }}` (action row) | 3+ in EnvironmentsPage | Shared `.editActions` class |
|
||||
|
||||
---
|
||||
|
||||
## 4. High: Design System Component Adoption Gaps
|
||||
|
||||
### Native HTML that should use DS components
|
||||
|
||||
| Element | Instances | Files | DS Replacement |
|
||||
|---------|-----------|-------|---------------|
|
||||
| `<button>` | 32 | 8 files | `Button`, `SegmentedTabs` |
|
||||
| `<select>` | 12 | 4 files | `Select` |
|
||||
| `<input>` | 8 | 4 files | `Input`, `Toggle`, `Checkbox` |
|
||||
| `<label>` | 9 | 2 files | `FormField`, `Label` |
|
||||
| `<table>` (data) | 2 | 2 files | `DataTable`, `LogViewer` |
|
||||
|
||||
### Highest-priority replacements
|
||||
|
||||
1. **`EnvironmentSelector.tsx`** — zero DS imports, entire component is a bare `<select>`. Used globally in sidebar.
|
||||
2. **`ExecutionDiagram/tabs/LogTab.tsx`** — reimplements LogViewer from scratch (raw table + input + button). AgentInstance and AgentHealth already use DS `LogViewer` correctly.
|
||||
3. **`AppsTab.tsx` sub-tabs** — 3 instances of homegrown `<button>` tab bars. DS provides `SegmentedTabs` and `Tabs`.
|
||||
4. **`AppConfigDetailPage.tsx`** — 4x `<select>`, 4x `<label>`, 2x `<input type="checkbox">`, 4x `<button>` — all have DS equivalents already used elsewhere.
|
||||
5. **`AgentHealth.tsx`** — config bar uses `Toggle` (correct) alongside raw `<select>` and `<button>` (incorrect).
|
||||
|
||||
### Cross-page inconsistencies
|
||||
|
||||
| Pattern | Correct usage | Incorrect usage |
|
||||
|---------|--------------|-----------------|
|
||||
| Log viewer | AgentInstance, AgentHealth use DS `LogViewer` | LogTab rebuilds from scratch |
|
||||
| Config edit form | Both pages render same 4 fields | AgentHealth uses `Toggle`, AppConfigDetail uses `<input type="checkbox">` |
|
||||
| Sub-tabs | RbacPage uses DS `Tabs` | AppsTab uses homegrown `<button>` tabs with non-DS `--accent` color |
|
||||
| Select dropdowns | AppsTab uses DS `Select` for some fields | Same file uses raw `<select>` for other fields |
|
||||
|
||||
---
|
||||
|
||||
## 5. Medium: Layout Inconsistencies
|
||||
|
||||
### Page padding (3 different values)
|
||||
|
||||
| Pages | Padding |
|
||||
|-------|---------|
|
||||
| AgentHealth, AgentInstance, AdminLayout | `20px 24px 40px` |
|
||||
| AppsTab | `16px` (all sides) |
|
||||
| DashboardTab, Dashboard | No padding (full-bleed) |
|
||||
|
||||
### Section gap spacing (mixed approaches)
|
||||
|
||||
| Approach | Pages |
|
||||
|----------|-------|
|
||||
| CSS `gap: 20px` on flex container | DashboardTab, RoutesMetrics |
|
||||
| `margin-bottom: 20px` | AgentInstance |
|
||||
| Mixed `margin-bottom: 16px` and `20px` on same page | AgentHealth, ClickHouseAdminPage |
|
||||
|
||||
### Typography inconsistencies
|
||||
|
||||
| Issue | Details |
|
||||
|-------|---------|
|
||||
| Card title weight | Most use `font-weight: 600`, RouteDetail `.paneTitle` uses `700` |
|
||||
| Chart title style | RouteDetail: `12px/700/uppercase`, AgentHealth: `12px/600/uppercase` |
|
||||
| Font units | ExchangeHeader + TabKpis use `rem`, everything else uses `px` |
|
||||
| Raw headings | DatabaseAdminPage uses `<h2>`/`<h3>` with inline styles; all others use DS `SectionHeader` or CSS classes |
|
||||
| Table header padding | Most: `12px 16px`, Dashboard: `8px 12px`, AgentHealth eventCard: `10px 16px` |
|
||||
|
||||
### Stat strip layouts
|
||||
|
||||
| Page | Layout | Gap |
|
||||
|------|--------|-----|
|
||||
| AgentHealth, AgentInstance, RbacPage | CSS grid `repeat(N, 1fr)` | `10px` |
|
||||
| ClickHouseAdminPage | Flexbox (unequal widths) | `10px` |
|
||||
| DatabaseAdminPage | Inline flex | `1rem` (16px) |
|
||||
|
||||
### Empty state patterns (4 different approaches)
|
||||
|
||||
1. DS `<EmptyState>` component (AgentInstance — correct)
|
||||
2. `EntityList emptyMessage` prop (EnvironmentsPage, RbacPage)
|
||||
3. `.logEmpty` CSS class, `12px`, `var(--text-faint)` (AgentHealth, AgentInstance)
|
||||
4. `.emptyNote` CSS class, `12px`, `italic` (AppsTab)
|
||||
5. Inline `0.875rem`, `var(--text-muted)` (ExchangesPage)
|
||||
|
||||
### Loading state patterns (3 different approaches)
|
||||
|
||||
1. `<Spinner size="lg">` in flex div with inline `padding: 4rem` — copy-pasted 3 times
|
||||
2. `<Spinner size="md">` returned directly, no centering (EnvironmentsPage)
|
||||
3. No loading UI, data simply absent (DashboardL1/L2/L3)
|
||||
|
||||
---
|
||||
|
||||
## 6. Low: Other Findings
|
||||
|
||||
- **`!important`**: 1 use in `RouteControlBar.module.css` — works around specificity conflict
|
||||
- **Zero responsive design**: no `@media` queries anywhere
|
||||
- **Z-index**: only 4 uses, all in diagram components (5 and 10), consistent
|
||||
- **Naming convention**: all camelCase — consistent, no issues
|
||||
- **Unused CSS classes**: ~11 likely unused in AppsTab (old create-modal classes) and TapConfigModal
|
||||
|
||||
---
|
||||
|
||||
## Recommended Fix Order
|
||||
|
||||
### Phase 1: Design system tokens (unblocks everything else)
|
||||
|
||||
1. Add missing DS variables: `--error-bg`, `--success-bg`, `--amber-bg`, `--bg-inverse`, `--text-inverse`
|
||||
2. Fix undefined variables: `--accent` -> `--amber`, `--bg-base` -> `--bg-body`, `--surface` -> `--bg-surface`
|
||||
|
||||
### Phase 2: Eliminate CSS duplication (~22% of all classes)
|
||||
|
||||
3. Extract shared `tableSection` pattern to shared CSS module (saves ~140 duplicate lines across 5 files)
|
||||
4. Extract shared log viewer CSS to shared module (saves ~50 lines across 2 files)
|
||||
5. Remove duplicate tap modal CSS from RouteDetail (saves ~40 lines)
|
||||
6. Extract shared rate/refresh/chart patterns
|
||||
|
||||
### Phase 3: Fix hardcoded colors
|
||||
|
||||
7. Replace all hex in `ProcessDiagram/*.tsx` SVG components (~45 values)
|
||||
8. Replace all hex in `ExecutionDiagram.module.css` (~17 naked + strip ~40 fallbacks)
|
||||
9. Fix remaining CSS hex violations (Dashboard, AppsTab, AgentHealth)
|
||||
|
||||
### Phase 4: Replace native HTML with DS components
|
||||
|
||||
10. `EnvironmentSelector` -> DS `Select`
|
||||
11. `LogTab` -> DS `LogViewer`
|
||||
12. `AppsTab` sub-tabs -> DS `SegmentedTabs`
|
||||
13. `AppConfigDetailPage` form elements -> DS `Select`/`Toggle`/`FormField`/`Button`
|
||||
14. Remaining `<button>` -> DS `Button`
|
||||
|
||||
### Phase 5: Eliminate inline styles
|
||||
|
||||
15. Create CSS modules for AdminLayout, DatabaseAdminPage, OidcCallback
|
||||
16. Extract shared `<PageLoader>` component
|
||||
17. Move AppsTab fixed-width inputs to CSS module size classes
|
||||
18. Move remaining inline margins/flex patterns to CSS classes
|
||||
|
||||
### Phase 6: Standardize layout patterns
|
||||
|
||||
19. Unify page padding to `20px 24px 40px`
|
||||
20. Standardize section gaps to `gap: 20px` on flex containers
|
||||
21. Normalize font units to `px` throughout
|
||||
22. Standardize empty state to DS `<EmptyState>`
|
||||
23. Standardize loading state to shared `<PageLoader>`
|
||||
Reference in New Issue
Block a user