Design language

The working brand for Cobrowse — in light and dark. Everything here renders from the real tokens (src/app/globals.css) and the real UI kit (src/components/ui), so this page is always true: flip the theme switcher above and the whole language flips with it. Re-branding later is a token edit, not a rewrite. Narrative & rules: docs/brand.md.

Two panes, one page

The mark is two overlapping browser panes: the visitor's page and the agent joining it. Togetherness, not surveillance.

Consent green

The primary color is the color of the “Allow” button — of consent given and a healthy live session.

The live spark

Fuchsia marks what is live right now: the remote cursor, live-session badges. Used sparingly, so “live” always stands out.

Color

Components use the semantic tokens — never raw palette steps — so every surface themes itself. Each token is defined once with CSS light-dark(); the swatches below are live. Click a chip to copy its utility class. Brand color is for actions and state, not decoration; amber/red are semantic only.

Surfaces

Ink (text)

Lines (borders)

Action

Status & live

Raw scales — decorative brand color only (gradients, illustrations); everything functional goes through the semantic tokens above.

brand (primary — “consent green”)

brand-50

brand-100

brand-200

brand-300

brand-400

brand-500

brand-600

brand-700

brand-800

brand-900

brand-950

live (the co-browsing accent)

live-400

live-500

live-600

Typography

Geist Sans for everything (--font-sans); Geist Mono for keys, ids, PINs and code (--font-mono). Numbers in stats use tabular figures.

Guided help, on the page

text-4xl · font-bold · tracking-tight — page heroes

Section heading

text-xl · font-bold — page titles

Card title

text-base · font-semibold · text-ink — card titles

Body copy. Cobrowse is guided help on the page you're already on: calm, competent, consent-first. We never say “take over”; we say “look at it together”.

text-sm · text-ink-secondary — body

Muted meta line.

text-xs · text-ink-muted / text-ink-faint — meta & hints

cb_Xy12… · PIN 4271 · 1,024

font-mono — keys, PINs, tabular numbers

Spacing & radii

One spacing unit = --spacing: 0.25rem; every gap/padding/margin utility derives from it. Cards round with rounded-card (0.75rem), controls with rounded-control (0.5rem); the embeddable widget uses --radius-widget (14px).

10.25rem
20.5rem
30.75rem
41rem
61.5rem
82rem
123rem
164rem

rounded-control

0.5rem

rounded-card

0.75rem

--radius-widget

14px

rounded-full

pill

Icons

The standard set: Lucide icons animated with Motion (animate-ui), vendored into @/components/icons. Import by name and set the size with a Tailwind size-* class. They animate on animateOnHover, animateOnTap, animateOnView or an animate trigger — or wrap a region in <AnimateIcon animateOnHover> to drive every icon inside it. Hover a tile to see it move.

LayoutDashboard
Activity
Blocks
Users
Settings
PhoneCall
MessageCircle
Bell
Send
Search
Copy
Check
CircleCheck
Plus
Trash2
ArrowRight
ChevronDown
ExternalLink
LogOut
Lock
Key
Fingerprint
BadgeCheck
Wifi
Gauge
Clock
Route
Compass
Paintbrush
Bot
Sun
Moon
Monitor
Sparkles
Star
PartyPopper

UI kit

Buttons

Variants primary / secondary / ghost / danger; sizes md / sm; busy state.

Badges & status

neutralbrandsuccesswarningdangerlive connected offline

Forms

Field owns label/hint/error; controls share one look.

Tabs, stats & empty states

Agents available

3

Fresh presence

Live sessions

1

Across the workspace

Open callbacks

12

Waiting right now

Nothing here yet

Empty states always say what would fill them — and how.

The on-page widget

Rendered by the same preview component the widget editor uses — themable per customer (its own light/dark, independent of the app theme), these are our defaults.

Talk to us right here

Start a voice call in your browser — no phone needed.

Your support code

Read this code to your support agent on the phone.

Request a callback

Leave your phone number and we'll call you back.

Live help

Theming — how it works

One definition, both modes

Every semantic token is defined once in globals.css with light-dark() and resolves against the effective color-scheme:

@theme {
  --color-surface: light-dark(
    #ffffff,               /* light */
    oklch(0.21 0.04 266)   /* dark  */
  );
}
/* usage: bg-surface, text-ink, border-line … */

Zero-flash resolution

Default is the OS preference — pure CSS (color-scheme: light dark), no script. An explicit pick lands in the cobrowse_theme cookie; the root layout renders it as <html data-theme> on the first byte. Either way the first paint is right — no flash, no middleware.

:root { color-scheme: light dark; }   /* follow OS   */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark;  }

Pinned-light islands

Surfaces that mirror inherently light external content — the session replay canvas, the demo's fake shop, the CRM mail composer — pin their subtree with Tailwind's scheme-light: nested tokens resolve light, the app theme stays out.

<div className="scheme-light bg-white …">
  {/* mirrored visitor page */}
</div>

Rules of thumb

  • Semantic tokens only — no raw slate-* on themed surfaces.
  • dark:is a last resort, for what a token can't express (decorative gradient intensity). Zero dark: on plain surfaces.
  • Brand color is for actions and state, one primary action per view; amber/red are semantic, never decorative.
  • The embeddable snippet, emails and the favicon theme independently (customer-themed / always light / fixed) — by design.