Skip to content
Median
Esc
↑↓navigate↵open⌘Jpreview
On this page

Theming

The CSS variables the components read, dark mode, corners, fonts and motion.

:root {
  --primary: oklch(0.55 0.2 260);
  --primary-foreground: oklch(0.985 0 0);
}

The components read the same CSS variables as shadcn/ui. A site that uses shadcn/ui already defines them. A variable you leave out falls back to Median’s light theme.

Variables

Variable Paints Fallback
--card The panel, the modal, the unread card, the feedback panel, the contact form, the composer oklch(1 0 0)
--card-foreground Text on those surfaces oklch(0.145 0 0)
--muted The agent’s bubbles, the AI label, hover fills, initials on teammate avatars. Mixed into the visitor’s bubble oklch(0.955 0 0)
--muted-foreground Timestamps, the status line, placeholders, icon buttons, the Powered by Median line, scrollbars, the letters on teammate avatars oklch(0.556 0 0)
--foreground The 12% tint in the visitor’s bubble, typing dots, the AI agent’s default avatar tile, inline code, hover text oklch(0.145 0 0)
--background The icon on the AI agent’s default avatar, the digits of the unread count, the starter rows, the scroll to latest button oklch(0.976 0 0)
--primary The launcher, send buttons, the highlight ring around an element on your page oklch(0.205 0 0)
--primary-foreground Icons and text on --primary oklch(0.985 0 0)
--destructive The unread count on the launcher, error text, invalid fields oklch(0.577 0.245 27.325)
--border Edges of the panel and cards, dividers oklch(0.916 0 0)
--input Borders of the composer and form fields oklch(0.916 0 0)
--ring Focus rings, the border of a focused composer or field oklch(0.708 0 0)

--popover, --accent and --secondary have no visible effect on the components. For --radius, see Corners.

Where to set them

Set the variables on :root or html. The modal, the highlight ring, the feedback panel, and the widgets from mountMedianSupport, mountMedianSupportModal and mountMedianFeedback render into <body>, outside any wrapper element in your app. The contact form and a MedianSupport you render yourself inherit from wherever you place them.

Dark mode

The components have no dark palette of their own. Define your dark values and they follow, whether you switch themes with a class or a media query.

.dark {
  --card: oklch(0.205 0 0);
  --card-foreground: oklch(0.985 0 0);
  --muted: oklch(0.269 0 0);
  --muted-foreground: oklch(0.708 0 0);
  --foreground: oklch(0.985 0 0);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 15%);
}

A few small fills and borders, and the logo in the Powered by Median line, switch with your theme: a dark class or data-theme="dark" on the page. The operating system’s setting does not change them, so a light site stays light. A site that switches themes with only a media query gets its own dark colours, with those few details left light.

Corners

Your --radius does not change the components’ corners. Only the highlight ring reads it.

Element Corner
Panel, modal, unread card, feedback panel, contact form 0.875rem
Buttons and fields inside them Derived from a --radius pinned to 0.625rem
Launcher A circle
Highlight ring around an element on your page Your --radius, or 0.625rem without one

Fonts and sizes

  • The components use your page’s font-family. Code uses the system monospace stack.
  • Sizes are in rem. A root font size other than 16px scales every component.
  • On screens narrower than 40rem, text fields use 16px text so iOS does not zoom in.

Motion

Two variables set the easing of most transitions.

Variable Default Used by
--ease-spring cubic-bezier(0.34, 1.45, 0.64, 1) The corner panel opening and closing, the modal and feedback panel opening, the launcher and unread card appearing
--ease-out-quint cubic-bezier(0.22, 1, 0.36, 1) The phone sheet, the modal closing, a panel opening with no launcher, the launcher hiding

The stylesheet declares both on the components’ own root, so a value on :root does not reach them. Set them on [data-median-widget], outside any @layer:

[data-median-widget] {
  --ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
}

Durations are fixed. The launcher’s flight to the edge after a drag, the contact form’s sent mark and bubbles changing size run in JavaScript and ignore both variables.

When the visitor’s system asks for reduced motion, the components turn their animations and transitions off.

Scoping

Every rule in @mediansh/widget/styles.css is limited to elements under [data-median-widget], so your page keeps its own styles. The stylesheet carries a reset scoped the same way, and the components look the same on pages without Tailwind.

These roots carry the attribute:

Root Rendered
The launcher and corner panel Where you render MedianSupport, or in <body> from mountMedianSupport
The modal In <body>
The highlight ring In <body>
The feedback panel In <body>
The contact form Where you mount it

Was this page helpful?