UCCA Ops Console UI Specification¶
Design standard for all control plane surfaces
Governing Document
This specification governs all UI development on ops.ucca.online. Read it before making any visual changes to the ops console. Deviations require explicit approval.
Section 1 — Design Philosophy¶
Core Model: Linear¶
The UCCA Ops Console follows the Linear design model: fast, keyboard-first, opinionated. Every interaction should feel immediate. Chrome is minimal. Navigation is hierarchical and predictable. The command palette is the primary navigation tool for power users.
Hybrid Approach by Layer¶
The four-layer control plane architecture determines UI density and polish:
| Layer | Name | UI Model | Feel |
|---|---|---|---|
| L1 | UCCA Ops (God Mode) | Linear | Dense, fast, keyboard-first. Raw and functional. |
| L2 | SU Overlay | Linear + Vercel | Toggleable panels. Clean sidebars. Debug/audit mode. |
| L3 | World Control Plane | Linear + Stripe | Domain admin. Stripe polish on revenue/analytics. |
| L4 | Client View | Vercel | Clean, minimal, branded. Zero UCCA fingerprinting. |
Layers 1-3 share the same codebase and component library. Layer 4 is a separate branded surface per world. Visibility is determined entirely by credentials — the same URL renders different views depending on who is logged in.
Design Principles¶
- Speed over decoration. Every click, every transition, every render should feel instant. No loading spinners where data can be prefetched.
- Keyboard supremacy. Every action reachable via keyboard. Cmd+K is the universal entry point. Mouse is supported but never required.
- Density matches role. L1/L2 users see dense information. L3/L4 users see progressively cleaner views. Same data, different presentation.
- No dead ends. Every page has a purpose. Stubs either show useful placeholder content or are hidden until live.
- Consistency is mandatory. Navigation patterns, spacing, typography, colour usage — identical across all control plane surfaces.
Section 2 — Navigation Hierarchy¶
Current Problems¶
- Worlds at the top of the sidebar — should be below platform controls
- Two separate 'Dashboard' entries — confusing
- 'Home' section unclear — what is Home vs Control Plane?
- No visual distinction between platform-level and world-level navigation
- Stubs visible with no content — makes console feel unfinished
New Sidebar Structure¶
The sidebar is the primary navigation surface. It is always visible and follows this exact structure:
Top Bar (Fixed)¶
- UCCA logo (top left, links to platform dashboard)
- Search trigger (Cmd+K shortcut hint, opens command palette)
Sidebar Sections (Top to Bottom)¶
Platform — always visible, always first
- Dashboard (platform-wide overview: all worlds summary, system health, recent activity)
- Health (full health page: surface health, status, traffic analytics)
- Incident Status (incident.io feed)
- Authority Catalogue
- Access Control
- Settings
Worlds — collapsible, below platform
Each world is an expandable section. Clicking a world name enters that world's context. The sidebar updates to show world-specific navigation:
- RTOpacks (with flag icon, 'live' badge)
- Dashboard (world dashboard: RTO count, enrichment, scope rows, drift)
- RTO Explorer (live)
- Enrichment
- Scope Analytics
- Provenance
- Drift Monitor
- Pipeline
- US General (scaffolded — 'coming soon' badge)
- Door (scaffolded — 'coming soon' badge)
Resources — bottom of sidebar, always visible
- Help (external link)
- Knowledge (external link)
- Docs (external link)
Active State¶
The active page is visually highlighted in the sidebar with a left border accent and background tint (matching Cloudflare's active state pattern). Parent sections expand automatically when a child is active. Collapsed sections show a count badge if they contain alerts or actionable items.
World Context Switching¶
When a user clicks into a world, the sidebar enters 'world context'. The world name appears as a breadcrumb above the world-specific navigation. A back arrow or breadcrumb returns to platform level. The URL reflects the context: /w/au-vet/dashboard, /w/au-vet/enrichment, etc.
Stub Policy¶
Pages that are not yet implemented:
- If architecturally planned: Show in sidebar with a subtle 'stub' badge. Page content shows a meaningful description of what will be built, not an empty page.
- If speculative: Hide from sidebar entirely until committed to.
Section 3 — Command Palette (Cmd+K)¶
The command palette is the single most important UI component in the console. It is inspired by Linear's command menu and follows the same interaction model seen in Cloudflare, Vercel, Notion, and Stripe dashboards.
Activation¶
- Cmd+K (macOS) / Ctrl+K (Windows/Linux)
- Click the search area in the top bar
- Always available from any page
Capabilities¶
- Navigation: Type any page name to jump directly. 'Health', 'RTOpacks Dashboard', 'Authority Catalogue'.
- Search: Search across RTOs by name, code, or ABN. Search across enrichment records. Search across authority objects.
- Actions: 'Declare Incident', 'Run Backup', 'Switch World'. Prefix with
>for action mode (like VS Code). - Recents: Show recently visited pages and recently searched items before the user types.
Search Tips¶
When the palette opens with no input, show search scope hints (matching Cloudflare's pattern):
| Prefix | Scope |
|---|---|
rto: |
Search RTOs |
d1: |
Search D1 databases |
access: |
Search Access policies |
> |
Run a command |
Implementation¶
Use a portal/overlay component. Trap focus inside the palette when open. Escape closes. Arrow keys navigate results. Enter selects. Debounce search input at 150ms. Results appear instantly from a local index, with async remote results appended.
Search Result Display¶
Hard Rule
Search results show ONLY icon + breadcrumb path. No descriptions, no subtitles, no purpose lines. The command palette is a high-speed navigation tool, not a discovery tool. The breadcrumb IS the description. Match Cloudflare's search result density exactly.
Each result row contains:
- Icon — matching the page's sidebar icon (e.g. shield for Health, database for RTO Explorer)
- Breadcrumb path — hierarchical path to the destination (e.g.
Platform > Health,RTOpacks > RTO Explorer) - Nothing else. No description text. No "purpose" lines. No secondary information.
This keeps the result list dense and scannable. A user typing "heal" should see the result and hit Enter in under 500ms. Any visual clutter in the result list slows this down.
Section 4 — User Identity & Roles¶
Current State¶
Cloudflare Access provides authentication via OTP to admin@ucca.online. There is no user identity display in the console, no role system, and no way to distinguish between operators.
Identity Display¶
Top right corner of every page:
- User avatar (initial-based, e.g. 'T' for Tim, 'J' for Jimmy)
- User name
- Role badge (Admin, Operator, Viewer)
- Click opens a dropdown: Profile, Theme toggle, Sign out
Role Model (Phase 1 — Simple)¶
Three roles, hardcoded initially. Migrate to database-driven roles when user management is built.
| Role | Layer Access | Capabilities |
|---|---|---|
| Admin | L1 + L2 + L3 | Full access. Infrastructure, all worlds, all controls. Tim and Alex. |
| Operator | L2 + L3 | SU overlay into worlds. Business data, analytics, enrichment. Cannot modify infrastructure. Jimmy. |
| Viewer | L3 (scoped) | Read-only access to a specific world. Future world admins, auditors. |
Authentication Flow¶
Cloudflare Access remains the perimeter gate. Behind it, the ops console checks the authenticated email against a user table (initially a hardcoded map in the codebase, later D1-backed). The email determines the role, which determines sidebar visibility and page access.
- Phase 1: Hardcoded user map in the codebase (Tim = Admin, Jimmy = Operator, Alex = Admin).
- Phase 2: D1-backed user table with role management in the Access Control page.
- Phase 3: World-scoped roles (a user can be Admin in RTOpacks but Viewer in US General).
Cloudflare Access Configuration¶
Add Jimmy's email and Alex's email to the Cloudflare Access policy for ops.ucca.online. Each person authenticates with their own email OTP. The console then maps their email to a role.
Section 5 — Theme System (Light / Dark)¶
Implementation¶
Tailwind CSS dark: variant provides the theme toggle. All components must support both themes from day one. No component ships without dark mode styles.
Colour Tokens¶
Define semantic colour tokens, not raw hex values. All colours reference tokens:
| Token | Light | Dark |
|---|---|---|
--bg-primary |
white (#FFFFFF) | slate-950 (#020617) |
--bg-secondary |
slate-50 (#F8FAFC) | slate-900 (#0F172A) |
--bg-tertiary |
slate-100 (#F1F5F9) | slate-800 (#1E293B) |
--text-primary |
slate-900 (#0F172A) | slate-50 (#F8FAFC) |
--text-secondary |
slate-500 (#64748B) | slate-400 (#94A3B8) |
--text-muted |
slate-400 (#94A3B8) | slate-500 (#64748B) |
--border-default |
slate-200 (#E2E8F0) | slate-700 (#334155) |
--accent-primary |
blue-600 (#2563EB) | blue-500 (#3B82F6) |
--status-healthy |
green-600 (#16A34A) | green-500 (#22C55E) |
--status-warning |
amber-600 (#D97706) | amber-500 (#F59E0B) |
--status-error |
red-600 (#DC2626) | red-500 (#EF4444) |
User Preference¶
- Default: follow system preference (
prefers-color-scheme) - Override: user can set Light, Dark, or System in the profile dropdown
- Preference stored per user (initially localStorage, later user table)
Rule
No component ships without both light and dark styles. This is not optional.
Section 6 — Component Standards¶
Component Library¶
Use shadcn/ui as the base component library. It provides unstyled, accessible primitives that work with Tailwind. Do not introduce additional component libraries (no Material UI, no Chakra, no Ant Design).
Typography¶
| Element | Font | Size | Weight |
|---|---|---|---|
| Page title | Inter / system-ui | 24px (text-2xl) | Semibold (600) |
| Section heading | Inter / system-ui | 18px (text-lg) | Semibold (600) |
| Card title | Inter / system-ui | 14px (text-sm) | Medium (500) |
| Body text | Inter / system-ui | 14px (text-sm) | Normal (400) |
| Caption / label | Inter / system-ui | 12px (text-xs) | Medium (500) |
| Code / mono | JetBrains Mono / monospace | 13px | Normal (400) |
Spacing¶
Use Tailwind's spacing scale consistently. Standard gaps:
| Element | Class | Value |
|---|---|---|
| Page padding | p-6 |
24px |
| Card padding | p-4 |
16px |
| Section gap | gap-6 |
24px |
| Inline gap | gap-2 or gap-3 |
8px or 12px |
| Sidebar width | w-64 |
256px |
| Sidebar collapsed | w-16 |
64px |
Cards¶
Cards are the primary content container. Every card follows this pattern:
- Rounded corners:
rounded-lg(8px) - Border: 1px solid
--border-default - Background:
--bg-primary - Shadow: none (flat) or
shadow-smon hover - No gradient backgrounds. No coloured borders except for status indicators.
Status Indicators¶
Three-state system used everywhere:
- Healthy / Active / Good — green dot + text
- Warning / Degraded / Review — amber dot + text
- Error / Down / Critical — red dot + text
Always use both colour AND text. Never rely on colour alone.
Tables¶
Data tables follow Linear's pattern: compact rows, hover highlight, sortable headers, inline actions. Use 14px body text, 12px headers. Row height: 40px minimum. Alternating row colours are optional (use --bg-secondary on even rows in dense tables).
Badges¶
Used for status, roles, and tags. Consistent sizing: text-xs, px-2, py-0.5, rounded-full. Colour matches status system (green/amber/red) or neutral (slate) for informational badges like 'live', 'stub', 'airlock'.
Section 7 — Footer Strip¶
Every control plane page has a consistent footer strip. Thin, unobtrusive, identical everywhere.
Contents¶
- Left: UCCA (c) 2026 (or current year)
- Centre: Status | Docs | Knowledge | Support
- Right: Version number (e.g. v0.1.0) | Environment badge (production / staging)
Behaviour¶
- Fixed at bottom of viewport when page content doesn't fill the screen
- Scrolls with content when page exceeds viewport
- Status link points to status.ucca.online
- Docs and Knowledge open in new tabs
Section 8 — Keyboard Shortcuts¶
Global Shortcuts¶
Available on every page:
| Shortcut | Action |
|---|---|
Cmd+K |
Open command palette |
Cmd+/ |
Show keyboard shortcuts overlay |
G then D |
Go to Dashboard |
G then H |
Go to Health |
G then W |
Go to Worlds list |
G then S |
Go to Settings |
Escape |
Close any overlay, modal, or palette |
? |
Show keyboard shortcuts (alternative) |
Within Data Tables¶
| Shortcut | Action |
|---|---|
J / K |
Move selection down / up |
Enter |
Open selected item |
/ |
Focus search/filter within table |
Section 9 — Responsive Behaviour¶
The ops console is primarily a desktop tool. Mobile support is secondary but should not be broken.
Breakpoints¶
| Range | Behaviour |
|---|---|
| Desktop (>1280px) | Full sidebar, full content area |
| Tablet (768-1280px) | Collapsible sidebar (icon-only mode), full content |
| Mobile (<768px) | Sidebar hidden behind hamburger menu. Content takes full width. Command palette remains accessible. |
Sidebar Collapse¶
On tablet, the sidebar collapses to icon-only mode (w-16). Hovering expands it temporarily. User can pin it open. Preference is persisted.
Section 10 — Implementation Phases¶
This specification is implemented in phases. Each phase is a complete, deployable unit.
Phase 1: Foundation (Priority)¶
Navigation hierarchy, sidebar restructure, active state highlighting, stub policy enforcement.
- Restructure sidebar per Section 2
- Implement active state highlighting
- Remove or improve stubs per stub policy
- Add consistent footer strip
- Deploy and validate
Phase 2: Command Palette¶
Build the Cmd+K command palette per Section 3.
- Overlay component with focus trap
- Navigation search (all pages indexed)
- RTO search integration
- Action mode (
>prefix) - Recent items
Phase 3: Identity & Roles¶
User identity display and role system per Section 4.
- Add identity component (top right)
- Hardcoded user map (Tim = Admin, Jimmy = Operator, Alex = Admin)
- Add Jimmy and Alex to Cloudflare Access policy
- Role-based sidebar visibility
- Profile dropdown with theme toggle
Phase 4: Theme System¶
Dark/light mode per Section 5.
- Define colour tokens
- Implement
dark:variants across all components - Theme toggle in profile dropdown
- System preference detection
- Persist user preference
Phase 5: Component Standardisation¶
Audit all existing components against Section 6 standards. Refactor to consistency.
- Typography audit and standardisation
- Card component standardisation
- Table component standardisation
- Badge and status indicator standardisation
- Spacing audit
Section 11 — What Not to Do¶
Hard Rules
Violating these creates technical debt that compounds.
- DO NOT introduce additional component libraries (no MUI, no Chakra, no Ant Design)
- DO NOT use inline styles. All styling via Tailwind utility classes.
- DO NOT ship a component without dark mode support
- DO NOT create one-off navigation patterns. Every page uses the standard sidebar.
- DO NOT use colour alone to convey meaning. Always pair with text or icons.
- DO NOT add loading spinners where data can be prefetched or skeletonised
- DO NOT create modal dialogs for navigation. Modals are for confirmations and destructive actions only.
- DO NOT hard-code world-specific logic in shared components. Use the world context system.
- DO NOT expose Layer 1/2 controls to Layer 3/4 users. Permissions are not optional.
Section 12 — Reference Implementations¶
When building new components, study these implementations:
| Pattern | Reference | What to Study |
|---|---|---|
| Command palette | Linear, Vercel, VS Code | Overlay, focus trap, search scoping, action mode |
| Sidebar navigation | Cloudflare Dashboard | Hierarchy, active state, expandable sections, icon-only collapse |
| Data tables | Linear, Stripe | Compact rows, inline actions, sortable headers, keyboard navigation |
| Identity display | Cloudflare, Vercel | Top-right avatar, dropdown menu, role badge |
| Footer strip | Cloudflare | Thin, consistent, links to status/docs/support |
| Status indicators | incident.io, Vercel | Three-state (green/amber/red), dot + text, consistent everywhere |
| Theme toggle | Linear, Notion | System/Light/Dark options, immediate switch, no flash |
| World context switch | Cloudflare (zone switch) | Breadcrumb, sidebar context change, URL reflects context |
Section 13 — Page-Level Design Rules¶
These rules apply to every page in the ops console. They are non-negotiable.
Rule 1: Every Page Earns Its Existence¶
A page exists because it solves a problem or answers a question. If a page cannot articulate its purpose in one sentence, it should not exist. Stub pages must explain what they will do when built — a blank page with a title is not acceptable.
Rule 2: Information Hierarchy Is Mandatory¶
Every page must have a clear visual hierarchy. The most important information is largest and highest on the page. Secondary information is smaller and lower. The user's eye should flow naturally from the page title through the key metrics to the detail sections. If a user cannot identify the most important element within 2 seconds of landing on the page, the hierarchy has failed.
Rule 3: Cards Are Content Containers, Not Decoration¶
A card groups related information. Every card must contain meaningful content. Empty cards, placeholder cards, and decorative cards waste space and train users to ignore the card pattern. If a section has no content yet, it should either not exist or show a compact stub — not a full-sized empty card.
Rule 4: Metrics Are Not Art¶
Dashboard metrics (KPIs, counters, status indicators) must be readable at a glance. This means: large, clear numbers. Short labels. Consistent formatting. No decorative charts that obscure the actual value. A metric card should answer one question in under 1 second. If you need to study it, it has failed.
Rule 5: Tables Are for Comparison, Not Display¶
A data table exists so users can compare rows and find patterns. This means: consistent column widths, sortable headers, aligned numbers, scannable text. A table that wraps text, has inconsistent alignment, or lacks sorting is not a table — it is a list pretending to be a table.
Rule 6: Empty States Are Informative, Not Apologetic¶
When a section has no data (no incidents, no drift detected, no enrichment queued), the empty state must:
- State clearly what would appear here when data exists
- Indicate whether 'empty' is good (e.g. 'No active incidents' = healthy) or just means 'no data yet'
- Be compact — do not let empty states dominate the page
Never use sad-face illustrations, apologetic language ('Sorry, nothing to show'), or large empty areas. A clean, informative one-liner is always better.
Rule 7: Clickable Things Look Clickable¶
Every interactive element must have visible affordance: hover state, cursor change, and an affordance (chevron, arrow icon). If a card is display-only, it must NOT have a hover state. The user must never wonder 'can I click this?'. Consistency is critical — if some cards in a grid are clickable and others aren't, they must be visually distinguishable.
Rule 8: White Space Is Intentional, Not Accidental¶
Large empty areas on a page signal either incomplete implementation or poor layout. If a section has minimal content (e.g. 'no incidents'), the section should be compact — not a large empty card consuming screen real estate. White space is used to separate logical groups, not to fill a grid that has nothing to show.
Section 14 — Self-Documenting Pages¶
Every page in the ops console must communicate its purpose. This is especially critical for stub and airlock pages, but applies to all pages.
Page Header Standard¶
Every page has a consistent header block:
- Page title (e.g. 'Enrichment')
- Purpose line (one sentence explaining what this page does and why it exists)
- Status badge (live, stub, airlock, beta) — right-aligned
Stub Pages¶
When a page is architecturally planned but not yet implemented, it must show:
- What it will do: A clear 2-3 sentence description of the page's purpose
- What problem it solves: Why this page exists in the system
- Where it fits: Its position in the pipeline or workflow (e.g. 'Enrichment runs after RTO discovery and before Scope Analytics')
- What's needed: What must be built before this page goes live
Airlock Pages¶
Pages gated behind a readiness check (e.g. Pipeline) follow the same pattern but add:
- Readiness criteria: What conditions must be met to unlock this page
- Current status: Which criteria are met and which are pending
Page Descriptions (Current Console)¶
The following descriptions should appear on each page:
| Page | Purpose Line |
|---|---|
| Platform Dashboard | Aggregate view across all worlds: system health, recent activity, key metrics. |
| Health | SLA evidence: live platform status, surface endpoint health, and traffic analytics. |
| Incident Status | Public incident feed from status.ucca.online. Declare and track incidents. |
| Authority Catalogue | Browse and inspect authority objects (units, qualifications, standards) across all worlds. |
| Access Control | Manage who can access the control plane and at what permission level. |
| Settings | Platform-wide configuration: API keys, integration settings, notification preferences. |
| RTOpacks Dashboard | World overview: RTO count, enrichment progress, scope coverage, drift alerts. |
| RTO Explorer | Search, browse, and inspect individual RTOs. Enrichment data, scope rows, contact details. |
| Enrichment | Monitor and manage the auto-enrichment pipeline. View enrichment queue, success rates, data sources (TGA, ABN Lookup). |
| Scope Analytics | Analyse training product scope across RTOs. Coverage gaps, popular qualifications, market analysis. |
| Provenance | Trace the origin and transformation history of every data point. Audit-grade evidence chain. |
| Drift Monitor | Detect when RTO data diverges from authoritative sources. Alert on stale or changed records. |
| Pipeline | Manage ingestion and processing pipelines. Run, schedule, monitor, and replay data flows. |
Section 15 — Claude Code Governance¶
This section defines how Claude Code interacts with the UI specification during development.
Hard Rule: Read Before Building¶
Claude Code must read the UI Specification before making any visual change to the ops console. This is non-negotiable.
The following instruction is in CLAUDE.md:
All UI development on ops.ucca.online must conform to the UI Specification (
architecture/ui-specification.md). Read it before making any visual changes to the ops console. If the spec does not cover a case, present options to Tim before implementing. Never guess at visual design — ask.
Decision Protocol¶
When Claude Code encounters a UI decision not covered by this spec:
- Stop. Do not implement a visual design choice without guidance.
- Identify the gap. What specific decision is needed? (layout, colour, component, interaction)
- Present options. Show 2-3 concrete alternatives with rationale for each.
- Wait for Tim. Implement only after direction is given.
- Update the spec. Once a decision is made, add it to this specification so it's captured for next time.
What This Prevents¶
- Random component choices that don't match existing patterns
- Inconsistent spacing, typography, or colour usage
- Pages that 'work' but don't communicate
- Design debt from ad hoc decisions that compound over time
Spec Evolution¶
This specification is a living document. As new patterns emerge and decisions are made, they are added here. The spec grows with the product. Every visual decision should eventually have a home in this document.
Version History¶
| Version | Date | Change | Author |
|---|---|---|---|
| 1.0 | 2026-03-03 | Initial specification. Linear model, four-layer architecture, five implementation phases. | Tim Rignold / Claude |
| 1.1 | 2026-03-03 | Added page-level design rules (S13), self-documenting pages (S14), Claude Code governance (S15). Command palette search result display clarification (S3). | Tim Rignold / Claude |