--- name: Mogplex App description: Design guidance for Mogplex's repository workspaces and authenticated product surfaces. colors: brand-accent: "oklch(66.57% 0.225 36.57)" brand-accent-hover: "oklch(61.6% 0.2079 36.63)" background-light: "oklch(96.88% 0.007 96)" card-light: "oklch(98.84% 0.006 95)" background-dark: "oklch(10.88% 0.006 132)" card-dark: "oklch(13.92% 0.007 128)" foreground-light: "oklch(17.48% 0.008 124)" foreground-dark: "oklch(94.57% 0.008 98)" muted-foreground-light: "oklch(54.72% 0.012 108)" muted-foreground-dark: "oklch(74.58% 0.011 103)" border-light: "oklch(89.8% 0.01 101)" border-dark: "oklch(28.11% 0.01 116)" success: "oklch(72.25% 0.192 149.58)" warning: "oklch(76.86% 0.1647 70.08)" destructive: "oklch(63.68% 0.2078 25.33)" info: "oklch(62.31% 0.188 259.81)" typography: body: fontFamily: "Geist, system-ui, sans-serif" fontSize: "0.875rem" lineHeight: 1.4285714286 label: fontFamily: "Geist, system-ui, sans-serif" fontSize: "0.75rem" lineHeight: 1.3333333333 mono: fontFamily: "Geist Mono, SF Mono, monospace" rounded: sm: "2px" md: "4px" lg: "6px" xl: "10px" 2xl: "12px" spacing: 1: "4px" 2: "8px" 3: "12px" 4: "16px" 6: "24px" 8: "32px" components: button-primary: backgroundColor: "{colors.brand-accent}" textColor: "{colors.card-light}" typography: "{typography.body}" rounded: "{rounded.md}" height: "36px" input: rounded: "{rounded.md}" height: "36px" card: rounded: "{rounded.lg}" app-navigation-link: rounded: "{rounded.lg}" height: "40px" --- # Design System: Mogplex App ## Overview This guide covers the authenticated app: repository workspaces, Control, agents, flows, models, settings, and operational views. Read [PRODUCT.md](./PRODUCT.md) for users, purpose, and accessibility principles. The interface should be precise, calm, and capable, with the selected repository, runtime, task, and action consequences visible where they matter. **Scope matters.** This is product UI guidance, not a universal marketing style. Landing pages and public marketing/auth surfaces have their own scoped styles in [components/marketing](./components/marketing), including `landing-v2.css`, `subpage.css`, and `auth.css`. Preserve their established typography and visual direction when working there; do not apply this document as a marketing redesign. The code is the source of truth. Values above describe the shared app defaults, not every contextual override. Use the semantic tokens in [app/globals.css](./app/globals.css), the fonts in [app/layout.tsx](./app/layout.tsx), and the existing [UI primitives](./components/ui). Update this guide in the same change when those defaults change. Do not copy its literal colors into components. Dense layouts are appropriate when they preserve readable context and actions. At narrow widths, rearrange or collapse panels deliberately instead of shrinking text and controls. Keep motion tied to state feedback and respect reduced motion. Page and dashboard scrolling suppress vertical overscroll on devices whose primary pointer is fine, including touchscreen laptops. Coarse-pointer devices keep native overscroll; horizontal navigation gestures remain available. The root background matches the page theme behind native touch overscroll. Nested run details and agent reports contain vertical scrolling so reaching their ends does not move the surrounding page. The former generated `DESIGN.json` remains a historical archive, not an active token source or required companion to this guide. ## Colors Warm neutrals carry the app, with orange for primary actions and restrained semantic colors for state. Light and dark themes use the same semantic roles. - Use `background`/`foreground` for the page and its text, `card`/`card-foreground` for raised content, and `popover`/`popover-foreground` for floating content. - Use `primary`/`primary-foreground` for primary actions. The CSS `accent` role is a neutral interaction surface; it is different from the orange `brand-accent`. - Use `muted-foreground` for secondary text and `border`/`input` for boundaries. Keep the `sidebar-*` roles for navigation. - Use `accent-green`, `accent-amber`, `destructive`/`accent-red`, and `accent-blue` for success, warning, failure, and information. Preserve established chart and project-color mappings rather than assigning new meanings to their colors. - Control has an existing `ink-*` scale and `addg`/`delr` roles. Preserve those mappings when editing Control; do not replace them with literal color values. **State needs more than color.** Pair status with text, an icon, or another accessible indicator. Confirm text and focus contrast in both themes. **Toasts always carry a status.** Use `toast.success()` for completed actions (created, saved, connected, copied), `toast.info()` for neutral state changes (archived, paused, removed, queued), `toast.warning()` for partial success or something that needs attention, and `toast.error()` (variant `destructive`) for failed actions. Each variant tints the surface, adds an accent bar, and shows a distinct, high-contrast icon with a screen-reader label. Notifications use the measured space between workspace tools and the composer. When that space is short, a compact row keeps text scrollable and actions fixed at 44px. Stop remains above the footer on short landscape screens. They never take focus when they appear. Bursts preserve every open message and action in a scrollable stack; newly added messages release already-dismissed cards and their removal timers. Respect reduced motion, and let users pause expiry by hovering or focusing a notification. Routine updates, including warnings, use polite screen-reader announcements; failures interrupt by default. Override the announcement type only when a specific message requires urgency. ## Typography Use `font-sans` for human-readable text and `font-mono` for code, paths, commands, identifiers, and machine values. The layout loads Geist Sans and Geist Mono; the CSS font variables supply their fallback stacks. Shared controls and descriptions commonly use `text-sm` (14px with a 20px line height); compact metadata uses `text-xs` (12px with a 16px line height). These are common roles, not a requirement to resize every view. Use the surrounding screen's heading hierarchy instead of inventing a global heading size. The shared input uses `text-base` (16px) below the `md` breakpoint and `text-sm` from `md` upward. Preserve this distinction. Use display typography only within surfaces that already define it, not in routine app labels or controls. ## Elevation Use tonal surfaces and one-pixel borders for ordinary separation. The app's `--radius` is 6px; the frontmatter radius keys match the Tailwind aliases derived from it. The badge's explicit 3px radius is a component-specific exception. Resting cards and bordered section surfaces use `rounded-lg` (6px), including hand-authored sections in Settings, Connections, and the agent library. This matches Geist's [base material](https://vercel.com/geist/materials). Reuse that token when adding a surface; keep dividers and adjoining rows inside the same card square. Clip edge-to-edge row fills to the card's corners when needed. Inline callouts and alert boxes use `rounded-md` (4px), matching their existing settings counterparts. The app shadow vocabulary in `app/globals.css` is: | Token | Geometry | Role | | --- | --- | --- | | `--app-shadow-sm` | `0 1px 2px`, 5% opacity | Small control separation | | `--app-shadow-md` | `0 4px 12px`, 10% opacity | Raised content | | `--app-shadow-lg` | `0 10px 30px`, 18% opacity | Floating overlays | | `--app-shadow-card` | `0 12px 30px`, 12% opacity | Surfaces using `shadow-app-card` | | `--app-shadow-panel` | `0 18px 50px`, 14% opacity | Surfaces using `shadow-app-panel` | All five use the dark neutral hue defined in CSS. These app tokens are distinct from Tailwind's `shadow-xs`, `shadow-sm`, and `shadow-lg` utilities used by shared primitives. Reuse the component's existing elevation before adding another shadow. ## Components Reuse the implementations below, including their variant APIs and accessibility behavior. Defaults can be overridden by the owning screen; inspect it first. - **[Button](./components/ui/button.tsx):** 4px corners; default, small, and large heights of 36px, 32px, and 40px. Use the existing primary, destructive, outline, secondary, ghost, and link variants. Primary hover uses `primary/90`, not the separate brand-hover token. Focus uses a 3px ring; active feedback scales to 97% with a 150ms base transition. Keep loading labels stable in width where practical and explain unavailable actions nearby. Native bordered app action buttons use the same 4px radius. Preserve pill buttons, circular icon buttons, and the seams of grouped controls. Shared primitives marked with `data-slot` retain their own radius variants; native pill buttons and tab, radio, and switch roles are excluded from the action-button override. - **[Input](./components/ui/input.tsx):** 36px height, 4px corners, horizontal padding of 12px. In the authenticated app, bordered inputs and textareas use the secondary surface background. Hand-authored fields use the same corners, focus ring, invalid state, and disabled treatment as the shared primitive. Preserve the focus ring, `aria-invalid` treatment, responsive text size, and associated label. A placeholder is not a label. - **[Password input](./components/ui/password-input.tsx):** password and secret fields always show an eye button. Newly entered values can be shown or hidden without changing the value or submitting the form, and hide when focus leaves the field and its eye button. Saved secrets that the server does not return use a disabled, muted eye; unavailable saved API-key fields are muted and disabled. An editable replacement field stays usable. Give each eye the field or provider name so multiple secrets have distinct accessible controls. Preserve compact text sizes at desktop breakpoints. - **Native bordered dropdowns:** secondary surface background, 4px corners, and an inset, vertically centered chevron, matching the shared control scale. The app stylesheet reserves room for the indicator on pages and in portaled dialogs. Native keyboard and picker behavior remain available; multiple-select listboxes keep their native treatment. Forced-color mode uses the system indicator. - **[Switch](./components/ui/switch.tsx):** 36px by 20px capsule with a 14px thumb. Off is a quiet secondary track with a 2px `border` outline and a grey `neutral-5` thumb; the thumb, not the outline, carries the 3:1 contrast against the page. On is a solid green `--switch-on` track (darker in light theme so it keeps 3:1 against the page) with a white thumb. Every toggle in the app uses this one style. Keep text outside the control. Thumb position conveys state alongside color. The quiet Off track and outline are a deliberate WCAG 1.4.11 exception chosen by Charles (2026-11-09): they sit near 1.2 to 1.4:1 against the page, so the grey thumb (3:1 or more against the page, asserted in `switch-appearance.spec.ts`) is what identifies the control and its state. If this is revisited, hold the Off outline at `--muted-foreground` and keep the `secondary` fill. An invisible surrounding hit area keeps the pointer target at least 24px tall. Keep the Radix switch semantics, associated label, keyboard focus ring, disabled treatment, and reduced-motion behavior. - **[Card](./components/ui/card.tsx):** 6px corners, border, and `shadow-sm`. The container has 24px vertical padding and gap; header, content, and footer provide their own 24px horizontal padding. Do not add another blanket padding layer or wrap every section in a card. - **[Badge](./components/ui/badge.tsx):** 3px corners, 12px medium text, and padding of 2px vertically and 8px horizontally. Reuse `live`, `warn`, and `error` variants for their existing meanings and include a readable label. - **[App navigation](./components/app-sidebar.tsx):** app links use 40px rows, 6px corners, Iconoir icons, and `aria-current` on the active destination. The generic [sidebar menu](./components/ui/sidebar-menu.tsx) has different defaults (32px rows, 4px corners); these are separate components. Settings opens a secondary menu in the same sidebar, with a back arrow to restore the main navigation. Each Settings section has a scoped page URL. The mobile navigation sheet follows the same pattern and closes after a section is selected. Billing has Billing Settings and Usage tabs. Connections has Integrations and MCP Servers tabs; MCP Servers is not a Settings section. - **[Command Center](./components/control/task-workspace.tsx):** use a compact app navigation rail, a resizable task sidebar, and a persistent conversation. Preview, changes, files, and plan open in a resizable right pane; commands, terminal, and logs open in a bottom drawer. Closing a pane hides its content without ending execution. At narrow workspace widths, tools fill the available space and their close control returns to the same conversation. Sandbox selection belongs to the task; ambiguous environments require an explicit choice. Read-only command output and the interactive shell remain separate tabs. Repository workspace links select the repository's latest task, or a new composer with that repository selected. Run links open the original run in Command Center. Live runs accept guidance; completed runs can continue in the same chat after saved history loads. Opening a link never starts an agent. Follow-ups and steering stay in one continuous, scrollable transcript. Keep earlier requests and their open tool details mounted when another turn arrives; do not move them behind an automatic history disclosure. Only the latest request shows live activity. Follow new output while the reader is at the bottom; scrolling up pauses automatic following. Show workspace verification as four compact rows: typecheck, lint, build, and tests. Each row discloses the actual command, exit result, and output without losing its open state when the chat is steered. Preserve stack frames and source literals in output; redact credentials. Distinguish failed, unconfirmed, unavailable, and explicitly skipped checks. A passing headline requires all four full checks against the checked source contents; later edits invalidate earlier results. Operator limits stay visible with their reason. Durable workspace files do not require always-running compute. Pause idle compute after task work, retaining it for an active preview, worker, command, or explicit keep-running request. Show routine running/paused state in the sandbox controls; chat explains known reasons cleanup was refused and warns when its outcome cannot be confirmed. Preserve each distinct warning when cleanup touches several workspaces, with actionable refusals first. Active tool buttons also collapse their pane. Right panes and the terminal drawer can expand into the workspace and restore their previous split size; Escape restores the split view. Sidebar collapse preserves project disclosure and scroll state. Use short, interruptible transform transitions for these layout changes, keeping controls interactive and live previews and editors mounted. Respect reduced-motion preferences with immediate updates. - **[Dialog](./components/ui/dialog.tsx):** reuse the Radix-backed focus and dismissal behavior, 6px corners, 24px padding, and responsive action layout. Provide a title and description. Use inline disclosure when it can complete the task without interrupting the surrounding work. For each changed interaction, account for hover, focus, active, disabled, loading, empty, error, and success states where applicable. Use semantic controls and accessible names for icon actions. Preserve usable touch targets and verify keyboard flow and narrow layouts when changing UI. ## Do's and Don'ts - Do show the exact resource and context affected by an action. - Do reuse semantic theme tokens and existing primitives. - Do distinguish repository state, agent execution, and sandbox lifecycle. - Do explain unavailable actions and errors in language the operator can act on. - Do preserve light/dark behavior, visible focus, and reduced-motion support. - Do update this document when shared defaults change, with code as evidence. - Don't resemble a decorative AI demo, a disconnected chatbot, or a Git client that obscures runtime behavior, as described in PRODUCT.md. - Don't add ornamental motion, novelty controls, or broad visual churn. - Don't collapse distinct resources into one label or icon. - Don't copy marketing display treatments into routine app controls. - Don't infer new token values or universal component rules from the old archive.