# Design Consistency Audit — Cameleer UI

**Audited**: 2026-04-09
**Scope**: All pages under `ui/src/pages/`
**Base path**: `C:/Users/Hendrik/Documents/projects/cameleer-server/ui/src/`


## Shared Layout Infrastructure

### LayoutShell (`components/LayoutShell.tsx`)
All pages render inside `<main className={css.mainContent}>` which applies:
```css
.mainContent {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
}
```
This is a flex column container with **no padding/margin**. Each page is responsible for its own content spacing.

### Shared CSS Modules (`styles/`)
| Module | Class | Pattern |
|--------|-------|---------|
| `section-card.module.css` | `.section` | Card with `padding: 16px 20px`, border, shadow, `margin-bottom: 16px` |
| `table-section.module.css` | `.tableSection` | Card wrapper for tables, no padding (overflow hidden), with `.tableHeader` (12px 16px padding) |
| `chart-card.module.css` | `.chartCard` | Card with `padding: 16px` |
| `log-panel.module.css` | `.logCard` | Card for log viewers, max-height 420px |
| `refresh-indicator.module.css` | `.refreshIndicator` | Auto-refresh dot indicator |
| `rate-colors.module.css` | `.rateGood/.rateWarn/.rateBad` | Semantic color helpers |


## Per-Page Findings

---

### 1. Exchanges Page (`pages/Exchanges/`)

**Files**: `ExchangesPage.tsx`, `ExchangesPage.module.css`, `ExchangeHeader.tsx`, `ExchangeHeader.module.css`, `RouteControlBar.tsx`, `RouteControlBar.module.css`

**Container pattern**: NO wrapper padding. Uses `height: 100%` split-view layout that fills the entire `mainContent` area.

**Content wrapper**:
```css
.splitView { display: flex; height: 100%; overflow: hidden; }
```

**Table**: The exchange list is rendered by `Dashboard.tsx` (in `pages/Dashboard/`), which uses:
```css
.content { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; background: var(--bg-body); }
```
- Custom `.tableHeader` with `padding: 8px 12px` (slightly tighter than shared `tableStyles.tableHeader` which uses `12px 16px`)
- `DataTable` rendered with `flush` and `fillHeight` props
- **NO card wrapper** around the table — it's full-bleed against the background
- **Does NOT import shared `table-section.module.css`** — rolls its own `.tableHeader`, `.tableTitle`, `.tableRight`, `.tableMeta`

**Shared modules used**: NONE. All custom.

**INCONSISTENCY**: Full-bleed table with no card, no container padding. Custom table header styling duplicates shared module patterns with slightly different padding values (8px 12px vs 12px 16px).

---

### 2. Dashboard Tab (`pages/DashboardTab/`)

**Files**: `DashboardPage.tsx`, `DashboardL1.tsx`, `DashboardL2.tsx`, `DashboardL3.tsx`, `DashboardTab.module.css`

**Container pattern**:
```css
.content { display: flex; flex-direction: column; gap: 20px; flex: 1; min-height: 0; overflow-y: auto; padding-bottom: 20px; }
```
- **No top/left/right padding** — content is full-width inside `mainContent`
- Only `padding-bottom: 20px` and `gap: 20px` between sections

**Tables**: Wrapped in shared `tableStyles.tableSection` (card with border, shadow, border-radius). Imports `table-section.module.css`.

**Charts**: Wrapped in design-system `<Card>` component.

**Custom sections**: `errorsSection` and `diagramSection` duplicate the card pattern:
```css
.errorsSection {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
```
This is identical to `tableStyles.tableSection` but defined separately in `DashboardTab.module.css`.

**Shared modules used**: `table-section.module.css`, `refresh-indicator.module.css`, `rate-colors.module.css`

**INCONSISTENCY**: No container padding means KPI strip and tables sit flush against the sidebar/edge. The `.errorsSection` duplicates `tableStyles.tableSection` exactly — should import the shared module instead of copy-pasting.

---

### 3. Runtime Tab — Agent Health (`pages/AgentHealth/`)

**Files**: `AgentHealth.tsx`, `AgentHealth.module.css`

**Container pattern**:
```css
.content { flex: 1; overflow-y: auto; padding: 20px 24px 40px; min-width: 0; background: var(--bg-body); }
```
- **Has explicit padding**: `20px 24px 40px` (top, sides, bottom)

**Tables**: Uses design-system `DataTable` inside a DS `Card` component for agent group cards. The group cards use custom `.groupGrid` grid layout. No `tableStyles.tableSection` wrapper.

**Cards/sections**: Custom card patterns like `.configBar`, `.eventCard`:
```css
.configBar {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 12px 16px;
  margin-bottom: 16px;
}
```

**Shared modules used**: `log-panel.module.css`

**INCONSISTENCY**: Uses `padding: 20px 24px 40px` — different from DashboardTab (no padding) and Exchanges (no padding). Custom card patterns duplicate the standard card styling. Does not use `table-section.module.css` or `section-card.module.css`.

---

### 4. Runtime Tab — Agent Instance (`pages/AgentInstance/`)

**Files**: `AgentInstance.tsx`, `AgentInstance.module.css`

**Container pattern**:
```css
.content { flex: 1; overflow-y: auto; padding: 20px 24px 40px; min-width: 0; background: var(--bg-body); }
```
- Matches AgentHealth padding exactly (consistent within Runtime tab)

**Cards/sections**: Custom `.processCard`, `.timelineCard` duplicate the card pattern. Uses `chart-card.module.css` for chart wrappers.

**Shared modules used**: `log-panel.module.css`, `chart-card.module.css`

**INCONSISTENCY**: Consistent with AgentHealth but inconsistent with DashboardTab and Exchanges. Custom card patterns (processCard, timelineCard) duplicate shared module patterns.

---

### 5. Apps Tab (`pages/AppsTab/`)

**Files**: `AppsTab.tsx`, `AppsTab.module.css`

**Container pattern**:
```css
.container { padding: 16px; overflow-y: auto; flex: 1; }
```
- **Has padding**: `16px` all around

**Content structure**: Three sub-views (`AppListView`, `AppDetailView`, `CreateAppView`) all wrapped in `.container`.

**Tables**: App list uses `DataTable` directly — no `tableStyles.tableSection` wrapper. Deployment table uses custom `.table` with manual `<table>` HTML (not DataTable).

**Form controls**: Directly on page background with custom grid layout (`.configGrid`). Uses `SectionHeader` from design-system for visual grouping, but forms are not in cards/sections — they sit flat against the `.container` background.

**Custom elements**:
- `.editBanner` / `.editBannerActive` — custom banner pattern
- `.configGrid` — 2-column label/input grid
- `.table` — fully custom `<table>` styling (not DataTable)

**Shared modules used**: NONE. All custom.

**INCONSISTENCY (user-reported)**: Controls "meshed into background" — correct. Form controls use `SectionHeader` for labels but no `section-card` wrapper. The Tabs component provides visual grouping but the content below tabs is flat. Config grids, toggles, and inputs sit directly on `var(--bg-body)` background via the 16px-padded container. No card/section separation between different config groups. Also uses a manual `<table>` element instead of DataTable for deployments.

---

### 6. Admin — RBAC Page (`pages/Admin/RbacPage.tsx`, `UsersTab.tsx`, `GroupsTab.tsx`, `RolesTab.tsx`)

**Container pattern**: AdminLayout provides `padding: 20px 24px 40px`. RbacPage renders a bare `<div>` (no extra wrapper class).

**Content**: Uses `StatCard` strip, `Tabs`, then tab content. Detail views use `SplitPane` (from design-system). User/Group/Role detail sections use `SectionHeader` without card wrappers.

**Stat strip**: Custom grid — `grid-template-columns: repeat(3, 1fr)` with `gap: 10px; margin-bottom: 16px`

**Shared modules used**: NONE. Uses `UserManagement.module.css` (custom).

**INCONSISTENCY**: Detail sections use `SectionHeader` labels but content is flat (no `section-card` wrapper). Similar to AppsTab pattern.

---

### 7. Admin — Audit Log (`pages/Admin/AuditLogPage.tsx`)

**Container pattern**: Inherits AdminLayout padding (`20px 24px 40px`). Renders a bare `<div>`.

**Table**: Properly uses shared `tableStyles.tableSection` with `.tableHeader`, `.tableTitle`, `.tableRight`, `.tableMeta`.

**Shared modules used**: `table-section.module.css`

**STATUS**: CONSISTENT with shared patterns for the table section. Good.

---

### 8. Admin — OIDC Config (`pages/Admin/OidcConfigPage.tsx`)

**Container pattern**: Inherits AdminLayout padding. Adds `.page { max-width: 640px; margin: 0 auto; }` — centered narrow layout.

**Sections**: Uses shared `sectionStyles.section` from `section-card.module.css` for every form group. Uses `SectionHeader` inside each section card.

**Shared modules used**: `section-card.module.css`

**STATUS**: GOOD. This is the correct pattern — form groups wrapped in section cards. Should be the model for other form pages.

---

### 9. Admin — Database (`pages/Admin/DatabaseAdminPage.tsx`)

**Container pattern**: Inherits AdminLayout padding. Renders bare `<div>`.

**Tables**: Uses `DataTable` directly with NO `tableStyles.tableSection` wrapper. Tables under custom `.section` divs with `.sectionHeading` text labels.

**Cards**: Uses DS `<Card>` for connection pool. Stat strip is a flex layout.

**Shared modules used**: NONE. All custom.

**INCONSISTENCY**: Tables not wrapped in `tableStyles.tableSection`. Uses custom section headings instead of `SectionHeader`. Missing card wrappers around tables. Stat strip uses `flex` layout while other pages use `grid`.

---

### 10. Admin — ClickHouse (`pages/Admin/ClickHouseAdminPage.tsx`)

**Container pattern**: Inherits AdminLayout padding. Renders bare `<div>`.

**Tables**: Uses shared `tableStyles.tableSection` combined with custom `.tableSection` for margin: `className={tableStyles.tableSection} ${styles.tableSection}`.

**Custom elements**: `.pipelineCard` duplicates card pattern (bg-surface, border, radius, shadow, padding).

**Shared modules used**: `table-section.module.css`

**PARTIAL**: Tables correctly use shared module. Pipeline card duplicates shared card pattern.

---

### 11. Admin — Environments (`pages/Admin/EnvironmentsPage.tsx`)

**Container pattern**: Inherits AdminLayout padding. Renders via `SplitPane` (design-system).

**Content**: Uses `SectionHeader`, `SplitPane`, custom meta grids from `UserManagement.module.css`.

**Shared modules used**: Uses `UserManagement.module.css` (shared with RBAC pages)

**INCONSISTENCY**: Does not use `section-card.module.css` for form sections. Config sections use `SectionHeader` without card wrappers. `SplitPane` provides some structure but detail content is flat.

---

### 12. Admin — App Config Detail (`pages/Admin/AppConfigDetailPage.tsx`)

**Container pattern**: Adds `.page { max-width: 720px; margin: 0 auto; }` — centered layout.

**Sections**: Uses shared `sectionStyles.section` from `section-card.module.css`. Uses `SectionHeader` inside section cards. Custom header card duplicates the card pattern.

**Shared modules used**: `section-card.module.css`

**STATUS**: GOOD. Follows same pattern as OIDC page.

---

### 13. Routes pages (`pages/Routes/`) — NOT ROUTED

These pages (`RoutesMetrics.tsx`, `RouteDetail.tsx`) exist but are NOT in `router.tsx`. They may be deprecated or used as sub-components. `RoutesMetrics` correctly uses shared `tableStyles.tableSection`. `RouteDetail` has many custom card patterns (`.headerCard`, `.diagramPane`, `.statsPane`, `.executionsTable`, `.routeFlowSection`) that duplicate the shared card pattern.

---


## Summary: Inconsistency Matrix

### Container Padding

| Page | Padding | Pattern |
|------|---------|---------|
| **Exchanges** | NONE (full-bleed) | `height: 100%`, fills container |
| **Dashboard Tab** | NONE (gap only) | `gap: 20px`, `padding-bottom: 20px` only |
| **Runtime (AgentHealth)** | `20px 24px 40px` | Explicit padding |
| **Runtime (AgentInstance)** | `20px 24px 40px` | Explicit padding |
| **Apps Tab** | `16px` | Uniform padding |
| **Admin pages** | `20px 24px 40px` | Via AdminLayout |

**Finding**: Three different padding strategies. Exchanges and Dashboard have no padding; Runtime and Admin use 20px/24px; Apps uses 16px.


### Table Wrapper Pattern

| Page | Uses `tableStyles.tableSection`? | Card wrapper? |
|------|----------------------------------|---------------|
| **Exchanges (Dashboard.tsx)** | NO — custom `.tableHeader` | NO — full-bleed |
| **Dashboard L1/L2/L3** | YES | YES (shared) |
| **Runtime AgentHealth** | NO | YES (via DS `Card`) |
| **Apps Tab** | NO | NO — bare `<table>` |
| **Admin — Audit** | YES | YES (shared) |
| **Admin — ClickHouse** | YES | YES (shared) |
| **Admin — Database** | NO | NO |

**Finding**: 4 of 7 table-using pages do NOT use the shared `table-section.module.css`. The Exchanges page custom header has padding `8px 12px` vs shared `12px 16px`.


### Form/Control Wrapper Pattern

| Page | Form controls in cards? | Uses `section-card`? |
|------|------------------------|---------------------|
| **Apps Tab (detail)** | NO — flat against background | NO |
| **Apps Tab (create)** | NO — flat against background | NO |
| **Admin — OIDC** | YES | YES |
| **Admin — App Config** | YES | YES |
| **Admin — RBAC detail** | NO — flat against background | NO |
| **Admin — Environments** | NO — flat against background | NO |
| **Admin — Database** | PARTIAL (Card for pool) | NO |
| **Runtime — AgentHealth** | YES (custom `.configBar`) | NO (custom) |

**Finding**: Only OIDC and AppConfigDetail use `section-card.module.css` for form grouping. Most form pages render controls flat against the page background.


### Duplicated Card Pattern

The following CSS pattern appears in 8+ custom locations instead of importing `section-card.module.css` or `table-section.module.css`:

```css
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
```

**Duplicated in**:
- `DashboardTab.module.css` → `.errorsSection`, `.diagramSection`
- `AgentHealth.module.css` → `.configBar`, `.eventCard`
- `AgentInstance.module.css` → `.processCard`, `.timelineCard`
- `ClickHouseAdminPage.module.css` → `.pipelineCard`
- `AppConfigDetailPage.module.css` → `.header`
- `RouteDetail.module.css` → `.headerCard`, `.diagramPane`, `.statsPane`, `.executionsTable`, `.routeFlowSection`


## Prioritized Fixes

### P0 — User-reported issues
1. **Exchanges table full-bleed**: `Dashboard.tsx` should wrap its table in `tableStyles.tableSection` and use the shared table header classes instead of custom ones. Custom `.tableHeader` padding (8px 12px) should match shared (12px 16px).
2. **Apps detail flat controls**: `AppsTab.tsx` config sections should wrap form groups in `sectionStyles.section` (from `section-card.module.css`), matching the OIDC page pattern.
3. **Apps deployment table**: Replace manual `<table>` with `DataTable` inside `tableStyles.tableSection`.

### P1 — Padding normalization
4. **Standardize container padding**: Choose ONE pattern for scrollable content areas. Recommended: `padding: 20px 24px 40px` (currently used by Runtime + Admin). Apply to DashboardTab's `.content`. Exchanges is an exception due to its split-view height-filling layout.
5. **DashboardTab.module.css**: Add side padding to `.content`.

### P2 — Shared module adoption
6. **Replace duplicated card patterns**: Import `section-card.module.css` or `table-section.module.css` instead of duplicating the card CSS in:
   - `DashboardTab.module.css` (`.errorsSection` -> use `tableStyles.tableSection`)
   - `AgentHealth.module.css` (`.configBar`, `.eventCard`)
   - `AgentInstance.module.css` (`.processCard`, `.timelineCard`)
   - `ClickHouseAdminPage.module.css` (`.pipelineCard`)
7. **Database admin**: Wrap tables in `tableStyles.tableSection`.
8. **Admin detail pages** (RBAC, Environments): Wrap form sections in `sectionStyles.section`.
