Playwright-driven audit of the live UI (build 69dcce2, 60+ screenshots)
covering all pages, CRUD lifecycles, design consistency, and interaction
patterns. Spec defines 8 batches of work: critical bugs, layout
consistency, interaction consistency, contrast/readability, data
formatting, chart fixes, admin polish, and nice-to-have items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16 KiB
Design Consistency Audit — Cameleer3 UI
Audited: 2026-04-09
Scope: All pages under ui/src/pages/
Base path: C:/Users/Hendrik/Documents/projects/cameleer3-server/ui/src/
Shared Layout Infrastructure
LayoutShell (components/LayoutShell.tsx)
All pages render inside <main className={css.mainContent}> which applies:
.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:
.splitView { display: flex; height: 100%; overflow: hidden; }
Table: The exchange list is rendered by Dashboard.tsx (in pages/Dashboard/), which uses:
.content { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; background: var(--bg-body); }
- Custom
.tableHeaderwithpadding: 8px 12px(slightly tighter than sharedtableStyles.tableHeaderwhich uses12px 16px) DataTablerendered withflushandfillHeightprops- 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:
.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: 20pxandgap: 20pxbetween 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:
.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:
.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:
.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:
.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:
.container { padding: 16px; overflow-y: auto; flex: 1; }
- Has padding:
16pxall 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:
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,.diagramSectionAgentHealth.module.css→.configBar,.eventCardAgentInstance.module.css→.processCard,.timelineCardClickHouseAdminPage.module.css→.pipelineCardAppConfigDetailPage.module.css→.headerRouteDetail.module.css→.headerCard,.diagramPane,.statsPane,.executionsTable,.routeFlowSection
Prioritized Fixes
P0 — User-reported issues
- Exchanges table full-bleed:
Dashboard.tsxshould wrap its table intableStyles.tableSectionand use the shared table header classes instead of custom ones. Custom.tableHeaderpadding (8px 12px) should match shared (12px 16px). - Apps detail flat controls:
AppsTab.tsxconfig sections should wrap form groups insectionStyles.section(fromsection-card.module.css), matching the OIDC page pattern. - Apps deployment table: Replace manual
<table>withDataTableinsidetableStyles.tableSection.
P1 — Padding normalization
- 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. - DashboardTab.module.css: Add side padding to
.content.
P2 — Shared module adoption
- Replace duplicated card patterns: Import
section-card.module.cssortable-section.module.cssinstead of duplicating the card CSS in:DashboardTab.module.css(.errorsSection-> usetableStyles.tableSection)AgentHealth.module.css(.configBar,.eventCard)AgentInstance.module.css(.processCard,.timelineCard)ClickHouseAdminPage.module.css(.pipelineCard)
- Database admin: Wrap tables in
tableStyles.tableSection. - Admin detail pages (RBAC, Environments): Wrap form sections in
sectionStyles.section.