Skip to content
Early preview — you found Guuey before launch · official launch soon

A @guuey/chat theme is data, not CSS — a serializable token object you can keep in code, in config, or in a database. The React kit projects it as --guuey-chat-* CSS custom properties on the transcript root; the React Native tier maps the same object to style values. One schema, two projections.

interface GuueyChatTheme {
name: string;
/** BOTH palettes always present — mode is the consumer's runtime choice. */
colors: { light: GuueyChatPalette; dark: GuueyChatPalette };
typography: { fontFamily?: string; monoFontFamily?: string; scale?: number };
shape: {
radius: "none" | "soft" | "round";
density: "compact" | "comfortable";
};
}
interface GuueyChatPalette {
accent: string; // primary action + user bubble
onAccent: string; // text on accent
ink: string; // primary text
inkMuted: string; // secondary text
surface: string; // bubbles, cards
canvas: string; // page background behind the transcript
canvasMuted: string; // grouped/collapsed rows, wells
error: string;
}

Both palettes are always present in a theme; which one paints is the mode prop ("light" / "dark") — so switching modes is a runtime choice, never a second theme object.

  • DEFAULT_CHAT_THEME — brand-neutral and polished; what you get without configuring anything. Designed to sit inside your product without announcing guuey.
  • GUUEY_CHAT_THEME — the guuey visual identity (the widget’s shipped palette). Use it when you want the surface to read as guuey.
import { GUUEY_CHAT_THEME } from "@guuey/chat";
<GuueyChat endpointUrl="…" appId="…" theme={GUUEY_CHAT_THEME} mode="dark" />;

resolveTheme merges a partial theme over the default per token — supply only what you change:

import { resolveTheme, type GuueyChatTheme } from "@guuey/chat";
const brandTheme: GuueyChatTheme = resolveTheme({
name: "acme",
colors: {
light: { accent: "#7c3aed", onAccent: "#ffffff" },
dark: { accent: "#a78bfa" },
},
shape: { radius: "round", density: "compact" },
});
<GuueyChat endpointUrl="…" appId="…" theme={brandTheme} mode="dark" />;

Every token you omit falls back to the default theme’s value — a half-configured theme can never produce an unreadable surface.

The schema is built to be stored: parsing is lenient (unknown keys pass through rather than failing), changes to the schema are additive-only, and every new token ships with a default fallback — so a theme object saved today keeps parsing against every future version of the package.

That posture is what makes the theme safe to store as platform data — which is exactly what per-app theme configuration does.

You can set a theme once per app in the console: your app’s Design page on platform.guuey.com has a theme editor over this same schema, with a live transcript preview, validated on save. Every surface guuey hosts for the app picks it up — the embedded widget, the app’s standalone page, and Portal chat — with no redeploy.

The editor lives on the app’s Design page in the console; branding and the distribution tabs are covered on the same docs page — Design & distribution.

Your own @guuey/chat mounts are unaffected: the theme prop you pass always wins on surfaces you render yourself.

The projection is ordinary CSS custom properties, so anything the token schema doesn’t express can be adjusted in your own stylesheet against the kit’s class names — or by replacing a component slot entirely. Prefer tokens first: they’re what survives schema evolution, and what the console’s per-app theme stores.