Theming
The widget ships with a precompiled, scoped stylesheet. Import it once; Tailwind is not required in the host application.
import '@mordn/chat-widget/styles.css';Theme contract
ThemeConfig is deliberately small: exactly three required hex colors. Omit
theme for the stock palette. There is no public mode switch or arbitrary CSS
token override API.
interface ThemeConfig {
backgroundColor: string;
textColor: string;
primaryColor: string;
}theme is not a prop. It lives in the client half of the canonical config
document, which normally arrives from the server when the widget calls
GET ${apiBase}/bootstrap — so the usual way to change colors is to publish
them from the dashboard and leave the component as
<ChatWidget apiBase="/api/chat" />.
To set them in code instead, pass the same canonical shape as config:
<ChatWidget
apiBase="/api/chat"
config={{
schemaVersion: 1,
runtime: { model: 'anthropic/claude-sonnet-4-5' },
client: {
theme: {
backgroundColor: '#171717',
textColor: '#ededed',
primaryColor: '#3b82f6',
},
},
}}
/>Explicit values are merged over the published config, so you can override the theme alone and inherit everything else from the server.
All three values must be valid hex colors. If any value is invalid, the widget ignores the entire custom theme rather than applying a partial palette. The remaining color ramp is derived internally from these values.
config.client.theme.backgroundColorstringrequiredHex color for the chat background.
config.client.theme.textColorstringrequiredHex color for body text. Choose a value with sufficient contrast against the background.
config.client.theme.primaryColorstringrequiredHex accent used for send controls, links, and focus states.
Display layout
display is a member of config.client too, and is published from the
dashboard the same way. To set it in code:
<ChatWidget
apiBase="/api/chat"
config={{
schemaVersion: 1,
runtime: { model: 'anthropic/claude-sonnet-4-5' },
client: {
display: {
layout: 'popup',
size: 'default',
defaultOpen: false,
resizable: true,
showToggleButton: true,
},
},
}}
/>config.client.display.layout'popup' | 'inline' | 'page'default: "popup"popup is a floating panel, inline takes its parent dimensions, and page
provides a full-height conversation surface.
config.client.display.size'compact' | 'default' | 'large' | 'full'default: "default"Popup width preset. Ignored by inline and page layouts.
config.client.display.widthstringCustom CSS width for popup layout; overrides size.
config.client.display.resizablebooleandefault: trueAllow drag resizing in popup layout.
config.client.display.defaultOpenbooleandefault: falseInitial uncontrolled popup state. Persisted panel state can override it after mount.
config.client.display.showToggleButtonbooleandefault: trueShow the built-in launcher. Controlled mode hides it automatically.
config.client.display.toggleButtonPosition{ bottom?: string; right?: string }Custom CSS position. The default is bottom-right and includes safe-area insets.
For controlled state, header actions, panel persistence, and the imperative ref,
see ChatWidget and AI UX extras.