# 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 `
` 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 `` 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 `` 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 `
` 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 `
` 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 `
` (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 `
`. **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 `
`. **Tables**: Uses `DataTable` directly with NO `tableStyles.tableSection` wrapper. Tables under custom `.section` divs with `.sectionHeading` text labels. **Cards**: Uses DS `` 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 `
`. **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 `
` | | **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 `
` 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`.