Skip to main content

Overview

Monad’s web UI uses a layered surface system: dark-first, dense, quiet, and operational. The primary app canvas is rgb(32 33 36) in dark mode, with panels layered using semi-transparent surface containers instead of heavy shadows or decorative gradients. Light mode exists as the same token system inverted to a pale canvas, not as a separate visual language. The core rule is token parity. Components should use the raw RGB token names when matching the base primitives (--backgroundColor-primary, --backgroundColor-surface-container, --textColor-primary, --borderColor-secondary) and semantic aliases (--background, --card, --foreground, --border) when building ordinary app UI. Do not hard-code one-off oklch or hex values for app chrome.

Colors

Raw base tokens

These tokens are stored as RGB channel triplets so they can be composed with alpha:

Light

Dark

Semantic Aliases

packages/ui/src/styles.css maps the raw tokens into shadcn/Tailwind-compatible aliases: --chart-1 … --chart-5 and the --sidebar-* family follow the same pattern; read packages/ui/src/styles.css for the complete list. The sidebar uses an app-chrome palette independent of content surfaces: foreground is rgb(26 28 31) in light mode and white in dark mode; secondary text is 70% foreground, selected rows are 5%, and hover states and borders are 8%. Keep brand purple for focus, links, and semantic status instead of persistent navigation chrome.

Surfaces

App panels compose the surface-container token over the canvas, with alpha chosen per surface rather than baked into a single utility class:
In dark mode a half-alpha container computes to rgba(22, 23, 24, 0.5) over the rgb(32, 33, 36) canvas; in light mode to rgba(255, 255, 255, 0.5) over rgb(250, 250, 250). The --card, --popover, and --sidebar aliases resolve the same token at full opacity — reach for those first and only drop to a raw alpha composite when a surface genuinely needs to read through. Use this layer for cards, sidebars, composer shells, settings panels, and compact dashboard modules. Use --backgroundColor-secondary without alpha for menus and surfaces that must be fully opaque.

Typography

Use the existing app font stack:
  • --font-ui: the platform system stack (-apple-system, BlinkMacSystemFont, Segoe UI, …) for all interface text.
  • --font-code: the system mono stack (ui-monospace, SF Mono, Menlo, Consolas, …) reserved for source code, terminals, commands, diffs, raw logs, and structured data editors.
Headings should stay functional and compact. The product UI should not use oversized marketing hero typography inside app surfaces. Labels, timestamps, counters, IDs, paths, metadata, buttons, and status text all use the UI family.

App Density Reference

Use these values as the density target for Monad’s app chrome and operational screens.

Font Families

Type Scale

The product UI scale is denser than Tailwind defaults: Practical Monad mapping:
  • Sidebar row text: 13px / 18px, font-weight: 400.
  • Active/sidebar emphasis: at most 500; avoid 600 except brand/title.
  • Toolbar/header title: 14px / 18px, font-weight: 500.
  • Button/control text: 12px / 18px for small controls, 14px / 18px for toolbar/composer controls.
  • Settings labels/descriptions: 12-14px, mostly 400; use 500 only for group titles.
  • Do not use text-xl+ inside app chrome except rare empty states or page headings.

Font Weight

Row And Toolbar Density

Reference row/toolbar tokens: Use these as upper bounds for ordinary chrome. If a sidebar item needs two lines, keep the row at px-2/py-1.5 or px-3/py-2; do not turn sidebar navigation into card-like rows. Adjacent sidebar rows use a 1px gap, producing a 29px center-to-center rhythm.

Buttons And Controls

Reference button variants: Composer button tokens:
In Monad, routine buttons should stay around 28-32px high. Composer send/stop may be larger, but should remain visually subordinate to the input surface; avoid 56px controls unless the whole composer is intentionally oversized.

Lists, Sidebar, Settings

Extracted recurring component patterns:

Composer And Thread Layout

Shapes, Borders, Shadow

Radii are tight. The 1.25 corner-radius scale is already baked into the token values — do not re-apply it: Default app chrome favors hairline borders over elevation:
The full ladder runs --shadow-2xs through --shadow-2xl; each step is a hairline plus a soft ambient layer, never a hard drop shadow. Use shadow-sm/md only for floating or elevated surfaces. Sidebars, route shells, settings groups, and composer surfaces should primarily use background contrast plus 0.5px-1px borders.

Motion

There are no motion custom properties — durations and easings are written as Tailwind utilities on the component. The budget:
  • Routine controls: duration-150 ease-out on color, border, opacity, and shadow only (Button.tsx is the reference).
  • Larger panel/composer state changes: 300ms cubic-bezier(0.4, 0, 0.2, 1).
  • Never animate layout properties, and honor prefers-reduced-motion for anything beyond a state fade.

Shapes

Functional controls use tight radii: Avoid pill-heavy app chrome unless the control is a segmented chip or mode selector.

Components

Buttons

Primary buttons use inverse surface fill:
  • Background: --primary
  • Text: --primary-foreground
  • Hover: slightly reduced opacity or state layer, not a new hue
  • Radius: --radius-md
Secondary, outline, and ghost buttons should use --secondary, --background, --accent, and --border. Avoid shadows and translate-on-hover motion for routine app controls.

Cards And Panels

Default panels:
  • Background: --card
  • Border: 1px solid var(--border)
  • Radius: --radius-lg
  • Shadow: none by default
Use visual hierarchy through density, grouping, and border contrast before adding elevation.

Composer

The composer should feel like a command surface:
  • Surface: rgb(var(--backgroundColor-surface-container) / 0.5)
  • Idle border: rgb(var(--borderColor-secondary) / 0.15)
  • Focus border: same as idle; focus is shown by the animated aurora ring, not a static accent border
  • Focus ring: none on the shell; the aurora layer is the focus treatment
  • Focus surface: same tokenized surface, with only the internal aurora glow layer fading in below content
  • Placeholder: --muted-foreground
  • Radius: --radius-3xl
  • Transition: 300ms cubic-bezier(0.4, 0, 0.2, 1) for border, background, and ring state
The composer’s active border uses these aurora tuning values: duration: 3.4s, borderThickness: 1, borderOpacity: 1, gradientCoverage: 25, tailSoftness: 10, innerGlowBlur: 45, glowMode: inside, and the color set #9154E7, #6056F0, #40D9C6, #4285F4. On Monad’s darker app canvas, the internal glow should be tuned below innerGlowOpacity: 0.25 / innerGlowInset: 53 so it reads as a soft edge shimmer rather than a foreground sweep. It should remain a focused composer treatment, honor prefers-reduced-motion, and never become a decorative glow around ordinary panels. Composer nesting must keep animation and content separated:
chat-input-frame only owns relative positioning. chat-input-aurora is the absolute pointer-events-none overlay sibling above the real surface (relative w-full containing an absolute z-[50] aurora layer plus a role="presentation" surface). The overlay must be internally constrained: inner glow uses four edge masks (top, bottom, left, right) and the border ring uses a content-box mask with padding: 1px. The rotating conic-gradient child is 150vmax, centered at top: 50% / left: 50%, and animated with translate(-50%, -50%) rotate(...); do not rotate the overlay box itself. Do not add a static focus border-color or shell box-shadow that competes with the animated ring.

Layout

The homepage is a workspace overview and must not show the session sidebar on first entry. App views after selecting a session or channel may use the sidebar. Dense operational panels are preferred over marketing layout patterns. Use full-width app bands and constrained inner content rather than card-in-card compositions. Cards are for individual repeated items, modals, settings groups, and framed tools.

Motion

Motion should clarify state changes only:
  • Use 150-200ms transitions for color, border, opacity, and box-shadow.
  • Avoid translate-on-hover for routine controls.
  • Honor prefers-reduced-motion.
  • Do not animate layout properties.

Do

  • Use --backgroundColor-primary and related raw RGB tokens when implementing base primitive controls.
  • Keep dark mode anchored to rgb(32 33 36).
  • Build surface hierarchy with rgb(var(--backgroundColor-surface-container) / 0.5).
  • Keep borders low-alpha and crisp.
  • Use accent purple only for links, focus, and small state cues.

Don’t

  • Do not reintroduce a near-white marketing palette as the primary app system.
  • Do not hard-code new hex/oklch app chrome colors when a token exists.
  • Do not put sidebars on the root workspace homepage.
  • Do not use heavy shadows, glow effects, or decorative gradient backgrounds for ordinary app panels.